summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/actions/godot-build/action.yml4
-rw-r--r--.github/actions/godot-cache/action.yml6
-rw-r--r--.github/actions/godot-deps/action.yml21
-rw-r--r--.github/workflows/android_builds.yml2
-rw-r--r--.github/workflows/godot_cpp_test.yml4
-rw-r--r--.github/workflows/ios_builds.yml2
-rw-r--r--.github/workflows/linux_builds.yml13
-rw-r--r--.github/workflows/macos_builds.yml2
-rw-r--r--.github/workflows/web_builds.yml2
-rw-r--r--.github/workflows/windows_builds.yml2
-rw-r--r--.pre-commit-config.yaml14
-rw-r--r--SConstruct6
-rw-r--r--doc/classes/ProjectSettings.xml4
-rw-r--r--doc/classes/TextEdit.xml2
-rw-r--r--drivers/gles3/storage/texture_storage.cpp16
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp55
-rw-r--r--drivers/wasapi/audio_driver_wasapi.h9
-rw-r--r--editor/editor_run_native.cpp18
-rw-r--r--editor/filesystem_dock.cpp16
-rw-r--r--editor/filesystem_dock.h1
-rw-r--r--editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp2
-rw-r--r--editor/import/resource_importer_imagefont.cpp32
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp16
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp7
-rw-r--r--editor/plugins/script_editor_plugin.cpp42
-rw-r--r--editor/plugins/script_editor_plugin.h2
-rw-r--r--editor/plugins/text_shader_editor.cpp2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp1
-rw-r--r--modules/gdscript/gdscript_parser.cpp4
-rw-r--r--modules/gdscript/gdscript_warning.cpp2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd13
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out8
-rw-r--r--modules/svg/SCsub14
-rw-r--r--platform/web/export/export_plugin.cpp7
-rw-r--r--platform/web/export/export_plugin.h1
-rw-r--r--scene/gui/button.cpp115
-rw-r--r--scene/gui/button.h9
-rw-r--r--scene/gui/check_box.cpp2
-rw-r--r--scene/gui/check_button.cpp2
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup.cpp12
-rw-r--r--scene/gui/text_edit.cpp105
-rw-r--r--scene/gui/tree.cpp26
-rw-r--r--scene/gui/tree.h1
-rw-r--r--scene/resources/shader.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.cpp6
-rw-r--r--servers/rendering/rendering_device.cpp3
-rw-r--r--thirdparty/thorvg/inc/config.h4
-rwxr-xr-xthirdparty/thorvg/update-thorvg.sh4
51 files changed, 441 insertions, 209 deletions
diff --git a/.github/actions/godot-build/action.yml b/.github/actions/godot-build/action.yml
index 0a0899db78..bf29b7e430 100644
--- a/.github/actions/godot-build/action.yml
+++ b/.github/actions/godot-build/action.yml
@@ -13,10 +13,10 @@ inputs:
sconsflags:
default: ""
scons-cache:
- description: The scons cache path.
+ description: The SCons cache path.
default: "${{ github.workspace }}/.scons-cache/"
scons-cache-limit:
- description: The scons cache size limit.
+ description: The SCons cache size limit.
# actions/cache has 10 GiB limit, and GitHub runners have a 14 GiB disk.
# Limit to 7 GiB to avoid having the extracted cache fill the disk.
default: 7168
diff --git a/.github/actions/godot-cache/action.yml b/.github/actions/godot-cache/action.yml
index b7ca01bb47..13142e7ed1 100644
--- a/.github/actions/godot-cache/action.yml
+++ b/.github/actions/godot-cache/action.yml
@@ -5,13 +5,13 @@ inputs:
description: The cache base name (job name by default).
default: "${{github.job}}"
scons-cache:
- description: The scons cache path.
+ description: The SCons cache path.
default: "${{github.workspace}}/.scons-cache/"
runs:
using: "composite"
steps:
- # Upload cache on completion and check it out now
- - name: Load .scons_cache directory
+ # Upload cache on completion and check it out now.
+ - name: Load SCons cache directory
uses: actions/cache@v4
with:
path: ${{inputs.scons-cache}}
diff --git a/.github/actions/godot-deps/action.yml b/.github/actions/godot-deps/action.yml
index cac72c436e..07a364cd79 100644
--- a/.github/actions/godot-deps/action.yml
+++ b/.github/actions/godot-deps/action.yml
@@ -1,27 +1,30 @@
-name: Setup python and scons
-description: Setup python, install the pip version of scons.
+name: Setup Python and SCons
+description: Setup Python, install the pip version of SCons.
inputs:
python-version:
- description: The python version to use.
+ description: The Python version to use.
default: "3.x"
python-arch:
- description: The python architecture.
+ description: The Python architecture.
default: "x64"
+ scons-version:
+ description: The SCons version to use.
+ default: "4.7.0"
runs:
using: "composite"
steps:
- # Use python 3.x release (works cross platform)
- name: Set up Python 3.x
uses: actions/setup-python@v5
with:
- # Semantic version range syntax or exact version of a Python version
+ # Semantic version range syntax or exact version of a Python version.
python-version: ${{ inputs.python-version }}
- # Optional - x64 or x86 architecture, defaults to x64
+ # Optional - x64 or x86 architecture, defaults to x64.
architecture: ${{ inputs.python-arch }}
- - name: Setup scons
+ - name: Setup SCons
shell: bash
run: |
python -c "import sys; print(sys.version)"
- python -m pip install scons==4.7.0
+ python -m pip install wheel
+ python -m pip install scons==${{ inputs.scons-version }}
scons --version
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml
index 5cb66a40ab..9a488bd095 100644
--- a/.github/workflows/android_builds.yml
+++ b/.github/workflows/android_builds.yml
@@ -32,7 +32,7 @@ jobs:
uses: ./.github/actions/godot-cache
continue-on-error: true
- - name: Setup python and scons
+ - name: Setup Python and SCons
uses: ./.github/actions/godot-deps
- name: Compilation (arm32)
diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml
index 7350920810..57114dacfc 100644
--- a/.github/workflows/godot_cpp_test.yml
+++ b/.github/workflows/godot_cpp_test.yml
@@ -22,7 +22,7 @@ jobs:
with:
submodules: recursive
- - name: Setup python and scons
+ - name: Setup Python and SCons
uses: ./.github/actions/godot-deps
# Checkout godot-cpp
@@ -47,7 +47,7 @@ jobs:
cp -f godot-api/gdextension_interface.h godot-cpp/gdextension/
cp -f godot-api/extension_api.json godot-cpp/gdextension/
- # TODO: Add caching to the scons build and store it for CI via the godot-cache
+ # TODO: Add caching to the SCons build and store it for CI via the godot-cache
# action.
# Build godot-cpp test extension
diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml
index e205d551ed..0546f43acc 100644
--- a/.github/workflows/ios_builds.yml
+++ b/.github/workflows/ios_builds.yml
@@ -26,7 +26,7 @@ jobs:
uses: ./.github/actions/godot-cache
continue-on-error: true
- - name: Setup python and scons
+ - name: Setup Python and SCons
uses: ./.github/actions/godot-deps
- name: Compilation (arm64)
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index 0420a02b1d..6b98256110 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -58,6 +58,8 @@ jobs:
tests: true
# Skip 2GiB artifact speeding up action.
artifact: false
+ # Test our oldest supported SCons/Python versions on one arbitrary editor build.
+ legacy-scons: true
- name: Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld)
cache-name: linux-editor-thread-sanitizer
@@ -115,9 +117,18 @@ jobs:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
- - name: Setup python and scons
+ - name: Setup Python and SCons
+ if: ${{ ! matrix.legacy-scons }}
uses: ./.github/actions/godot-deps
+ - name: Setup Python and SCons (legacy versions)
+ if: ${{ matrix.legacy-scons }}
+ uses: ./.github/actions/godot-deps
+ with:
+ # Sync with Ensure*Version in SConstruct.
+ python-version: 3.6
+ scons-version: 3.1.2
+
- name: Setup GCC problem matcher
uses: ammaraskar/gcc-problem-matcher@master
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml
index 70031ec4c3..badcb688d1 100644
--- a/.github/workflows/macos_builds.yml
+++ b/.github/workflows/macos_builds.yml
@@ -43,7 +43,7 @@ jobs:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
- - name: Setup python and scons
+ - name: Setup Python and SCons
uses: ./.github/actions/godot-deps
- name: Setup Vulkan SDK
diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml
index cfa1571d1f..1eb7b901cd 100644
--- a/.github/workflows/web_builds.yml
+++ b/.github/workflows/web_builds.yml
@@ -58,7 +58,7 @@ jobs:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
- - name: Setup python and scons
+ - name: Setup Python and SCons
uses: ./.github/actions/godot-deps
- name: Compilation
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
index 5443ba20ab..f9513af5e3 100644
--- a/.github/workflows/windows_builds.yml
+++ b/.github/workflows/windows_builds.yml
@@ -48,7 +48,7 @@ jobs:
cache-name: ${{ matrix.cache-name }}
continue-on-error: true
- - name: Setup python and scons
+ - name: Setup Python and SCons
uses: ./.github/actions/godot-deps
- name: Download Direct3D 12 SDK components
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5c0c0b6917..5a0e919c47 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -125,8 +125,18 @@ repos:
language: node
entry: jsdoc
files: ^platform/web/js/engine/(engine|config|features)\.js$
- args: [--template, platform/web/js/jsdoc2rst/, --destination, '', -d, dry-run]
- additional_dependencies: ["jsdoc@4.0.2"]
+ args:
+ - --template
+ - platform/web/js/jsdoc2rst/
+ - platform/web/js/engine/engine.js
+ - platform/web/js/engine/config.js
+ - platform/web/js/engine/features.js
+ - --destination
+ - ''
+ - -d
+ - dry-run
+ pass_filenames: false
+ additional_dependencies: ['jsdoc@^4.0.3']
- id: copyright-headers
name: copyright-headers
diff --git a/SConstruct b/SConstruct
index 07ec014cf9..c668ac7df3 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-EnsureSConsVersion(3, 0, 0)
+EnsureSConsVersion(3, 1, 2)
EnsurePythonVersion(3, 6)
# System
@@ -365,7 +365,7 @@ if env["platform"] in platform_opts:
opts.Add(opt)
# Update the environment to take platform-specific options into account.
-opts.Update(env, {**ARGUMENTS, **env})
+opts.Update(env, {**ARGUMENTS, **env.Dictionary()})
# Detect modules.
modules_detected = OrderedDict()
@@ -425,7 +425,7 @@ for name, path in modules_detected.items():
env.modules_detected = modules_detected
# Update the environment again after all the module options are added.
-opts.Update(env, {**ARGUMENTS, **env})
+opts.Update(env, {**ARGUMENTS, **env.Dictionary()})
Help(opts.GenerateHelpText(env))
# add default include paths
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 926d981f68..5ac4c96d93 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -554,10 +554,10 @@
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when defining a local or subclass member variable that would shadow a variable that is inherited from a parent class.
</member>
<member name="debug/gdscript/warnings/standalone_expression" type="int" setter="" getter="" default="1">
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling an expression that has no effect on the surrounding code, such as writing [code]2 + 2[/code] as a statement.
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling an expression that may have no effect on the surrounding code, such as writing [code]2 + 2[/code] as a statement.
</member>
<member name="debug/gdscript/warnings/standalone_ternary" type="int" setter="" getter="" default="1">
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a ternary expression that has no effect on the surrounding code, such as writing [code]42 if active else 0[/code] as a statement.
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a ternary expression that may have no effect on the surrounding code, such as writing [code]42 if active else 0[/code] as a statement.
</member>
<member name="debug/gdscript/warnings/static_called_on_instance" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a static method from an instance of a class instead of from the class directly.
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 4dda1f8db8..75cad4d08b 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -1314,7 +1314,7 @@
[b]Note:[/b] This method is only implemented on Linux.
</member>
<member name="minimap_draw" type="bool" setter="set_draw_minimap" getter="is_drawing_minimap" default="false">
- If [code]true[/code], a minimap is shown, providing an outline of your source code.
+ If [code]true[/code], a minimap is shown, providing an outline of your source code. The minimap uses a fixed-width text size.
</member>
<member name="minimap_width" type="int" setter="set_minimap_width" getter="get_minimap_width" default="80">
The width, in pixels, of the minimap.
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 57ef951bdf..2dcf623995 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1495,14 +1495,18 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image,
for (int i = 0; i < mipmaps; i++) {
int size, ofs;
img->get_mipmap_offset_and_size(i, ofs, size);
-
if (compressed) {
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-
- int bw = w;
- int bh = h;
-
- glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]);
+ if (texture->target == GL_TEXTURE_2D_ARRAY) {
+ if (p_initialize) {
+ glCompressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0,
+ size * texture->layers, &read[ofs]);
+ } else {
+ glCompressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, internal_format, size, &read[ofs]);
+ }
+ } else {
+ glCompressedTexImage2D(blit_target, i, internal_format, w, h, 0, size, &read[ofs]);
+ }
} else {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (texture->target == GL_TEXTURE_2D_ARRAY) {
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index 8ea1f52d15..a349a66f75 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -107,12 +107,6 @@ const IID IID_IAudioClient3 = __uuidof(IAudioClient3);
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
-#define SAFE_RELEASE(memory) \
- if ((memory) != nullptr) { \
- (memory)->Release(); \
- (memory) = nullptr; \
- }
-
#define REFTIMES_PER_SEC 10000000
#define REFTIMES_PER_MILLISEC 10000
@@ -129,16 +123,10 @@ static bool default_input_device_changed = false;
class CMMNotificationClient : public IMMNotificationClient {
LONG _cRef = 1;
- IMMDeviceEnumerator *_pEnumerator = nullptr;
public:
CMMNotificationClient() {}
- virtual ~CMMNotificationClient() {
- if ((_pEnumerator) != nullptr) {
- (_pEnumerator)->Release();
- (_pEnumerator) = nullptr;
- }
- }
+ virtual ~CMMNotificationClient() {}
ULONG STDMETHODCALLTYPE AddRef() {
return InterlockedIncrement(&_cRef);
@@ -203,8 +191,8 @@ static CMMNotificationClient notif_client;
Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3) {
WAVEFORMATEX *pwfex;
- IMMDeviceEnumerator *enumerator = nullptr;
- IMMDevice *output_device = nullptr;
+ ComPtr<IMMDeviceEnumerator> enumerator = nullptr;
+ ComPtr<IMMDevice> output_device = nullptr;
HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
@@ -212,7 +200,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
if (p_device->device_name == "Default") {
hr = enumerator->GetDefaultAudioEndpoint(p_input ? eCapture : eRender, eConsole, &output_device);
} else {
- IMMDeviceCollection *devices = nullptr;
+ ComPtr<IMMDeviceCollection> devices = nullptr;
hr = enumerator->EnumAudioEndpoints(p_input ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
@@ -225,12 +213,12 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
for (ULONG i = 0; i < count && !found; i++) {
- IMMDevice *tmp_device = nullptr;
+ ComPtr<IMMDevice> tmp_device = nullptr;
hr = devices->Item(i, &tmp_device);
ERR_BREAK(hr != S_OK);
- IPropertyStore *props = nullptr;
+ ComPtr<IPropertyStore> props = nullptr;
hr = tmp_device->OpenPropertyStore(STGM_READ, &props);
ERR_BREAK(hr != S_OK);
@@ -248,8 +236,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
}
PropVariantClear(&propvar);
- props->Release();
- tmp_device->Release();
}
if (found) {
@@ -276,7 +262,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
}
hr = enumerator->RegisterEndpointNotificationCallback(&notif_client);
- SAFE_RELEASE(enumerator)
if (hr != S_OK) {
ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error");
@@ -303,8 +288,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
hr = output_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client);
}
- SAFE_RELEASE(output_device)
-
if (p_reinit) {
if (hr != S_OK) {
return ERR_CANT_OPEN;
@@ -319,7 +302,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
audioProps.bIsOffload = FALSE;
audioProps.eCategory = AudioCategory_GameEffects;
- hr = ((IAudioClient3 *)p_device->audio_client)->SetClientProperties(&audioProps);
+ hr = ((IAudioClient3 *)p_device->audio_client.Get())->SetClientProperties(&audioProps);
ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: SetClientProperties failed with error 0x" + String::num_uint64(hr, 16) + ".");
}
@@ -402,7 +385,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
}
} else {
- IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client;
+ IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client.Get();
// AUDCLNT_STREAMFLAGS_RATEADJUST is an invalid flag with IAudioClient3, therefore we have to use
// the closest supported mix rate supported by the audio driver.
@@ -419,7 +402,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
if (hr != S_OK) {
print_verbose("WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient.");
CoTaskMemFree(pwfex);
- SAFE_RELEASE(output_device)
return audio_device_init(p_device, p_input, p_reinit, true);
}
@@ -441,7 +423,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
if (hr != S_OK) {
print_verbose("WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient.");
CoTaskMemFree(pwfex);
- SAFE_RELEASE(output_device);
return audio_device_init(p_device, p_input, p_reinit, true);
} else {
uint32_t output_latency_in_frames;
@@ -453,7 +434,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
} else {
print_verbose("WASAPI: GetCurrentSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient.");
CoTaskMemFree(pwfex);
- SAFE_RELEASE(output_device);
return audio_device_init(p_device, p_input, p_reinit, true);
}
}
@@ -468,7 +448,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_i
// Free memory
CoTaskMemFree(pwfex);
- SAFE_RELEASE(output_device)
return OK;
}
@@ -537,9 +516,9 @@ Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) {
p_device->active.clear();
}
- SAFE_RELEASE(p_device->audio_client)
- SAFE_RELEASE(p_device->render_client)
- SAFE_RELEASE(p_device->capture_client)
+ p_device->audio_client.Reset();
+ p_device->render_client.Reset();
+ p_device->capture_client.Reset();
return OK;
}
@@ -581,8 +560,8 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const {
PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) {
PackedStringArray list;
- IMMDeviceCollection *devices = nullptr;
- IMMDeviceEnumerator *enumerator = nullptr;
+ ComPtr<IMMDeviceCollection> devices = nullptr;
+ ComPtr<IMMDeviceEnumerator> enumerator = nullptr;
list.push_back(String("Default"));
@@ -597,12 +576,12 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) {
ERR_FAIL_COND_V(hr != S_OK, PackedStringArray());
for (ULONG i = 0; i < count; i++) {
- IMMDevice *output_device = nullptr;
+ ComPtr<IMMDevice> output_device = nullptr;
hr = devices->Item(i, &output_device);
ERR_BREAK(hr != S_OK);
- IPropertyStore *props = nullptr;
+ ComPtr<IPropertyStore> props = nullptr;
hr = output_device->OpenPropertyStore(STGM_READ, &props);
ERR_BREAK(hr != S_OK);
@@ -615,12 +594,8 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) {
list.push_back(String(propvar.pwszVal));
PropVariantClear(&propvar);
- props->Release();
- output_device->Release();
}
- devices->Release();
- enumerator->Release();
return list;
}
diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h
index 367c30607a..d73cbf4a8a 100644
--- a/drivers/wasapi/audio_driver_wasapi.h
+++ b/drivers/wasapi/audio_driver_wasapi.h
@@ -40,15 +40,18 @@
#include <audioclient.h>
#include <mmdeviceapi.h>
+#include <wrl/client.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
+using Microsoft::WRL::ComPtr;
+
class AudioDriverWASAPI : public AudioDriver {
class AudioDeviceWASAPI {
public:
- IAudioClient *audio_client = nullptr;
- IAudioRenderClient *render_client = nullptr; // Output
- IAudioCaptureClient *capture_client = nullptr; // Input
+ ComPtr<IAudioClient> audio_client = nullptr;
+ ComPtr<IAudioRenderClient> render_client = nullptr; // Output
+ ComPtr<IAudioCaptureClient> capture_client = nullptr; // Input
SafeFlag active;
WORD format_tag = 0;
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index 4eb3f07c47..5c50231623 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -35,7 +35,6 @@
#include "editor/export/editor_export.h"
#include "editor/export/editor_export_platform.h"
#include "editor/themes/editor_scale.h"
-#include "scene/resources/image_texture.h"
void EditorRunNative::_notification(int p_what) {
switch (p_what) {
@@ -49,17 +48,26 @@ void EditorRunNative::_notification(int p_what) {
if (changed) {
PopupMenu *popup = remote_debug->get_popup();
popup->clear();
- for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) {
- Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(i);
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i);
+ Ref<EditorExportPlatform> eep = preset->get_platform();
if (eep.is_null()) {
continue;
}
+ int platform_idx = -1;
+ for (int j = 0; j < EditorExport::get_singleton()->get_export_platform_count(); j++) {
+ if (eep->get_name() == EditorExport::get_singleton()->get_export_platform(j)->get_name()) {
+ platform_idx = j;
+ }
+ }
int dc = MIN(eep->get_options_count(), 9000);
- if (dc > 0) {
+ bool needs_templates;
+ String error;
+ if (dc > 0 && preset->is_runnable() && eep->can_export(preset, error, needs_templates)) {
popup->add_icon_item(eep->get_run_icon(), eep->get_name(), -1);
popup->set_item_disabled(-1, true);
for (int j = 0; j < dc; j++) {
- popup->add_icon_item(eep->get_option_icon(j), eep->get_option_label(j), 10000 * i + j);
+ popup->add_icon_item(eep->get_option_icon(j), eep->get_option_label(j), 10000 * platform_idx + j);
popup->set_item_tooltip(-1, eep->get_option_tooltip(j));
popup->set_item_indent(-1, 2);
}
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index b05cafa694..5f311ae445 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -79,6 +79,15 @@ Control *FileSystemList::make_custom_tooltip(const String &p_text) const {
}
void FileSystemList::_line_editor_submit(const String &p_text) {
+ if (popup_edit_commited) {
+ return; // Already processed by _text_editor_popup_modal_close
+ }
+
+ if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) {
+ return; // ESC pressed, app focus lost, or forced close from code.
+ }
+
+ popup_edit_commited = true; // End edit popup processing.
popup_editor->hide();
emit_signal(SNAME("item_edited"));
@@ -127,6 +136,7 @@ bool FileSystemList::edit_selected() {
line_editor->set_text(name);
line_editor->select(0, name.rfind("."));
+ popup_edit_commited = false; // Start edit popup processing.
popup_editor->popup();
popup_editor->child_controls_changed();
line_editor->grab_focus();
@@ -138,8 +148,12 @@ String FileSystemList::get_edit_text() {
}
void FileSystemList::_text_editor_popup_modal_close() {
+ if (popup_edit_commited) {
+ return; // Already processed by _text_editor_popup_modal_close
+ }
+
if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) {
- return;
+ return; // ESC pressed, app focus lost, or forced close from code.
}
_line_editor_submit(line_editor->get_text());
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 7449657c06..3fbff3ef19 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -59,6 +59,7 @@ class FileSystemTree : public Tree {
class FileSystemList : public ItemList {
GDCLASS(FileSystemList, ItemList);
+ bool popup_edit_commited = true;
VBoxContainer *popup_editor_vb = nullptr;
Popup *popup_editor = nullptr;
LineEdit *line_editor = nullptr;
diff --git a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp
index 3eabe46950..b69d38afa0 100644
--- a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp
+++ b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp
@@ -204,7 +204,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
} else {
for (int j = 0; j < key_len; j++) {
Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j)));
- anim->track_set_key_value(i, j, (global_transform.basis * sc).get_scale());
+ anim->track_set_key_value(i, j, (global_transform.orthonormalized().basis * sc).get_scale());
}
}
}
diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp
index c454ebc6eb..94e5dd0f12 100644
--- a/editor/import/resource_importer_imagefont.cpp
+++ b/editor/import/resource_importer_imagefont.cpp
@@ -158,17 +158,16 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
c++; // Skip "+".
continue;
}
- } else if (range[c] == '0') {
- if ((c <= range.length() - 2) && range[c + 1] == 'x') {
- token = String();
- if (step == STEP_START_BEGIN) {
- step = STEP_START_READ_HEX;
- } else {
- step = STEP_END_READ_HEX;
- }
- c++; // Skip "x".
- continue;
+ } else if (range[c] == '0' && (c <= range.length() - 2) && range[c + 1] == 'x') {
+ // Read hexadecimal value, start.
+ token = String();
+ if (step == STEP_START_BEGIN) {
+ step = STEP_START_READ_HEX;
+ } else {
+ step = STEP_END_READ_HEX;
}
+ c++; // Skip "x".
+ continue;
} else if (range[c] == '\'' || range[c] == '\"') {
if ((c <= range.length() - 3) && (range[c + 2] == '\'' || range[c + 2] == '\"')) {
token = String();
@@ -184,14 +183,13 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
}
} else if (is_digit(range[c])) {
// Read decimal value, start.
- c++;
token = String();
+ token += range[c];
if (step == STEP_START_BEGIN) {
step = STEP_START_READ_DEC;
} else {
step = STEP_END_READ_DEC;
}
- token += range[c];
continue;
}
[[fallthrough]];
@@ -254,9 +252,19 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
} break;
}
}
+ if (step == STEP_START_READ_HEX) {
+ start = token.hex_to_int();
+ } else if (step == STEP_START_READ_DEC) {
+ start = token.to_int();
+ } else if (step == STEP_END_READ_HEX) {
+ end = token.hex_to_int();
+ } else if (step == STEP_END_READ_DEC) {
+ end = token.to_int();
+ }
if (end == -1) {
end = start;
}
+
if (start == -1) {
WARN_PRINT(vformat("Invalid range: \"%s\"", range));
continue;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 731c89250e..a44430ca7f 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5732,11 +5732,10 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons
Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res));
Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
if (texture != nullptr || scene != nullptr) {
- bool root_node_selected = EditorNode::get_singleton()->get_editor_selection()->is_selected(EditorNode::get_singleton()->get_edited_scene());
- String desc = TTR("Drag and drop to add as child of selected node.") + "\n" + TTR("Hold Alt when dropping to add as child of root node.");
- if (!root_node_selected) {
- desc += "\n" + TTR("Hold Shift when dropping to add as sibling of selected node.");
- }
+ String desc = TTR("Drag and drop to add as sibling of selected node (except when root is selected).") +
+ "\n" + TTR("Hold Shift when dropping to add as child of selected node.") +
+ "\n" + TTR("Hold Alt when dropping to add as child of root node.");
+
if (texture != nullptr) {
Sprite2D *sprite = memnew(Sprite2D);
sprite->set_texture(texture);
@@ -6099,11 +6098,12 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p
Node *root_node = EditorNode::get_singleton()->get_edited_scene();
if (selected_nodes.size() > 0) {
Node *selected_node = selected_nodes.front()->get();
- target_node = selected_node;
if (is_alt) {
target_node = root_node;
- } else if (is_shift && selected_node != root_node) {
- target_node = selected_node->get_parent();
+ } else if (is_shift) {
+ target_node = selected_node;
+ } else { // Default behavior.
+ target_node = (selected_node != root_node) ? selected_node->get_parent() : root_node;
}
} else {
if (root_node) {
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 5873d10e76..ca7ea821e8 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -4593,11 +4593,12 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_
Node *root_node = EditorNode::get_singleton()->get_edited_scene();
if (selected_nodes.size() > 0) {
Node *selected_node = selected_nodes.front()->get();
- target_node = selected_node;
if (is_alt) {
target_node = root_node;
- } else if (is_shift && selected_node != root_node) {
- target_node = selected_node->get_parent();
+ } else if (is_shift) {
+ target_node = selected_node;
+ } else { // Default behavior.
+ target_node = (selected_node != root_node) ? selected_node->get_parent() : root_node;
}
} else {
if (root_node) {
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 6cb49bbfa6..eb6282ca0c 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -731,6 +731,7 @@ void ScriptEditor::_go_to_tab(int p_idx) {
_update_members_overview();
_update_help_overview();
_update_selected_editor_menu();
+ _update_online_doc();
_update_members_overview_visibility();
_update_help_overview_visibility();
}
@@ -903,6 +904,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
_go_to_tab(idx);
} else {
_update_selected_editor_menu();
+ _update_online_doc();
}
_update_history_arrows();
@@ -1350,7 +1352,21 @@ void ScriptEditor::_menu_option(int p_option) {
help_search_dialog->popup_dialog();
} break;
case SEARCH_WEBSITE: {
- OS::get_singleton()->shell_open(VERSION_DOCS_URL "/");
+ Control *tab = tab_container->get_current_tab_control();
+
+ EditorHelp *eh = Object::cast_to<EditorHelp>(tab);
+ bool native_class_doc = false;
+ if (eh) {
+ const HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(eh->get_class());
+ native_class_doc = E && !E->value.is_script_doc;
+ }
+ if (native_class_doc) {
+ String name = eh->get_class().to_lower();
+ String doc_url = vformat(VERSION_DOCS_URL "/classes/class_%s.html", name);
+ OS::get_singleton()->shell_open(doc_url);
+ } else {
+ OS::get_singleton()->shell_open(VERSION_DOCS_URL "/");
+ }
} break;
case WINDOW_NEXT: {
_history_forward();
@@ -2029,6 +2045,26 @@ void ScriptEditor::_update_help_overview() {
}
}
+void ScriptEditor::_update_online_doc() {
+ Node *current = tab_container->get_tab_control(tab_container->get_current_tab());
+
+ EditorHelp *eh = Object::cast_to<EditorHelp>(current);
+ bool native_class_doc = false;
+ if (eh) {
+ const HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(eh->get_class());
+ native_class_doc = E && !E->value.is_script_doc;
+ }
+ if (native_class_doc) {
+ String name = eh->get_class();
+ String tooltip = vformat(TTR("Open '%s' in Godot online documentation."), name);
+ site_search->set_text(TTR("Open in Online Docs"));
+ site_search->set_tooltip_text(tooltip);
+ } else {
+ site_search->set_text(TTR("Online Docs"));
+ site_search->set_tooltip_text(TTR("Open Godot online documentation."));
+ }
+}
+
void ScriptEditor::_update_script_colors() {
bool script_temperature_enabled = EDITOR_GET("text_editor/script_list/script_temperature_enabled");
@@ -4147,10 +4183,8 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
site_search = memnew(Button);
site_search->set_flat(true);
- site_search->set_text(TTR("Online Docs"));
site_search->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditor::_menu_option).bind(SEARCH_WEBSITE));
menu_hb->add_child(site_search);
- site_search->set_tooltip_text(TTR("Open Godot online documentation."));
help_search = memnew(Button);
help_search->set_flat(true);
@@ -4271,6 +4305,8 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
Ref<EditorJSONSyntaxHighlighter> json_syntax_highlighter;
json_syntax_highlighter.instantiate();
register_syntax_highlighter(json_syntax_highlighter);
+
+ _update_online_doc();
}
ScriptEditor::~ScriptEditor() {
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index e6bb8f14a9..6f8e71ce75 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -450,6 +450,8 @@ class ScriptEditor : public PanelContainer {
void _update_help_overview();
void _help_overview_selected(int p_idx);
+ void _update_online_doc();
+
void _find_scripts(Node *p_base, Node *p_current, HashSet<Ref<Script>> &used);
void _tree_changed();
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index 637547062c..bb74bf8d1f 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -318,6 +318,8 @@ void ShaderTextEditor::_load_theme_settings() {
const Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color");
syntax_highlighter->add_color_region("/**", "*/", doc_comment_color, false);
+ // "/**/" will be treated as the start of the "/**" region, this line is guaranteed to end the color_region.
+ syntax_highlighter->add_color_region("/**/", "", comment_color, true);
// Disabled preprocessor branches use translucent text color to be easier to distinguish from comments.
syntax_highlighter->set_disabled_branch_color(Color(EDITOR_GET("text_editor/theme/highlighting/text_color")) * Color(1, 1, 1, 0.5));
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 38d5c59e0e..a2680c932f 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3394,6 +3394,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
#ifdef DEBUG_ENABLED
+ // FIXME: No warning for built-in constructors and utilities due to early return.
if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL &&
!(p_call->is_super && p_call->function_name == GDScriptLanguage::get_singleton()->strings._init)) {
parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 9be9307b8a..76f02e44e9 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -1877,6 +1877,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case Node::CALL:
// Fine.
break;
+ case Node::PRELOAD:
+ // `preload` is a function-like keyword.
+ push_warning(expression, GDScriptWarning::RETURN_VALUE_DISCARDED, "preload");
+ break;
case Node::LAMBDA:
// Standalone lambdas can't be used, so make this an error.
push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression);
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 48a0abe617..611a9ad2d9 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -74,7 +74,7 @@ String GDScriptWarning::get_message() const {
case UNREACHABLE_PATTERN:
return "Unreachable pattern (pattern after wildcard or bind).";
case STANDALONE_EXPRESSION:
- return "Standalone expression (the line has no effect).";
+ return "Standalone expression (the line may have no effect).";
case STANDALONE_TERNARY:
return "Standalone ternary operator: the return value is being discarded.";
case INCOMPATIBLE_TERNARY:
diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd
index 00598e4d50..f8f70b8cc3 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd
+++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd
@@ -4,3 +4,4 @@ func i_return_int() -> int:
func test():
i_return_int()
+ preload("../../utils.notest.gd") # `preload` is a function-like keyword.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
index f2db4e9307..107051df6c 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
@@ -3,3 +3,7 @@ GDTEST_OK
>> Line: 6
>> RETURN_VALUE_DISCARDED
>> The function "i_return_int()" returns a value that will be discarded if not used.
+>> WARNING
+>> Line: 7
+>> RETURN_VALUE_DISCARDED
+>> The function "preload()" returns a value that will be discarded if not used.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd
index dc4223ec2d..74f42b012b 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd
+++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd
@@ -6,3 +6,16 @@ func test():
Vector3.ZERO
[true, false]
float(125)
+ # The following statements should not produce `STANDALONE_EXPRESSION`:
+ var _a = 1
+ _a = 2 # Assignment is a local (or global) side effect.
+ @warning_ignore("redundant_await")
+ await 3 # The `await` operand is usually a coroutine or a signal.
+ absi(4) # A call (in general) can have side effects.
+ @warning_ignore("return_value_discarded")
+ preload("../../utils.notest.gd") # A static initializer may have side effects.
+ """
+ Python-like "comment".
+ """
+ @warning_ignore("standalone_ternary")
+ 1 if 2 else 3 # Produces `STANDALONE_TERNARY` instead.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out
index a2c67a6e51..72c659c952 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out
@@ -2,16 +2,16 @@ GDTEST_OK
>> WARNING
>> Line: 3
>> STANDALONE_EXPRESSION
->> Standalone expression (the line has no effect).
+>> Standalone expression (the line may have no effect).
>> WARNING
>> Line: 4
>> STANDALONE_EXPRESSION
->> Standalone expression (the line has no effect).
+>> Standalone expression (the line may have no effect).
>> WARNING
>> Line: 6
>> STANDALONE_EXPRESSION
->> Standalone expression (the line has no effect).
+>> Standalone expression (the line may have no effect).
>> WARNING
>> Line: 7
>> STANDALONE_EXPRESSION
->> Standalone expression (the line has no effect).
+>> Standalone expression (the line may have no effect).
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index 0e21c1e6d7..a32be0e41a 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -26,7 +26,6 @@ thirdparty_sources = [
"src/loaders/raw/tvgRawLoader.cpp",
# image loaders
"src/loaders/external_png/tvgPngLoader.cpp",
- "src/loaders/external_webp/tvgWebpLoader.cpp",
"src/loaders/jpg/tvgJpgd.cpp",
"src/loaders/jpg/tvgJpgLoader.cpp",
# renderer common
@@ -59,6 +58,10 @@ thirdparty_sources = [
"src/renderer/sw_engine/tvgSwStroke.cpp",
]
+if env["module_webp_enabled"]:
+ thirdparty_sources += ["src/loaders/external_webp/tvgWebpLoader.cpp"]
+ env_svg.Append(CPPDEFINES=["THORVG_WEBP_LOADER_SUPPORT"])
+
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_svg.Prepend(CPPPATH=[thirdparty_dir + "inc"])
@@ -76,12 +79,15 @@ env_thirdparty.Prepend(
thirdparty_dir + "src/renderer/sw_engine",
thirdparty_dir + "src/loaders/raw",
thirdparty_dir + "src/loaders/external_png",
- thirdparty_dir + "src/loaders/external_webp",
thirdparty_dir + "src/loaders/jpg",
- "#thirdparty/libpng",
- "#thirdparty/libwebp/src",
]
)
+if env["builtin_libpng"]:
+ env_thirdparty.Prepend(CPPPATH=["#thirdparty/libpng"])
+if env["module_webp_enabled"]:
+ env_thirdparty.Prepend(CPPPATH=[thirdparty_dir + "src/loaders/external_webp"])
+ if env["builtin_libwebp"]:
+ env_thirdparty.Prepend(CPPPATH=["#thirdparty/libwebp/src"])
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 0bf3927e14..06dfe7d646 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -585,17 +585,20 @@ bool EditorExportPlatformWeb::poll_export() {
}
}
+ int prev = menu_options;
+ menu_options = preset.is_valid();
HTTPServerState prev_server_state = server_state;
server_state = HTTP_SERVER_STATE_OFF;
if (server->is_listening()) {
- if (preset.is_null()) {
+ if (preset.is_null() || menu_options == 0) {
server->stop();
} else {
server_state = HTTP_SERVER_STATE_ON;
+ menu_options += 1;
}
}
- return server_state != prev_server_state;
+ return server_state != prev_server_state || menu_options != prev;
}
Ref<ImageTexture> EditorExportPlatformWeb::get_option_icon(int p_index) const {
diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h
index 9d3a1a7861..d3d2083a23 100644
--- a/platform/web/export/export_plugin.h
+++ b/platform/web/export/export_plugin.h
@@ -58,6 +58,7 @@ class EditorExportPlatformWeb : public EditorExportPlatform {
HTTPServerState server_state = HTTP_SERVER_STATE_OFF;
Ref<EditorHTTPServer> server;
+ int menu_options = 0;
String _get_template_name(bool p_extension, bool p_thread_support, bool p_debug) const {
String name = "web";
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 15b7f62036..3bf9d79c7f 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -50,6 +50,83 @@ void Button::_set_internal_margin(Side p_side, float p_value) {
void Button::_queue_update_size_cache() {
}
+void Button::_update_theme_item_cache() {
+ Control::_update_theme_item_cache();
+
+ const bool rtl = is_layout_rtl();
+ if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) {
+ theme_cache.max_style_size = theme_cache.normal_mirrored->get_minimum_size();
+ theme_cache.style_margin_left = theme_cache.normal_mirrored->get_margin(SIDE_LEFT);
+ theme_cache.style_margin_right = theme_cache.normal_mirrored->get_margin(SIDE_RIGHT);
+ theme_cache.style_margin_top = theme_cache.normal_mirrored->get_margin(SIDE_TOP);
+ theme_cache.style_margin_bottom = theme_cache.normal_mirrored->get_margin(SIDE_BOTTOM);
+ } else {
+ theme_cache.max_style_size = theme_cache.normal->get_minimum_size();
+ theme_cache.style_margin_left = theme_cache.normal->get_margin(SIDE_LEFT);
+ theme_cache.style_margin_right = theme_cache.normal->get_margin(SIDE_RIGHT);
+ theme_cache.style_margin_top = theme_cache.normal->get_margin(SIDE_TOP);
+ theme_cache.style_margin_bottom = theme_cache.normal->get_margin(SIDE_BOTTOM);
+ }
+ if (has_theme_stylebox("hover_pressed")) {
+ if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) {
+ theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover_pressed_mirrored->get_minimum_size());
+ theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover_pressed_mirrored->get_margin(SIDE_LEFT));
+ theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover_pressed_mirrored->get_margin(SIDE_RIGHT));
+ theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover_pressed_mirrored->get_margin(SIDE_TOP));
+ theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover_pressed_mirrored->get_margin(SIDE_BOTTOM));
+ } else {
+ theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover_pressed->get_minimum_size());
+ theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover_pressed->get_margin(SIDE_LEFT));
+ theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover_pressed->get_margin(SIDE_RIGHT));
+ theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover_pressed->get_margin(SIDE_TOP));
+ theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover_pressed->get_margin(SIDE_BOTTOM));
+ }
+ }
+ if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) {
+ theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.pressed_mirrored->get_minimum_size());
+ theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.pressed_mirrored->get_margin(SIDE_LEFT));
+ theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.pressed_mirrored->get_margin(SIDE_RIGHT));
+ theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.pressed_mirrored->get_margin(SIDE_TOP));
+ theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.pressed_mirrored->get_margin(SIDE_BOTTOM));
+ } else {
+ theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.pressed->get_minimum_size());
+ theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.pressed->get_margin(SIDE_LEFT));
+ theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.pressed->get_margin(SIDE_RIGHT));
+ theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.pressed->get_margin(SIDE_TOP));
+ theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.pressed->get_margin(SIDE_BOTTOM));
+ }
+ if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) {
+ theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover_mirrored->get_minimum_size());
+ theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover_mirrored->get_margin(SIDE_LEFT));
+ theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover_mirrored->get_margin(SIDE_RIGHT));
+ theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover_mirrored->get_margin(SIDE_TOP));
+ theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover_mirrored->get_margin(SIDE_BOTTOM));
+ } else {
+ theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.hover->get_minimum_size());
+ theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.hover->get_margin(SIDE_LEFT));
+ theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.hover->get_margin(SIDE_RIGHT));
+ theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.hover->get_margin(SIDE_TOP));
+ theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.hover->get_margin(SIDE_BOTTOM));
+ }
+ if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) {
+ theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.disabled_mirrored->get_minimum_size());
+ theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.disabled_mirrored->get_margin(SIDE_LEFT));
+ theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.disabled_mirrored->get_margin(SIDE_RIGHT));
+ theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.disabled_mirrored->get_margin(SIDE_TOP));
+ theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.disabled_mirrored->get_margin(SIDE_BOTTOM));
+ } else {
+ theme_cache.max_style_size = theme_cache.max_style_size.max(theme_cache.disabled->get_minimum_size());
+ theme_cache.style_margin_left = MAX(theme_cache.style_margin_left, theme_cache.disabled->get_margin(SIDE_LEFT));
+ theme_cache.style_margin_right = MAX(theme_cache.style_margin_right, theme_cache.disabled->get_margin(SIDE_RIGHT));
+ theme_cache.style_margin_top = MAX(theme_cache.style_margin_top, theme_cache.disabled->get_margin(SIDE_TOP));
+ theme_cache.style_margin_bottom = MAX(theme_cache.style_margin_bottom, theme_cache.disabled->get_margin(SIDE_BOTTOM));
+ }
+}
+
+Size2 Button::_get_largest_stylebox_size() const {
+ return theme_cache.max_style_size;
+}
+
Ref<StyleBox> Button::_get_current_stylebox() const {
Ref<StyleBox> stylebox = theme_cache.normal;
const bool rtl = is_layout_rtl();
@@ -137,16 +214,13 @@ void Button::_notification(int p_what) {
const RID ci = get_canvas_item();
const Size2 size = get_size();
- const Ref<StyleBox> style = _get_current_stylebox();
- { // Draws the stylebox in the current state.
- if (!flat) {
- style->draw(ci, Rect2(Point2(), size));
- }
+ // Draws the stylebox in the current state.
+ if (!flat) {
+ _get_current_stylebox()->draw(ci, Rect2(Point2(), size));
+ }
- if (has_focus()) {
- Ref<StyleBox> style2 = theme_cache.focus;
- style2->draw(ci, Rect2(Point2(), size));
- }
+ if (has_focus()) {
+ theme_cache.focus->draw(ci, Rect2(Point2(), size));
}
Ref<Texture2D> _icon = icon;
@@ -158,16 +232,11 @@ void Button::_notification(int p_what) {
break;
}
- const float style_margin_left = style->get_margin(SIDE_LEFT);
- const float style_margin_right = style->get_margin(SIDE_RIGHT);
- const float style_margin_top = style->get_margin(SIDE_TOP);
- const float style_margin_bottom = style->get_margin(SIDE_BOTTOM);
-
Size2 drawable_size_remained = size;
{ // The size after the stelybox is stripped.
- drawable_size_remained.width -= style_margin_left + style_margin_right;
- drawable_size_remained.height -= style_margin_top + style_margin_bottom;
+ drawable_size_remained.width -= theme_cache.style_margin_left + theme_cache.style_margin_right;
+ drawable_size_remained.height -= theme_cache.style_margin_top + theme_cache.style_margin_bottom;
}
const int h_separation = MAX(0, theme_cache.h_separation);
@@ -312,12 +381,12 @@ void Button::_notification(int p_what) {
[[fallthrough]];
case HORIZONTAL_ALIGNMENT_FILL:
case HORIZONTAL_ALIGNMENT_LEFT: {
- icon_ofs.x += style_margin_left;
+ icon_ofs.x += theme_cache.style_margin_left;
icon_ofs.x += left_internal_margin_with_h_separation;
} break;
case HORIZONTAL_ALIGNMENT_RIGHT: {
- icon_ofs.x = size.x - style_margin_right;
+ icon_ofs.x = size.x - theme_cache.style_margin_right;
icon_ofs.x -= right_internal_margin_with_h_separation;
icon_ofs.x -= icon_size.width;
} break;
@@ -330,11 +399,11 @@ void Button::_notification(int p_what) {
[[fallthrough]];
case VERTICAL_ALIGNMENT_FILL:
case VERTICAL_ALIGNMENT_TOP: {
- icon_ofs.y += style_margin_top;
+ icon_ofs.y += theme_cache.style_margin_top;
} break;
case VERTICAL_ALIGNMENT_BOTTOM: {
- icon_ofs.y = size.y - style_margin_bottom - icon_size.height;
+ icon_ofs.y = size.y - theme_cache.style_margin_bottom - icon_size.height;
} break;
}
icon_ofs = icon_ofs.floor();
@@ -373,7 +442,7 @@ void Button::_notification(int p_what) {
case HORIZONTAL_ALIGNMENT_FILL:
case HORIZONTAL_ALIGNMENT_LEFT:
case HORIZONTAL_ALIGNMENT_RIGHT: {
- text_ofs.x += style_margin_left;
+ text_ofs.x += theme_cache.style_margin_left;
text_ofs.x += left_internal_margin_with_h_separation;
if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
// Offset by the space's width that occupied by icon and h_separation together.
@@ -382,7 +451,7 @@ void Button::_notification(int p_what) {
} break;
}
- text_ofs.y = (drawable_size_remained.height - text_buf->get_size().height) / 2.0f + style_margin_top;
+ text_ofs.y = (drawable_size_remained.height - text_buf->get_size().height) / 2.0f + theme_cache.style_margin_top;
if (vertical_icon_alignment == VERTICAL_ALIGNMENT_TOP) {
text_ofs.y += custom_element_size.height - drawable_size_remained.height; // Offset by the icon's height.
}
@@ -452,7 +521,7 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
}
}
- return _get_current_stylebox()->get_minimum_size() + minsize;
+ return _get_largest_stylebox_size() + minsize;
}
void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) {
diff --git a/scene/gui/button.h b/scene/gui/button.h
index 86fdbd35d5..d2f80893e0 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -69,6 +69,12 @@ private:
Ref<StyleBox> disabled_mirrored;
Ref<StyleBox> focus;
+ Size2 max_style_size;
+ float style_margin_left = 0;
+ float style_margin_right = 0;
+ float style_margin_top = 0;
+ float style_margin_bottom = 0;
+
Color font_color;
Color font_focus_color;
Color font_pressed_color;
@@ -100,10 +106,13 @@ private:
void _texture_changed();
protected:
+ virtual void _update_theme_item_cache() override;
+
void _set_internal_margin(Side p_side, float p_value);
virtual void _queue_update_size_cache();
Ref<StyleBox> _get_current_stylebox() const;
+ Size2 _get_largest_stylebox_size() const;
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index af6696834e..0dcd1d4ac4 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -66,7 +66,7 @@ Size2 CheckBox::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
const Size2 tex_size = get_icon_size();
if (tex_size.width > 0 || tex_size.height > 0) {
- const Size2 padding = _get_current_stylebox()->get_minimum_size();
+ const Size2 padding = _get_largest_stylebox_size();
Size2 content_size = minsize - padding;
if (content_size.width > 0 && tex_size.width > 0) {
content_size.width += MAX(0, theme_cache.h_separation);
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index ab3b74a3c3..fe449fbfc3 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -70,7 +70,7 @@ Size2 CheckButton::get_minimum_size() const {
Size2 minsize = Button::get_minimum_size();
const Size2 tex_size = get_icon_size();
if (tex_size.width > 0 || tex_size.height > 0) {
- const Size2 padding = _get_current_stylebox()->get_minimum_size();
+ const Size2 padding = _get_largest_stylebox_size();
Size2 content_size = minsize - padding;
if (content_size.width > 0 && tex_size.width > 0) {
content_size.width += MAX(0, theme_cache.h_separation);
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 0f161a014a..68e72ea996 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -60,7 +60,7 @@ Size2 OptionButton::get_minimum_size() const {
}
if (has_theme_icon(SNAME("arrow"))) {
- const Size2 padding = _get_current_stylebox()->get_minimum_size();
+ const Size2 padding = _get_largest_stylebox_size();
const Size2 arrow_size = theme_cache.arrow_icon->get_size();
Size2 content_size = minsize - padding;
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 1d45a10d2a..38204af6d5 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -37,6 +37,7 @@
void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
+ hide_reason = HIDE_REASON_CANCELED; // ESC pressed, mark as canceled unconditionally.
_close_pressed();
}
Window::_input_from_window(p_event);
@@ -104,13 +105,18 @@ void Popup::_notification(int p_what) {
case NOTIFICATION_WM_CLOSE_REQUEST: {
if (!is_in_edited_scene_root()) {
- hide_reason = HIDE_REASON_UNFOCUSED;
+ if (hide_reason == HIDE_REASON_NONE) {
+ hide_reason = HIDE_REASON_UNFOCUSED;
+ }
_close_pressed();
}
} break;
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
if (!is_in_edited_scene_root() && get_flag(FLAG_POPUP)) {
+ if (hide_reason == HIDE_REASON_NONE) {
+ hide_reason = HIDE_REASON_UNFOCUSED;
+ }
_close_pressed();
}
} break;
@@ -119,7 +125,9 @@ void Popup::_notification(int p_what) {
void Popup::_parent_focused() {
if (popped_up && get_flag(FLAG_POPUP)) {
- hide_reason = HIDE_REASON_UNFOCUSED;
+ if (hide_reason == HIDE_REASON_NONE) {
+ hide_reason = HIDE_REASON_UNFOCUSED;
+ }
_close_pressed();
}
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 49cfa8a030..69b84da23d 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -558,8 +558,6 @@ void TextEdit::_notification(int p_what) {
int visible_rows = get_visible_line_count() + 1;
- Color color = !editable ? theme_cache.font_readonly_color : theme_cache.font_color;
-
if (theme_cache.background_color.a > 0.01) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), theme_cache.background_color);
}
@@ -734,7 +732,7 @@ void TextEdit::_notification(int p_what) {
if (draw_minimap) {
int minimap_visible_lines = get_minimap_visible_lines();
int minimap_line_height = (minimap_char_size.y + minimap_line_spacing);
- int minimap_tab_size = minimap_char_size.x * text.get_tab_size();
+ int tab_size = text.get_tab_size();
// Calculate viewport size and y offset.
int viewport_height = (draw_amount - 1) * minimap_line_height;
@@ -839,68 +837,74 @@ void TextEdit::_notification(int p_what) {
}
}
- Color previous_color;
+ Color next_color = current_color;
int characters = 0;
- int tabs = 0;
+ int tab_alignment = 0;
+ int xpos = xmargin_end + 2 + indent_px;
for (int j = 0; j < str.length(); j++) {
- const Variant *color_data = color_map.getptr(last_wrap_column + j);
- if (color_data != nullptr) {
- current_color = (color_data->operator Dictionary()).get("color", theme_cache.font_color);
- if (!editable) {
- current_color.a = theme_cache.font_readonly_color.a;
+ bool next_is_whitespace = false;
+ bool next_is_tab = false;
+ // Get the number of characters to draw together.
+ for (characters = 0; j + characters < str.length(); characters++) {
+ int next_char_index = j + characters;
+ const Variant *color_data = color_map.getptr(last_wrap_column + next_char_index);
+ if (color_data != nullptr) {
+ next_color = (color_data->operator Dictionary()).get("color", theme_cache.font_color);
+ if (!editable) {
+ next_color.a = theme_cache.font_readonly_color.a;
+ }
+ next_color.a *= 0.6;
}
- }
- color = current_color;
-
- if (j == 0) {
- previous_color = color;
- }
-
- int xpos = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * j)) + tabs;
- bool out_of_bounds = (xpos >= xmargin_end + minimap_width);
-
- bool whitespace = is_whitespace(str[j]);
- if (!whitespace) {
- characters++;
-
- if (j < str.length() - 1 && color == previous_color && !out_of_bounds) {
- continue;
+ if (characters == 0) {
+ current_color = next_color;
}
-
- // If we've changed color we are at the start of a new section, therefore we need to go back to the end
- // of the previous section to draw it, we'll also add the character back on.
- if (color != previous_color) {
- characters--;
- j--;
-
- if (str[j] == '\t') {
- tabs -= minimap_tab_size;
+ if (next_color != current_color) {
+ break;
+ }
+ next_is_whitespace = is_whitespace(str[next_char_index]);
+ if (next_is_whitespace) {
+ if (str[next_char_index] == '\t') {
+ next_is_tab = true;
}
+ break;
+ }
+ bool out_of_bounds = xpos + minimap_char_size.x * characters >= xmargin_end + minimap_width;
+ if (out_of_bounds) {
+ break;
}
}
+ if (!next_is_whitespace && characters == 0) {
+ break;
+ }
if (characters > 0) {
- previous_color.a *= 0.6;
- // Take one for zero indexing, and if we hit whitespace / the end of a word.
- int chars = MAX(0, (j - (characters - 1)) - (whitespace ? 1 : 0)) + 1;
- int char_x_ofs = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * chars)) + tabs;
if (rtl) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - char_x_ofs - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - xpos - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), current_color);
} else {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_x_ofs, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(xpos, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), current_color);
}
}
- if (out_of_bounds) {
- break;
- }
+ j += characters - 1;
+ xpos += minimap_char_size.x * characters;
+ tab_alignment += characters;
- if (str[j] == '\t') {
- tabs += minimap_tab_size;
+ if (next_is_whitespace) {
+ if (next_is_tab) {
+ tab_alignment %= tab_size;
+ xpos += minimap_char_size.x * (tab_size - tab_alignment);
+ tab_alignment = 0;
+ } else {
+ xpos += minimap_char_size.x;
+ tab_alignment += 1;
+ }
+ j += 1;
}
- previous_color = color;
- characters = 0;
+ if (xpos >= xmargin_end + minimap_width) {
+ // Out of bounds.
+ break;
+ }
}
}
}
@@ -1188,6 +1192,7 @@ void TextEdit::_notification(int p_what) {
if (!clipped && lookup_symbol_word.length() != 0) { // Highlight word
if (is_ascii_alphabet_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '_' || lookup_symbol_word[0] == '.') {
+ Color highlight_underline_color = !editable ? theme_cache.font_readonly_color : theme_cache.font_color;
int lookup_symbol_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
int lookup_symbol_word_len = lookup_symbol_word.length();
while (lookup_symbol_word_col != -1) {
@@ -1205,7 +1210,7 @@ void TextEdit::_notification(int p_what) {
}
rect.position.y += ceil(TS->shaped_text_get_ascent(rid)) + ceil(theme_cache.font->get_underline_position(theme_cache.font_size));
rect.size.y = MAX(1, theme_cache.font->get_underline_thickness(theme_cache.font_size));
- draw_rect(rect, color);
+ draw_rect(rect, highlight_underline_color);
}
lookup_symbol_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, lookup_symbol_word_col + lookup_symbol_word_len);
@@ -7973,7 +7978,7 @@ void TextEdit::_update_minimap_click() {
Point2 mp = get_local_mouse_pos();
int xmargin_end = get_size().width - theme_cache.style_normal->get_margin(SIDE_RIGHT);
- if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.y > xmargin_end)) {
+ if (!dragging_minimap && (mp.x < xmargin_end - minimap_width || mp.x > xmargin_end)) {
minimap_clicked = false;
return;
}
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 133515754b..fc5b942918 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -3152,8 +3152,12 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
}
void Tree::_text_editor_popup_modal_close() {
+ if (popup_edit_commited) {
+ return; // Already processed by LineEdit/TextEdit commit.
+ }
+
if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) {
- return;
+ return; // ESC pressed, app focus lost, or forced close from code.
}
if (value_editor->has_point(value_editor->get_local_mouse_position())) {
@@ -3172,9 +3176,18 @@ void Tree::_text_editor_popup_modal_close() {
}
void Tree::_text_editor_gui_input(const Ref<InputEvent> &p_event) {
+ if (popup_edit_commited) {
+ return; // Already processed by _text_editor_popup_modal_close
+ }
+
+ if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) {
+ return; // ESC pressed, app focus lost, or forced close from code.
+ }
+
if (p_event->is_action_pressed("ui_text_newline_blank", true)) {
accept_event();
} else if (p_event->is_action_pressed("ui_text_newline")) {
+ popup_edit_commited = true; // End edit popup processing.
popup_editor->hide();
_apply_multiline_edit();
accept_event();
@@ -3205,6 +3218,15 @@ void Tree::_apply_multiline_edit() {
}
void Tree::_line_editor_submit(String p_text) {
+ if (popup_edit_commited) {
+ return; // Already processed by _text_editor_popup_modal_close
+ }
+
+ if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) {
+ return; // ESC pressed, app focus lost, or forced close from code.
+ }
+
+ popup_edit_commited = true; // End edit popup processing.
popup_editor->hide();
if (!popup_edited_item) {
@@ -4072,6 +4094,7 @@ bool Tree::edit_selected(bool p_force_edit) {
if (!popup_editor->is_embedded()) {
popup_editor->set_content_scale_factor(popup_scale);
}
+ popup_edit_commited = false; // Start edit popup processing.
popup_editor->popup();
popup_editor->child_controls_changed();
@@ -4091,6 +4114,7 @@ bool Tree::edit_selected(bool p_force_edit) {
if (!popup_editor->is_embedded()) {
popup_editor->set_content_scale_factor(popup_scale);
}
+ popup_edit_commited = false; // Start edit popup processing.
popup_editor->popup();
popup_editor->child_controls_changed();
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 311055a2f8..e9c93c6e03 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -479,6 +479,7 @@ private:
VBoxContainer *popup_editor_vb = nullptr;
+ bool popup_edit_commited = true;
Popup *popup_editor = nullptr;
LineEdit *line_editor = nullptr;
TextEdit *text_editor = nullptr;
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 5e9510c1ac..8267e958b4 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -155,7 +155,7 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr
DocData::PropertyDoc prop_doc;
prop_doc.name = "shader_parameter/" + pi.name;
#ifdef MODULE_REGEX_ENABLED
- const RegEx pattern("/\\*\\*([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/\\s*uniform\\s+\\w+\\s+" + pi.name + "(?=[\\s:;=])");
+ const RegEx pattern("/\\*\\*\\s([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/\\s*uniform\\s+\\w+\\s+" + pi.name + "(?=[\\s:;=])");
Ref<RegExMatch> pattern_ref = pattern.search(code);
if (pattern_ref != nullptr) {
RegExMatch *match = pattern_ref.ptr();
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
index 012451a5cd..14d138181f 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
@@ -169,7 +169,11 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
return;
}
- RD::get_singleton()->screen_prepare_for_drawing(DisplayServer::MAIN_WINDOW_ID);
+ Error err = RD::get_singleton()->screen_prepare_for_drawing(DisplayServer::MAIN_WINDOW_ID);
+ if (err != OK) {
+ // Window is minimized and does not have valid swapchain, skip drawing without printing errors.
+ return;
+ }
RID texture = texture_storage->texture_allocate();
texture_storage->texture_2d_initialize(texture, p_image);
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index d630238cb9..15e1731823 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -3154,9 +3154,6 @@ Error RenderingDevice::screen_create(DisplayServer::WindowID p_screen) {
RDD::SwapChainID swap_chain = driver->swap_chain_create(surface);
ERR_FAIL_COND_V_MSG(swap_chain.id == 0, ERR_CANT_CREATE, "Unable to create swap chain.");
- Error err = driver->swap_chain_resize(main_queue, swap_chain, _get_swap_chain_desired_count());
- ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Unable to resize the new swap chain.");
-
screen_swap_chains[p_screen] = swap_chain;
return OK;
diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h
index f271b52faf..3ae166cf22 100644
--- a/thirdparty/thorvg/inc/config.h
+++ b/thirdparty/thorvg/inc/config.h
@@ -5,9 +5,11 @@
#define THORVG_SVG_LOADER_SUPPORT
#define THORVG_PNG_LOADER_SUPPORT
#define THORVG_JPG_LOADER_SUPPORT
-#define THORVG_WEBP_LOADER_SUPPORT
#define THORVG_THREAD_SUPPORT
+// Added conditionally if webp module is enabled.
+//#define THORVG_WEBP_LOADER_SUPPORT
+
// For internal debugging:
//#define THORVG_LOG_ENABLED
diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh
index 23604afbcd..796060d3fb 100755
--- a/thirdparty/thorvg/update-thorvg.sh
+++ b/thirdparty/thorvg/update-thorvg.sh
@@ -38,9 +38,11 @@ cat << EOF > ../inc/config.h
#define THORVG_SVG_LOADER_SUPPORT
#define THORVG_PNG_LOADER_SUPPORT
#define THORVG_JPG_LOADER_SUPPORT
-#define THORVG_WEBP_LOADER_SUPPORT
#define THORVG_THREAD_SUPPORT
+// Added conditionally if webp module is enabled.
+//#define THORVG_WEBP_LOADER_SUPPORT
+
// For internal debugging:
//#define THORVG_LOG_ENABLED