diff options
47 files changed, 784 insertions, 325 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 12240aef93..d09ecabe70 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,37 +35,10 @@ repos: types_or: [text] - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell - types_or: [text] - exclude: | - (?x)^( - .*\.desktop$| - .*\.gitignore$| - .*\.po$| - .*\.pot$| - .*\.rc$| - \.mailmap$| - AUTHORS.md$| - COPYRIGHT.txt$| - DONORS.md$| - core/input/gamecontrollerdb.txt$| - core/string/locales.h$| - editor/project_converter_3_to_4.cpp$| - platform/android/java/lib/src/com/.*| - platform/web/package-lock.json$ - ) - args: - - --enable-colors - - --write-changes - - --check-hidden - - --quiet-level - - '3' - - --ignore-words-list - - aesthetic,aesthetics,breaked,cancelled,colour,curvelinear,doubleclick,expct,findn,gird,hel,inout,lod,mis,nd,numer,ot,requestor,te,thirdparty,vai - - --builtin - - clear,rare,en-GB_to_en-US + additional_dependencies: [tomli] ### Requires Docker; look into alternative implementation. # - repo: https://github.com/comkieffer/pre-commit-xmllint.git diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 0996db9d89..e8a6a5075b 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" #include "core/debugger/engine_debugger.h" +#include "core/debugger/script_debugger.h" #include "core/io/file_access_compressed.h" #include "core/io/file_access_encrypted.h" #include "core/io/marshalls.h" @@ -1919,6 +1920,16 @@ void EngineDebugger::send_message(const String &p_msg, const Array &p_data) { ::EngineDebugger::get_singleton()->send_message(p_msg, p_data); } +void EngineDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { + ERR_FAIL_COND_MSG(!::EngineDebugger::is_active(), "Can't send debug. No active debugger"); + ::EngineDebugger::get_singleton()->debug(p_can_continue, p_is_error_breakpoint); +} + +void EngineDebugger::script_debug(ScriptLanguage *p_lang, bool p_can_continue, bool p_is_error_breakpoint) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't send debug. No active debugger"); + ::EngineDebugger::get_script_debugger()->debug(p_lang, p_can_continue, p_is_error_breakpoint); +} + Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) { Callable &capture = *(Callable *)p_user; if (!capture.is_valid()) { @@ -1935,6 +1946,56 @@ Error EngineDebugger::call_capture(void *p_user, const String &p_cmd, const Arra return OK; } +void EngineDebugger::line_poll() { + ERR_FAIL_COND_MSG(!::EngineDebugger::is_active(), "Can't poll. No active debugger"); + ::EngineDebugger::get_singleton()->line_poll(); +} + +void EngineDebugger::set_lines_left(int p_lines) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't set lines left. No active debugger"); + ::EngineDebugger::get_script_debugger()->set_lines_left(p_lines); +} + +int EngineDebugger::get_lines_left() const { + ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), 0, "Can't get lines left. No active debugger"); + return ::EngineDebugger::get_script_debugger()->get_lines_left(); +} + +void EngineDebugger::set_depth(int p_depth) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't set depth. No active debugger"); + ::EngineDebugger::get_script_debugger()->set_depth(p_depth); +} + +int EngineDebugger::get_depth() const { + ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), 0, "Can't get depth. No active debugger"); + return ::EngineDebugger::get_script_debugger()->get_depth(); +} + +bool EngineDebugger::is_breakpoint(int p_line, const StringName &p_source) const { + ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), false, "Can't check breakpoint. No active debugger"); + return ::EngineDebugger::get_script_debugger()->is_breakpoint(p_line, p_source); +} + +bool EngineDebugger::is_skipping_breakpoints() const { + ERR_FAIL_COND_V_MSG(!::EngineDebugger::get_script_debugger(), false, "Can't check skipping breakpoint. No active debugger"); + return ::EngineDebugger::get_script_debugger()->is_skipping_breakpoints(); +} + +void EngineDebugger::insert_breakpoint(int p_line, const StringName &p_source) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't insert breakpoint. No active debugger"); + ::EngineDebugger::get_script_debugger()->insert_breakpoint(p_line, p_source); +} + +void EngineDebugger::remove_breakpoint(int p_line, const StringName &p_source) { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't remove breakpoint. No active debugger"); + ::EngineDebugger::get_script_debugger()->remove_breakpoint(p_line, p_source); +} + +void EngineDebugger::clear_breakpoints() { + ERR_FAIL_COND_MSG(!::EngineDebugger::get_script_debugger(), "Can't clear breakpoints. No active debugger"); + ::EngineDebugger::get_script_debugger()->clear_breakpoints(); +} + EngineDebugger::~EngineDebugger() { for (const KeyValue<StringName, Callable> &E : captures) { ::EngineDebugger::unregister_message_capture(E.key); @@ -1960,7 +2021,23 @@ void EngineDebugger::_bind_methods() { ClassDB::bind_method(D_METHOD("unregister_message_capture", "name"), &EngineDebugger::unregister_message_capture); ClassDB::bind_method(D_METHOD("has_capture", "name"), &EngineDebugger::has_capture); + ClassDB::bind_method(D_METHOD("line_poll"), &EngineDebugger::line_poll); + ClassDB::bind_method(D_METHOD("send_message", "message", "data"), &EngineDebugger::send_message); + ClassDB::bind_method(D_METHOD("debug", "can_continue", "is_error_breakpoint"), &EngineDebugger::debug, DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("script_debug", "language", "can_continue", "is_error_breakpoint"), &EngineDebugger::script_debug, DEFVAL(true), DEFVAL(false)); + + ClassDB::bind_method(D_METHOD("set_lines_left", "lines"), &EngineDebugger::set_lines_left); + ClassDB::bind_method(D_METHOD("get_lines_left"), &EngineDebugger::get_lines_left); + + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &EngineDebugger::set_depth); + ClassDB::bind_method(D_METHOD("get_depth"), &EngineDebugger::get_depth); + + ClassDB::bind_method(D_METHOD("is_breakpoint", "line", "source"), &EngineDebugger::is_breakpoint); + ClassDB::bind_method(D_METHOD("is_skipping_breakpoints"), &EngineDebugger::is_skipping_breakpoints); + ClassDB::bind_method(D_METHOD("insert_breakpoint", "line", "source"), &EngineDebugger::insert_breakpoint); + ClassDB::bind_method(D_METHOD("remove_breakpoint", "line", "source"), &EngineDebugger::remove_breakpoint); + ClassDB::bind_method(D_METHOD("clear_breakpoints"), &EngineDebugger::clear_breakpoints); } } // namespace core_bind diff --git a/core/core_bind.h b/core/core_bind.h index 148e0ad83e..febc33a9c1 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -576,9 +576,25 @@ public: bool has_capture(const StringName &p_name); void send_message(const String &p_msg, const Array &p_data); + void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false); + void script_debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false); static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured); + void line_poll(); + + void set_lines_left(int p_lines); + int get_lines_left() const; + + void set_depth(int p_depth); + int get_depth() const; + + bool is_breakpoint(int p_line, const StringName &p_source) const; + bool is_skipping_breakpoints() const; + void insert_breakpoint(int p_line, const StringName &p_source); + void remove_breakpoint(int p_line, const StringName &p_source); + void clear_breakpoints(); + EngineDebugger() { singleton = this; } ~EngineDebugger(); }; diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index fcf4a727ca..4c16650439 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -410,6 +410,7 @@ void ResourceFormatImporter::get_importers_for_extension(const String &p_extensi for (const String &F : local_exts) { if (p_extension.to_lower() == F) { r_importers->push_back(importers[i]); + break; } } } diff --git a/core/object/object.cpp b/core/object/object.cpp index 303624e6d7..0383753f38 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -923,6 +923,7 @@ void Object::notification(int p_notification, bool p_reversed) { } String Object::to_string() { + // Keep this method in sync with `Node::to_string`. if (script_instance) { bool valid; String ret = script_instance->to_string(&valid); diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp index a18ef8d4d7..7b643e4637 100644 --- a/core/object/script_language_extension.cpp +++ b/core/object/script_language_extension.cpp @@ -132,6 +132,7 @@ void ScriptLanguageExtension::_bind_methods() { GDVIRTUAL_BIND(_debug_get_stack_level_line, "level"); GDVIRTUAL_BIND(_debug_get_stack_level_function, "level"); + GDVIRTUAL_BIND(_debug_get_stack_level_source, "level"); GDVIRTUAL_BIND(_debug_get_stack_level_locals, "level", "max_subitems", "max_depth"); GDVIRTUAL_BIND(_debug_get_stack_level_members, "level", "max_subitems", "max_depth"); GDVIRTUAL_BIND(_debug_get_stack_level_instance, "level"); diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index 0f7884305a..4d67cd930e 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -159,11 +159,10 @@ void UndoRedo::add_do_method(const Callable &p_callable) { do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object)); } do_op.type = Operation::TYPE_METHOD; - // There's no `get_method()` for custom callables, so use `operator String()` instead. - if (p_callable.is_custom()) { + do_op.name = p_callable.get_method(); + if (do_op.name == StringName()) { + // There's no `get_method()` for custom callables, so use `operator String()` instead. do_op.name = static_cast<String>(p_callable); - } else { - do_op.name = p_callable.get_method(); } actions.write[current_action + 1].do_ops.push_back(do_op); @@ -191,11 +190,10 @@ void UndoRedo::add_undo_method(const Callable &p_callable) { } undo_op.type = Operation::TYPE_METHOD; undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; - // There's no `get_method()` for custom callables, so use `operator String()` instead. - if (p_callable.is_custom()) { + undo_op.name = p_callable.get_method(); + if (undo_op.name == StringName()) { + // There's no `get_method()` for custom callables, so use `operator String()` instead. undo_op.name = static_cast<String>(p_callable); - } else { - undo_op.name = p_callable.get_method(); } actions.write[current_action + 1].undo_ops.push_back(undo_op); diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index d2af6179d9..98f25ed573 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -118,6 +118,9 @@ <theme_item name="icon_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> Icon modulate [Color] used when the [Button] is being pressed. </theme_item> + <theme_item name="align_to_largest_stylebox" data_type="constant" type="int" default="0"> + This constant acts as a boolean. If [code]true[/code], text and icon are always aligned to the largest stylebox margins, otherwise it's aligned to the current button state stylebox margins. + </theme_item> <theme_item name="h_separation" data_type="constant" type="int" default="4"> The horizontal space between [Button]'s icon and text. Negative values will be treated as [code]0[/code] when used. </theme_item> diff --git a/doc/classes/EditorDebuggerPlugin.xml b/doc/classes/EditorDebuggerPlugin.xml index 4c602b8359..a519e43bc6 100644 --- a/doc/classes/EditorDebuggerPlugin.xml +++ b/doc/classes/EditorDebuggerPlugin.xml @@ -47,6 +47,21 @@ <tutorials> </tutorials> <methods> + <method name="_breakpoint_set_in_tree" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="script" type="Script" /> + <param index="1" name="line" type="int" /> + <param index="2" name="enabled" type="bool" /> + <description> + Override this method to be notified when a breakpoint is set in the editor. + </description> + </method> + <method name="_breakpoints_cleared_in_tree" qualifiers="virtual"> + <return type="void" /> + <description> + Override this method to be notified when all breakpoints are cleared in the editor. + </description> + </method> <method name="_capture" qualifiers="virtual"> <return type="bool" /> <param index="0" name="message" type="String" /> @@ -56,6 +71,14 @@ Override this method to process incoming messages. The [param session_id] is the ID of the [EditorDebuggerSession] that received the message (which you can retrieve via [method get_session]). </description> </method> + <method name="_goto_script_line" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="script" type="Script" /> + <param index="1" name="line" type="int" /> + <description> + Override this method to be notified when a breakpoint line has been clicked in the debugger breakpoint panel. + </description> + </method> <method name="_has_capture" qualifiers="virtual const"> <return type="bool" /> <param index="0" name="capture" type="String" /> diff --git a/doc/classes/EditorDebuggerSession.xml b/doc/classes/EditorDebuggerSession.xml index c6c632be01..b4e754cc7e 100644 --- a/doc/classes/EditorDebuggerSession.xml +++ b/doc/classes/EditorDebuggerSession.xml @@ -50,6 +50,15 @@ Sends the given [param message] to the attached remote instance, optionally passing additionally [param data]. See [EngineDebugger] for how to retrieve those messages. </description> </method> + <method name="set_breakpoint"> + <return type="void" /> + <param index="0" name="path" type="String" /> + <param index="1" name="line" type="int" /> + <param index="2" name="enabled" type="bool" /> + <description> + Enables or disables a specific breakpoint based on [param enabled], updating the Editor Breakpoint Panel accordingly. + </description> + </method> <method name="toggle_profiler"> <return type="void" /> <param index="0" name="profiler" type="String" /> diff --git a/doc/classes/EngineDebugger.xml b/doc/classes/EngineDebugger.xml index 29ac04f097..7583520da0 100644 --- a/doc/classes/EngineDebugger.xml +++ b/doc/classes/EngineDebugger.xml @@ -9,6 +9,32 @@ <tutorials> </tutorials> <methods> + <method name="clear_breakpoints"> + <return type="void" /> + <description> + Clears all breakpoints. + </description> + </method> + <method name="debug"> + <return type="void" /> + <param index="0" name="can_continue" type="bool" default="true" /> + <param index="1" name="is_error_breakpoint" type="bool" default="false" /> + <description> + Starts a debug break in script execution, optionally specifying whether the program can continue based on [param can_continue] and whether the break was due to a breakpoint. + </description> + </method> + <method name="get_depth" qualifiers="const" experimental=""> + <return type="int" /> + <description> + Returns the current debug depth. + </description> + </method> + <method name="get_lines_left" qualifiers="const" experimental=""> + <return type="int" /> + <description> + Returns the number of lines that remain. + </description> + </method> <method name="has_capture"> <return type="bool" /> <param index="0" name="name" type="StringName" /> @@ -23,12 +49,28 @@ Returns [code]true[/code] if a profiler with the given name is present otherwise [code]false[/code]. </description> </method> + <method name="insert_breakpoint"> + <return type="void" /> + <param index="0" name="line" type="int" /> + <param index="1" name="source" type="StringName" /> + <description> + Inserts a new breakpoint with the given [param source] and [param line]. + </description> + </method> <method name="is_active"> <return type="bool" /> <description> Returns [code]true[/code] if the debugger is active otherwise [code]false[/code]. </description> </method> + <method name="is_breakpoint" qualifiers="const"> + <return type="bool" /> + <param index="0" name="line" type="int" /> + <param index="1" name="source" type="StringName" /> + <description> + Returns [code]true[/code] if the given [param source] and [param line] represent an existing breakpoint. + </description> + </method> <method name="is_profiling"> <return type="bool" /> <param index="0" name="name" type="StringName" /> @@ -36,6 +78,18 @@ Returns [code]true[/code] if a profiler with the given name is present and active otherwise [code]false[/code]. </description> </method> + <method name="is_skipping_breakpoints" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the debugger is skipping breakpoints otherwise [code]false[/code]. + </description> + </method> + <method name="line_poll"> + <return type="void" /> + <description> + Forces a processing loop of debugger events. The purpose of this method is just processing events every now and then when the script might get too busy, so that bugs like infinite loops can be caught + </description> + </method> <method name="profiler_add_frame_data"> <return type="void" /> <param index="0" name="name" type="StringName" /> @@ -70,6 +124,23 @@ Registers a profiler with the given [param name]. See [EngineProfiler] for more information. </description> </method> + <method name="remove_breakpoint"> + <return type="void" /> + <param index="0" name="line" type="int" /> + <param index="1" name="source" type="StringName" /> + <description> + Removes a breakpoint with the given [param source] and [param line]. + </description> + </method> + <method name="script_debug"> + <return type="void" /> + <param index="0" name="language" type="ScriptLanguage" /> + <param index="1" name="can_continue" type="bool" default="true" /> + <param index="2" name="is_error_breakpoint" type="bool" default="false" /> + <description> + Starts a debug break in script execution, optionally specifying whether the program can continue based on [param can_continue] and whether the break was due to a breakpoint. + </description> + </method> <method name="send_message"> <return type="void" /> <param index="0" name="message" type="String" /> @@ -78,6 +149,20 @@ Sends a message with given [param message] and [param data] array. </description> </method> + <method name="set_depth" experimental=""> + <return type="void" /> + <param index="0" name="depth" type="int" /> + <description> + Sets the current debugging depth. + </description> + </method> + <method name="set_lines_left" experimental=""> + <return type="void" /> + <param index="0" name="lines" type="int" /> + <description> + Sets the current debugging lines that remain. + </description> + </method> <method name="unregister_message_capture"> <return type="void" /> <param index="0" name="name" type="StringName" /> diff --git a/doc/classes/ScriptLanguageExtension.xml b/doc/classes/ScriptLanguageExtension.xml index a453866e27..cc47ca274d 100644 --- a/doc/classes/ScriptLanguageExtension.xml +++ b/doc/classes/ScriptLanguageExtension.xml @@ -108,6 +108,13 @@ <description> </description> </method> + <method name="_debug_get_stack_level_source" qualifiers="virtual const"> + <return type="String" /> + <param index="0" name="level" type="int" /> + <description> + Returns the source associated with a given debug stack position. + </description> + </method> <method name="_debug_parse_stack_level_expression" qualifiers="virtual"> <return type="String" /> <param index="0" name="level" type="int" /> diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 37e00bf042..cdad1f0fab 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1725,7 +1725,7 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) { if (!_find_file(file, &fs, cpos)) { if (!fs) { - return; + continue; } } @@ -1748,77 +1748,72 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) { memdelete(fs->files[cpos]); fs->files.remove_at(cpos); } + } else { + String type = ResourceLoader::get_resource_type(file); + if (type.is_empty() && textfile_extensions.has(file.get_extension())) { + type = "TextFile"; + } + String script_class = ResourceLoader::get_resource_script_class(file); - _update_pending_script_classes(); - _update_pending_scene_groups(); - call_deferred(SNAME("emit_signal"), "filesystem_changed"); //update later - return; - } - - String type = ResourceLoader::get_resource_type(file); - if (type.is_empty() && textfile_extensions.has(file.get_extension())) { - type = "TextFile"; - } - String script_class = ResourceLoader::get_resource_script_class(file); - - ResourceUID::ID uid = ResourceLoader::get_resource_uid(file); + ResourceUID::ID uid = ResourceLoader::get_resource_uid(file); - if (cpos == -1) { - // The file did not exist, it was added. - int idx = 0; - String file_name = file.get_file(); + if (cpos == -1) { + // The file did not exist, it was added. + int idx = 0; + String file_name = file.get_file(); - for (int i = 0; i < fs->files.size(); i++) { - if (file.filenocasecmp_to(fs->files[i]->file) < 0) { - break; + for (int i = 0; i < fs->files.size(); i++) { + if (file.filenocasecmp_to(fs->files[i]->file) < 0) { + break; + } + idx++; } - idx++; - } - EditorFileSystemDirectory::FileInfo *fi = memnew(EditorFileSystemDirectory::FileInfo); - fi->file = file_name; - fi->import_modified_time = 0; - fi->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file); + EditorFileSystemDirectory::FileInfo *fi = memnew(EditorFileSystemDirectory::FileInfo); + fi->file = file_name; + fi->import_modified_time = 0; + fi->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file); - if (idx == fs->files.size()) { - fs->files.push_back(fi); + if (idx == fs->files.size()) { + fs->files.push_back(fi); + } else { + fs->files.insert(idx, fi); + } + cpos = idx; } else { - fs->files.insert(idx, fi); + //the file exists and it was updated, and was not added in this step. + //this means we must force upon next restart to scan it again, to get proper type and dependencies + late_update_files.insert(file); + _save_late_updated_files(); //files need to be updated in the re-scan } - cpos = idx; - } else { - //the file exists and it was updated, and was not added in this step. - //this means we must force upon next restart to scan it again, to get proper type and dependencies - late_update_files.insert(file); - _save_late_updated_files(); //files need to be updated in the re-scan - } - fs->files[cpos]->type = type; - fs->files[cpos]->resource_script_class = script_class; - fs->files[cpos]->uid = uid; - fs->files[cpos]->script_class_name = _get_global_script_class(type, file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path); - fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(file); - fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); - fs->files[cpos]->deps = _get_dependencies(file); - fs->files[cpos]->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file); + fs->files[cpos]->type = type; + fs->files[cpos]->resource_script_class = script_class; + fs->files[cpos]->uid = uid; + fs->files[cpos]->script_class_name = _get_global_script_class(type, file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path); + fs->files[cpos]->import_group_file = ResourceLoader::get_import_group_file(file); + fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); + fs->files[cpos]->deps = _get_dependencies(file); + fs->files[cpos]->import_valid = type == "TextFile" ? true : ResourceLoader::is_import_valid(file); + + if (uid != ResourceUID::INVALID_ID) { + if (ResourceUID::get_singleton()->has_id(uid)) { + ResourceUID::get_singleton()->set_id(uid, file); + } else { + ResourceUID::get_singleton()->add_id(uid, file); + } - if (uid != ResourceUID::INVALID_ID) { - if (ResourceUID::get_singleton()->has_id(uid)) { - ResourceUID::get_singleton()->set_id(uid, file); - } else { - ResourceUID::get_singleton()->add_id(uid, file); + ResourceUID::get_singleton()->update_cache(); } + // Update preview + EditorResourcePreview::get_singleton()->check_for_invalidation(file); - ResourceUID::get_singleton()->update_cache(); - } - // Update preview - EditorResourcePreview::get_singleton()->check_for_invalidation(file); - - if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { - _queue_update_script_class(file); - } - if (fs->files[cpos]->type == SNAME("PackedScene")) { - _queue_update_scene_groups(file); + if (ClassDB::is_parent_class(fs->files[cpos]->type, SNAME("Script"))) { + _queue_update_script_class(file); + } + if (fs->files[cpos]->type == SNAME("PackedScene")) { + _queue_update_scene_groups(file); + } } } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 94bd590fc1..d5e1242406 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4928,7 +4928,7 @@ String EditorNode::_get_system_info() const { const String &vad_version = video_adapter_driver_info[1]; // Version could be potentially empty on Linux/BSD. if (!vad_version.is_empty()) { graphics += vformat(" (%s; %s)", vad_name, vad_version); - } else { + } else if (!vad_name.is_empty()) { graphics += vformat(" (%s)", vad_name); } } diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp index 129d0544c3..3d1eae26af 100644 --- a/editor/import/3d/scene_import_settings.cpp +++ b/editor/import/3d/scene_import_settings.cpp @@ -609,7 +609,7 @@ void SceneImportSettingsDialog::_update_camera() { float rot_y = cam_rot_y; float zoom = cam_zoom; - if (selected_type == "Node" || selected_type.is_empty()) { + if (selected_type == "Node" || selected_type == "Animation" || selected_type.is_empty()) { camera_aabb = contents_aabb; } else { if (mesh_preview->get_mesh().is_valid()) { diff --git a/editor/plugins/editor_debugger_plugin.cpp b/editor/plugins/editor_debugger_plugin.cpp index c96bec6e7f..fbb389ccb4 100644 --- a/editor/plugins/editor_debugger_plugin.cpp +++ b/editor/plugins/editor_debugger_plugin.cpp @@ -56,6 +56,7 @@ void EditorDebuggerSession::_bind_methods() { ClassDB::bind_method(D_METHOD("is_active"), &EditorDebuggerSession::is_active); ClassDB::bind_method(D_METHOD("add_session_tab", "control"), &EditorDebuggerSession::add_session_tab); ClassDB::bind_method(D_METHOD("remove_session_tab", "control"), &EditorDebuggerSession::remove_session_tab); + ClassDB::bind_method(D_METHOD("set_breakpoint", "path", "line", "enabled"), &EditorDebuggerSession::set_breakpoint); ADD_SIGNAL(MethodInfo("started")); ADD_SIGNAL(MethodInfo("stopped")); @@ -100,6 +101,11 @@ bool EditorDebuggerSession::is_active() { return debugger->is_session_active(); } +void EditorDebuggerSession::set_breakpoint(const String &p_path, int p_line, bool p_enabled) { + ERR_FAIL_NULL_MSG(debugger, "Plugin is not attached to debugger."); + debugger->set_breakpoint(p_path, p_line, p_enabled); +} + void EditorDebuggerSession::detach_debugger() { if (!debugger) { return; @@ -184,10 +190,31 @@ bool EditorDebuggerPlugin::capture(const String &p_message, const Array &p_data, return false; } +void EditorDebuggerPlugin::goto_script_line(const Ref<Script> &p_script, int p_line) { + GDVIRTUAL_CALL(_goto_script_line, p_script, p_line); +} + +void EditorDebuggerPlugin::breakpoints_cleared_in_tree() { + GDVIRTUAL_CALL(_breakpoints_cleared_in_tree); +} + +void EditorDebuggerPlugin::breakpoint_set_in_tree(const Ref<Script> &p_script, int p_line, bool p_enabled) { + GDVIRTUAL_CALL(_breakpoint_set_in_tree, p_script, p_line, p_enabled); +} + void EditorDebuggerPlugin::_bind_methods() { GDVIRTUAL_BIND(_setup_session, "session_id"); GDVIRTUAL_BIND(_has_capture, "capture"); GDVIRTUAL_BIND(_capture, "message", "data", "session_id"); + GDVIRTUAL_BIND(_goto_script_line, "script", "line"); + GDVIRTUAL_BIND(_breakpoints_cleared_in_tree); + GDVIRTUAL_BIND(_breakpoint_set_in_tree, "script", "line", "enabled"); ClassDB::bind_method(D_METHOD("get_session", "id"), &EditorDebuggerPlugin::get_session); ClassDB::bind_method(D_METHOD("get_sessions"), &EditorDebuggerPlugin::get_sessions); } + +EditorDebuggerPlugin::EditorDebuggerPlugin() { + EditorDebuggerNode::get_singleton()->connect("goto_script_line", callable_mp(this, &EditorDebuggerPlugin::goto_script_line)); + EditorDebuggerNode::get_singleton()->connect("breakpoints_cleared_in_tree", callable_mp(this, &EditorDebuggerPlugin::breakpoints_cleared_in_tree)); + EditorDebuggerNode::get_singleton()->connect("breakpoint_set_in_tree", callable_mp(this, &EditorDebuggerPlugin::breakpoint_set_in_tree)); +} diff --git a/editor/plugins/editor_debugger_plugin.h b/editor/plugins/editor_debugger_plugin.h index 41f34f67cf..4f09824d03 100644 --- a/editor/plugins/editor_debugger_plugin.h +++ b/editor/plugins/editor_debugger_plugin.h @@ -62,6 +62,8 @@ public: bool is_debuggable(); bool is_active(); + void set_breakpoint(const String &p_path, int p_line, bool p_enabled); + EditorDebuggerSession(ScriptEditorDebugger *p_debugger); ~EditorDebuggerSession(); }; @@ -90,7 +92,15 @@ public: GDVIRTUAL1RC(bool, _has_capture, const String &); GDVIRTUAL1(_setup_session, int); - EditorDebuggerPlugin() {} + virtual void goto_script_line(const Ref<Script> &p_script, int p_line); + virtual void breakpoints_cleared_in_tree(); + virtual void breakpoint_set_in_tree(const Ref<Script> &p_script, int p_line, bool p_enabled); + + GDVIRTUAL2(_goto_script_line, const Ref<Script> &, int); + GDVIRTUAL0(_breakpoints_cleared_in_tree); + GDVIRTUAL3(_breakpoint_set_in_tree, const Ref<Script> &, int, bool); + + EditorDebuggerPlugin(); ~EditorDebuggerPlugin(); }; diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp index 8926f612a2..ec59bbb543 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.cpp +++ b/editor/plugins/navigation_polygon_editor_plugin.cpp @@ -217,6 +217,8 @@ void NavigationPolygonEditor::_clear_pressed() { if (node) { if (node->get_navigation_polygon().is_valid()) { node->get_navigation_polygon()->clear(); + // Needed to update all the region internals. + node->set_navigation_polygon(node->get_navigation_polygon()); } } diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index 406425e9fd..2623079cfe 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -126,6 +126,16 @@ void BackgroundProgress::end_task(const String &p_task) { ProgressDialog *ProgressDialog::singleton = nullptr; +void ProgressDialog::_update_ui() { + // Run main loop for two frames. + if (is_inside_tree()) { + DisplayServer::get_singleton()->process_events(); +#ifndef ANDROID_ENABLED + Main::iteration(); +#endif + } +} + void ProgressDialog::_popup() { Size2 ms = main->get_combined_minimum_size(); ms.width = MAX(500 * EDSCALE, ms.width); @@ -138,7 +148,13 @@ void ProgressDialog::_popup() { main->set_offset(SIDE_TOP, style->get_margin(SIDE_TOP)); main->set_offset(SIDE_BOTTOM, -style->get_margin(SIDE_BOTTOM)); - if (!is_inside_tree()) { + if (is_inside_tree()) { + Rect2i adjust = _popup_adjust_rect(); + if (adjust != Rect2i()) { + set_position(adjust.position); + set_size(adjust.size); + } + } else { for (Window *window : host_windows) { if (window->has_focus()) { popup_exclusive_centered(window, ms); @@ -182,6 +198,7 @@ void ProgressDialog::add_task(const String &p_task, const String &p_label, int p if (p_can_cancel) { cancel->grab_focus(); } + _update_ui(); } bool ProgressDialog::task_step(const String &p_task, const String &p_state, int p_step, bool p_force_redraw) { @@ -203,11 +220,8 @@ bool ProgressDialog::task_step(const String &p_task, const String &p_state, int t.state->set_text(p_state); last_progress_tick = OS::get_singleton()->get_ticks_usec(); - DisplayServer::get_singleton()->process_events(); + _update_ui(); -#ifndef ANDROID_ENABLED - Main::iteration(); // this will not work on a lot of platforms, so it's only meant for the editor -#endif return canceled; } diff --git a/editor/progress_dialog.h b/editor/progress_dialog.h index 74196a28df..82d59219da 100644 --- a/editor/progress_dialog.h +++ b/editor/progress_dialog.h @@ -85,6 +85,8 @@ class ProgressDialog : public PopupPanel { void _popup(); void _cancel_pressed(); + + void _update_ui(); bool canceled = false; public: diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 4448f165d3..11e477e044 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2695,6 +2695,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history(); editor_history->cleanup_history(); InspectorDock::get_singleton()->call("_prepare_history"); + InspectorDock::get_singleton()->update(nullptr); } void SceneTreeDock::_update_script_button() { diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index 9a0941c75e..10b7c64999 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -451,6 +451,9 @@ void EditorThemeManager::_create_shared_styles(const Ref<EditorTheme> &p_theme, p_theme->set_color("success_color", EditorStringName(Editor), p_config.success_color); p_theme->set_color("warning_color", EditorStringName(Editor), p_config.warning_color); p_theme->set_color("error_color", EditorStringName(Editor), p_config.error_color); +#ifndef DISABLE_DEPRECATED // Used before 4.3. + p_theme->set_color("disabled_highlight_color", EditorStringName(Editor), p_config.highlight_disabled_color); +#endif // Only used when the Draw Extra Borders editor setting is enabled. p_config.extra_border_color_1 = Color(0.5, 0.5, 0.5); @@ -480,6 +483,12 @@ void EditorThemeManager::_create_shared_styles(const Ref<EditorTheme> &p_theme, p_theme->set_color("font_readonly_color", EditorStringName(Editor), p_config.font_readonly_color); p_theme->set_color("font_placeholder_color", EditorStringName(Editor), p_config.font_placeholder_color); p_theme->set_color("font_outline_color", EditorStringName(Editor), p_config.font_outline_color); +#ifndef DISABLE_DEPRECATED // Used before 4.3. + p_theme->set_color("readonly_font_color", EditorStringName(Editor), p_config.font_readonly_color); + p_theme->set_color("disabled_font_color", EditorStringName(Editor), p_config.font_disabled_color); + p_theme->set_color("readonly_color", EditorStringName(Editor), p_config.font_readonly_color); + p_theme->set_color("highlighted_font_color", EditorStringName(Editor), p_config.font_hover_color); // Closest equivalent. +#endif // Icon colors. @@ -728,6 +737,8 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the p_theme->set_constant("h_separation", "Button", 4 * EDSCALE); p_theme->set_constant("outline_size", "Button", 0); + p_theme->set_constant("align_to_largest_stylebox", "Button", 1); // Enabled. + // MenuButton. p_theme->set_stylebox(CoreStringName(normal), "MenuButton", p_config.panel_container_style); @@ -2106,6 +2117,9 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme p_theme->set_color("prop_category", EditorStringName(Editor), prop_category_color); p_theme->set_color("prop_section", EditorStringName(Editor), prop_section_color); p_theme->set_color("prop_subsection", EditorStringName(Editor), prop_subsection_color); +#ifndef DISABLE_DEPRECATED // Used before 4.3. + p_theme->set_color("property_color", EditorStringName(Editor), prop_category_color); +#endif // EditorInspectorCategory. diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 03c7a8291a..b8f244ca02 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -126,13 +126,6 @@ Validate extension JSON: Error: Field 'classes/Animation/methods/value_track_int Added optional argument to track_interpolate to treat playing backward correctly. Compatibility method registered. -GH-86661 --------- -Validate extension JSON: Error: Field 'classes/Animation/methods/track_find_key/arguments': size changed value in new API, from 3 to 4. - -Added optional argument to track_find_key to avoid finding keys out of the animation range. Compatibility method registered. - - GH-84792 -------- Validate extension JSON: Error: Field 'classes/RenderingServer/methods/environment_set_fog/arguments': size changed value in new API, from 10 to 11. @@ -365,8 +358,10 @@ Validate extension JSON: Error: Field 'classes/EditorInspectorPlugin/methods/add Optional arguments added. Compatibility methods registered. -GH-86661 + +GH-92861 -------- Validate extension JSON: Error: Field 'classes/Animation/methods/track_find_key/arguments': size changed value in new API, from 3 to 5. -Added optional argument to track_find_key to handle backward seeking. Compatibility method registered. +Added optional arguments to avoid finding keys out of the animation range (GH-86661), and to handle backward seeking. +Compatibility method registered. diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 11d4a4002c..fcabb7c4a8 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -3571,9 +3571,13 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co switch (base_type.kind) { case GDScriptParser::DataType::CLASS: { if (base_type.class_type) { - if (base_type.class_type->has_member(p_symbol)) { + String name = p_symbol; + if (name == "new") { + name = "_init"; + } + if (base_type.class_type->has_member(name)) { r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION; - r_result.location = base_type.class_type->get_member(p_symbol).get_line(); + r_result.location = base_type.class_type->get_member(name).get_line(); r_result.class_path = base_type.script_path; Error err = OK; r_result.script = GDScriptCache::get_shallow_script(r_result.class_path, err); diff --git a/platform/web/detect.py b/platform/web/detect.py index c6568625c1..a9be1471ae 100644 --- a/platform/web/detect.py +++ b/platform/web/detect.py @@ -204,7 +204,7 @@ def configure(env: "SConsEnvironment"): if env["opengl3"]: env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"]) # This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1. - env.Append(LINKFLAGS=["-s", "USE_WEBGL2=1"]) + env.Append(LINKFLAGS=["-s", "MAX_WEBGL_VERSION=2"]) # Allow use to take control of swapping WebGL buffers. env.Append(LINKFLAGS=["-s", "OFFSCREEN_FRAMEBUFFER=1"]) # Breaking change since emscripten 3.1.51 diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 0d3e9c606e..5092c6fadf 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -1,4 +1,5 @@ import os +import re import subprocess import sys from typing import TYPE_CHECKING @@ -304,6 +305,7 @@ def setup_msvc_manual(env: "SConsEnvironment"): print("Using VCVARS-determined MSVC, arch %s" % (env_arch)) +# FIXME: Likely overwrites command-line options for the msvc compiler. See #91883. def setup_msvc_auto(env: "SConsEnvironment"): """Set up MSVC using SCons's auto-detection logic""" @@ -386,33 +388,64 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env["MAXLINELENGTH"] = 8192 # Windows Vista and beyond, so always applicable. - if env["silence_msvc"]: + if env["silence_msvc"] and not env.GetOption("clean"): from tempfile import mkstemp + # Ensure we have a location to write captured output to, in case of false positives. + capture_path = methods.base_folder_path + "platform/windows/msvc_capture.log" + with open(capture_path, "wt"): + pass + old_spawn = env["SPAWN"] + re_redirect_stream = re.compile(r"^[12]?>") + re_cl_capture = re.compile(r"^.+\.(c|cc|cpp|cxx|c[+]{2})$", re.IGNORECASE) + re_link_capture = re.compile(r'\s{3}\S.+\s(?:"[^"]+.lib"|\S+.lib)\s.+\s(?:"[^"]+.exp"|\S+.exp)') def spawn_capture(sh, escape, cmd, args, env): # We only care about cl/link, process everything else as normal. if args[0] not in ["cl", "link"]: return old_spawn(sh, escape, cmd, args, env) + # Process as normal if the user is manually rerouting output. + for arg in args: + if re_redirect_stream.match(arg): + return old_spawn(sh, escape, cmd, args, env) + tmp_stdout, tmp_stdout_name = mkstemp() os.close(tmp_stdout) args.append(f">{tmp_stdout_name}") ret = old_spawn(sh, escape, cmd, args, env) try: - with open(tmp_stdout_name, "rb") as tmp_stdout: - # First line is always bloat, subsequent lines are always errors. If content - # exists after discarding the first line, safely decode & send to stderr. - tmp_stdout.readline() - content = tmp_stdout.read() - if content: - sys.stderr.write(content.decode(sys.stdout.encoding, "replace")) + with open(tmp_stdout_name, encoding="oem", errors="replace") as tmp_stdout: + lines = tmp_stdout.read().splitlines() os.remove(tmp_stdout_name) except OSError: pass + # Early process no lines (OSError) + if not lines: + return ret + + is_cl = args[0] == "cl" + content = "" + caught = False + for line in lines: + # These conditions are far from all-encompassing, but are specialized + # for what can be reasonably expected to show up in the repository. + if not caught and (is_cl and re_cl_capture.match(line)) or (not is_cl and re_link_capture.match(line)): + caught = True + try: + with open(capture_path, "a") as log: + log.write(line + "\n") + except OSError: + print_warning(f'Failed to log captured line: "{line}".') + continue + content += line + "\n" + # Content remaining assumed to be an error/warning. + if content: + sys.stderr.write(content) + return ret env["SPAWN"] = spawn_capture diff --git a/pyproject.toml b/pyproject.toml index 34ae075f2b..59b6d09a03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,3 +27,51 @@ extend-select = [ "E402", # Module level import not at top of file "F821", # Undefined name ] + +[tool.codespell] +enable-colors = "" +write-changes = "" +check-hidden = "" +quiet-level = 3 +builtin = "clear,rare,en-GB_to_en-US" +skip = """\ + .mailmap, + *.desktop, + *.gitignore, + *.po, + *.pot, + *.rc, + AUTHORS.md, + COPYRIGHT.txt, + core/input/gamecontrollerdb.txt, + core/string/locales.h, + DONORS.md, + editor/project_converter_3_to_4.cpp, + platform/android/java/lib/src/com/*, + platform/web/package-lock.json +""" +ignore-words-list = """\ + breaked, + cancelled, + checkin, + colour, + curvelinear, + doubleclick, + expct, + findn, + gird, + hel, + inout, + labelin, + lod, + mis, + nd, + numer, + ot, + outin, + requestor, + te, + textin, + thirdparty, + vai +""" diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 18ef2d8505..514c5e7a8f 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -74,6 +74,14 @@ void Camera2D::_update_scroll() { } } +#ifdef TOOLS_ENABLED +void Camera2D::_project_settings_changed() { + if (screen_drawing_enabled) { + queue_redraw(); + } +} +#endif + void Camera2D::_update_process_callback() { if (is_physics_interpolated_and_enabled()) { set_process_internal(is_current()); @@ -267,6 +275,14 @@ void Camera2D::_ensure_update_interpolation_data() { void Camera2D::_notification(int p_what) { switch (p_what) { +#ifdef TOOLS_ENABLED + case NOTIFICATION_READY: { + if (Engine::get_singleton()->is_editor_hint() && is_part_of_edited_scene()) { + ProjectSettings::get_singleton()->connect(SNAME("settings_changed"), callable_mp(this, &Camera2D::_project_settings_changed)); + } + } break; +#endif + case NOTIFICATION_INTERNAL_PROCESS: { _update_scroll(); } break; diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index bf25267aa8..8754e35e88 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -88,6 +88,9 @@ protected: bool _is_editing_in_editor() const; void _update_process_callback(); void _update_scroll(); +#ifdef TOOLS_ENABLED + void _project_settings_changed(); +#endif void _make_current(Object *p_which); void _reset_just_exited() { just_exited_tree = false; } diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index bad9de5daa..9b3c7bb9ea 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -458,6 +458,7 @@ void NavigationRegion2D::_update_debug_mesh() { return; } + rs->canvas_item_clear(debug_instance_rid); rs->mesh_clear(debug_mesh_rid); debug_mesh_dirty = false; diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 3bf9d79c7f..55ca5fc3ee 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -121,6 +121,7 @@ void Button::_update_theme_item_cache() { 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)); } + theme_cache.max_style_size = theme_cache.max_style_size.max(Vector2(theme_cache.style_margin_left + theme_cache.style_margin_right, theme_cache.style_margin_top + theme_cache.style_margin_bottom)); } Size2 Button::_get_largest_stylebox_size() const { @@ -214,9 +215,10 @@ void Button::_notification(int p_what) { const RID ci = get_canvas_item(); const Size2 size = get_size(); + Ref<StyleBox> style = _get_current_stylebox(); // Draws the stylebox in the current state. if (!flat) { - _get_current_stylebox()->draw(ci, Rect2(Point2(), size)); + style->draw(ci, Rect2(Point2(), size)); } if (has_focus()) { @@ -232,18 +234,23 @@ void Button::_notification(int p_what) { break; } + const float style_margin_left = (theme_cache.align_to_largest_stylebox) ? theme_cache.style_margin_left : style->get_margin(SIDE_LEFT); + const float style_margin_right = (theme_cache.align_to_largest_stylebox) ? theme_cache.style_margin_right : style->get_margin(SIDE_RIGHT); + const float style_margin_top = (theme_cache.align_to_largest_stylebox) ? theme_cache.style_margin_top : style->get_margin(SIDE_TOP); + const float style_margin_bottom = (theme_cache.align_to_largest_stylebox) ? theme_cache.style_margin_bottom : style->get_margin(SIDE_BOTTOM); + Size2 drawable_size_remained = size; { // The size after the stelybox is stripped. - 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; + drawable_size_remained.width -= style_margin_left + style_margin_right; + drawable_size_remained.height -= style_margin_top + style_margin_bottom; } const int h_separation = MAX(0, theme_cache.h_separation); float left_internal_margin_with_h_separation = _internal_margin[SIDE_LEFT]; float right_internal_margin_with_h_separation = _internal_margin[SIDE_RIGHT]; - { // The width reserved for internal element in derived classes (and h_separation if need). + { // The width reserved for internal element in derived classes (and h_separation if needed). if (_internal_margin[SIDE_LEFT] > 0.0f) { left_internal_margin_with_h_separation += h_separation; @@ -381,12 +388,12 @@ void Button::_notification(int p_what) { [[fallthrough]]; case HORIZONTAL_ALIGNMENT_FILL: case HORIZONTAL_ALIGNMENT_LEFT: { - icon_ofs.x += theme_cache.style_margin_left; + icon_ofs.x += style_margin_left; icon_ofs.x += left_internal_margin_with_h_separation; } break; case HORIZONTAL_ALIGNMENT_RIGHT: { - icon_ofs.x = size.x - theme_cache.style_margin_right; + icon_ofs.x = size.x - style_margin_right; icon_ofs.x -= right_internal_margin_with_h_separation; icon_ofs.x -= icon_size.width; } break; @@ -399,11 +406,11 @@ void Button::_notification(int p_what) { [[fallthrough]]; case VERTICAL_ALIGNMENT_FILL: case VERTICAL_ALIGNMENT_TOP: { - icon_ofs.y += theme_cache.style_margin_top; + icon_ofs.y += style_margin_top; } break; case VERTICAL_ALIGNMENT_BOTTOM: { - icon_ofs.y = size.y - theme_cache.style_margin_bottom - icon_size.height; + icon_ofs.y = size.y - style_margin_bottom - icon_size.height; } break; } icon_ofs = icon_ofs.floor(); @@ -442,7 +449,7 @@ void Button::_notification(int p_what) { case HORIZONTAL_ALIGNMENT_FILL: case HORIZONTAL_ALIGNMENT_LEFT: case HORIZONTAL_ALIGNMENT_RIGHT: { - text_ofs.x += theme_cache.style_margin_left; + text_ofs.x += 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. @@ -451,7 +458,7 @@ void Button::_notification(int p_what) { } break; } - text_ofs.y = (drawable_size_remained.height - text_buf->get_size().height) / 2.0f + theme_cache.style_margin_top; + text_ofs.y = (drawable_size_remained.height - text_buf->get_size().height) / 2.0f + 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. } @@ -493,6 +500,21 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu minsize.width = 0; } + float left_internal_margin_with_h_separation = _internal_margin[SIDE_LEFT]; + float right_internal_margin_with_h_separation = _internal_margin[SIDE_RIGHT]; + { // The width reserved for internal element in derived classes (and h_separation if needed). + + if (_internal_margin[SIDE_LEFT] > 0.0f) { + left_internal_margin_with_h_separation += theme_cache.h_separation; + } + + if (_internal_margin[SIDE_RIGHT] > 0.0f) { + right_internal_margin_with_h_separation += theme_cache.h_separation; + } + + minsize.width += left_internal_margin_with_h_separation + right_internal_margin_with_h_separation; // The size after the internal element is stripped. + } + if (!expand_icon && p_icon.is_valid()) { Size2 icon_size = _fit_icon_size(p_icon->get_size()); if (vertical_icon_alignment == VERTICAL_ALIGNMENT_CENTER) { @@ -828,6 +850,8 @@ void Button::_bind_methods() { BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, h_separation); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, icon_max_width); + + BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, align_to_largest_stylebox); } Button::Button(const String &p_text) { diff --git a/scene/gui/button.h b/scene/gui/button.h index eefb690913..5f4429bc1d 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -75,6 +75,8 @@ private: float style_margin_top = 0; float style_margin_bottom = 0; + bool align_to_largest_stylebox = false; + Color font_color; Color font_focus_color; Color font_pressed_color; diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index c328022d4f..e867fceaf2 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -80,6 +80,7 @@ void Container::remove_child_notify(Node *p_child) { void Container::_sort_children() { if (!is_inside_tree()) { + pending_sort = false; return; } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 7590cf2ad3..d69fa78d9d 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -228,6 +228,36 @@ String RichTextLabel::_letters(int p_num, bool p_capitalize) const { return s; } +String RichTextLabel::_get_prefix(Item *p_item, const Vector<int> &p_list_index, const Vector<ItemList *> &p_list_items) { + String prefix; + int segments = 0; + for (int i = 0; i < p_list_index.size(); i++) { + String segment; + if (p_list_items[i]->list_type == LIST_DOTS) { + if (segments == 0) { + prefix = p_list_items[i]->bullet; + } + break; + } + prefix = "." + prefix; + if (p_list_items[i]->list_type == LIST_NUMBERS) { + segment = itos(p_list_index[i]); + if (is_localizing_numeral_system()) { + segment = TS->format_number(segment, _find_language(p_item)); + } + segments++; + } else if (p_list_items[i]->list_type == LIST_LETTERS) { + segment = _letters(p_list_index[i], p_list_items[i]->capitalize); + segments++; + } else if (p_list_items[i]->list_type == LIST_ROMAN) { + segment = _roman(p_list_index[i], p_list_items[i]->capitalize); + segments++; + } + prefix = segment + prefix; + } + return prefix; +} + void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) { ERR_FAIL_NULL(p_frame); ERR_FAIL_COND(p_line < 0 || p_line >= (int)p_frame->lines.size()); @@ -235,6 +265,51 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref< Line &l = p_frame->lines[p_line]; MutexLock lock(l.text_buf->get_mutex()); + // Prefix. + Vector<int> list_index; + Vector<int> list_count; + Vector<ItemList *> list_items; + _find_list(l.from, list_index, list_count, list_items); + + if (list_items.size() > 0) { + if (list_index[0] == 1) { + // List level start, shape all prefixes for this level and compute max. prefix width. + list_items[0]->max_width = 0; + int index = 0; + for (int i = p_line; i < (int)p_frame->lines.size(); i++) { + Line &list_l = p_frame->lines[i]; + if (_find_list_item(list_l.from) == list_items[0]) { + index++; + + Ref<Font> font = theme_cache.normal_font; + int font_size = theme_cache.normal_font_size; + + ItemFont *font_it = _find_font(list_l.from); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } + } + ItemFontSize *font_size_it = _find_font_size(list_l.from); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; + } + + list_index.write[0] = index; + String prefix = _get_prefix(list_l.from, list_index, list_items); + list_l.text_prefix.instantiate(); + list_l.text_prefix->set_direction(_find_direction(list_l.from)); + list_l.text_prefix->add_string(prefix, font, font_size); + list_items.write[0]->max_width = MAX(list_items[0]->max_width, list_l.text_prefix->get_size().x); + } + } + } + l.prefix_width = list_items[0]->max_width; + } + RID t = l.text_buf->get_rid(); int spans = TS->shaped_get_span_count(t); for (int i = 0; i < spans; i++) { @@ -287,7 +362,7 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font Line &l = p_frame->lines[p_line]; MutexLock lock(l.text_buf->get_mutex()); - l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size); + l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size) + l.prefix_width; l.text_buf->set_width(p_width - l.offset.x); PackedFloat32Array tab_stops = _find_tab_stops(l.from); @@ -378,8 +453,53 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> l.char_offset = *r_char_offset; l.char_count = 0; + // List prefix. + Vector<int> list_index; + Vector<int> list_count; + Vector<ItemList *> list_items; + _find_list(l.from, list_index, list_count, list_items); + + if (list_items.size() > 0) { + if (list_index[0] == 1) { + // List level start, shape all prefixes for this level and compute max. prefix width. + list_items[0]->max_width = 0; + int index = 0; + for (int i = p_line; i < (int)p_frame->lines.size(); i++) { + Line &list_l = p_frame->lines[i]; + if (_find_list_item(list_l.from) == list_items[0]) { + index++; + + Ref<Font> font = theme_cache.normal_font; + int font_size = theme_cache.normal_font_size; + + ItemFont *font_it = _find_font(list_l.from); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } + } + ItemFontSize *font_size_it = _find_font_size(list_l.from); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; + } + + list_index.write[0] = index; + String prefix = _get_prefix(list_l.from, list_index, list_items); + list_l.text_prefix.instantiate(); + list_l.text_prefix->set_direction(_find_direction(list_l.from)); + list_l.text_prefix->add_string(prefix, font, font_size); + list_items.write[0]->max_width = MAX(list_items[0]->max_width, list_l.text_prefix->get_size().x); + } + } + } + l.prefix_width = list_items[0]->max_width; + } + // Add indent. - l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size); + l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size) + l.prefix_width; l.text_buf->set_width(p_width - l.offset.x); l.text_buf->set_alignment(_find_alignment(l.from)); l.text_buf->set_direction(_find_direction(l.from)); @@ -685,45 +805,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o int total_glyphs = (trim_glyphs_ltr || trim_glyphs_rtl) ? get_total_glyph_count() : 0; int visible_glyphs = total_glyphs * visible_ratio; - Vector<int> list_index; - Vector<ItemList *> list_items; - _find_list(l.from, list_index, list_items); - - String prefix; - int segments = 0; - for (int i = 0; i < list_index.size(); i++) { - String segment; - if (list_items[i]->list_type == LIST_DOTS) { - if (segments == 0) { - prefix = list_items[i]->bullet; - } - break; - } - if (rtl) { - prefix = prefix + "."; - } else { - prefix = "." + prefix; - } - if (list_items[i]->list_type == LIST_NUMBERS) { - segment = itos(list_index[i]); - if (is_localizing_numeral_system()) { - segment = TS->format_number(segment, _find_language(l.from)); - } - segments++; - } else if (list_items[i]->list_type == LIST_LETTERS) { - segment = _letters(list_index[i], list_items[i]->capitalize); - segments++; - } else if (list_items[i]->list_type == LIST_ROMAN) { - segment = _roman(list_index[i], list_items[i]->capitalize); - segments++; - } - if (rtl) { - prefix = prefix + segment; - } else { - prefix = segment + prefix; - } - } - // Draw dropcap. int dc_lines = l.text_buf->get_dropcap_lines(); float h_off = l.text_buf->get_dropcap_size().x; @@ -790,27 +871,27 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } bool skip_prefix = (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING && l.char_offset == visible_characters) || (trim_chars && l.char_offset > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs)); - if (!prefix.is_empty() && line == 0 && !skip_prefix) { - Ref<Font> font = theme_cache.normal_font; - int font_size = theme_cache.normal_font_size; - - ItemFont *font_it = _find_font(l.from); - if (font_it) { - if (font_it->font.is_valid()) { - font = font_it->font; + if (l.text_prefix.is_valid() && line == 0 && !skip_prefix) { + Color font_color = _find_color(l.from, p_base_color); + int outline_size = _find_outline_size(l.from, p_outline_size); + Color font_outline_color = _find_outline_color(l.from, p_outline_color); + Color font_shadow_color = p_font_shadow_color * Color(1, 1, 1, font_color.a); + if (rtl) { + if (p_shadow_outline_size > 0 && font_shadow_color.a != 0.0) { + l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x + length, 0) + p_shadow_ofs, p_shadow_outline_size, font_shadow_color); } - if (font_it->font_size > 0) { - font_size = font_it->font_size; + if (outline_size > 0 && font_outline_color.a != 0.0) { + l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x + length, 0), outline_size, font_outline_color); } - } - ItemFontSize *font_size_it = _find_font_size(l.from); - if (font_size_it && font_size_it->font_size > 0) { - font_size = font_size_it->font_size; - } - if (rtl) { - font->draw_string(ci, p_ofs + Vector2(off.x + length, l.text_buf->get_line_ascent(0)), " " + prefix, HORIZONTAL_ALIGNMENT_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color)); + l.text_prefix->draw(ci, p_ofs + Vector2(off.x + length, 0), font_color); } else { - font->draw_string(ci, p_ofs + Vector2(off.x - l.offset.x, l.text_buf->get_line_ascent(0)), prefix + " ", HORIZONTAL_ALIGNMENT_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color)); + if (p_shadow_outline_size > 0 && font_shadow_color.a != 0.0) { + l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0) + p_shadow_ofs, p_shadow_outline_size, font_shadow_color); + } + if (outline_size > 0 && font_outline_color.a != 0.0) { + l.text_prefix->draw_outline(ci, p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0), outline_size, font_outline_color); + } + l.text_prefix->draw(ci, p_ofs + Vector2(off.x - l.text_prefix->get_size().x, 0), font_color); } } @@ -2320,7 +2401,7 @@ RichTextLabel::ItemList *RichTextLabel::_find_list_item(Item *p_item) { return nullptr; } -int RichTextLabel::_find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list) { +int RichTextLabel::_find_list(Item *p_item, Vector<int> &r_index, Vector<int> &r_count, Vector<ItemList *> &r_list) { Item *item = p_item; Item *prev_item = p_item; @@ -2335,15 +2416,20 @@ int RichTextLabel::_find_list(Item *p_item, Vector<int> &r_index, Vector<ItemLis _find_frame(list, &frame, &line); int index = 1; + int count = 1; if (frame != nullptr) { - for (int i = list->line + 1; i <= prev_item->line && i < (int)frame->lines.size(); i++) { + for (int i = list->line + 1; i < (int)frame->lines.size(); i++) { if (_find_list_item(frame->lines[i].from) == list) { - index++; + if (i <= prev_item->line) { + index++; + } + count++; } } } r_index.push_back(index); + r_count.push_back(count); r_list.push_back(list); prev_item = item; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 45dfb88c55..83285bd7cd 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -144,6 +144,8 @@ private: struct Line { Item *from = nullptr; + Ref<TextLine> text_prefix; + float prefix_width = 0; Ref<TextParagraph> text_buf; Color dc_color; int dc_ol_size = 0; @@ -322,6 +324,7 @@ private: bool capitalize = false; int level = 0; String bullet = U"•"; + float max_width = 0; ItemList() { type = ITEM_LIST; } }; @@ -571,7 +574,7 @@ private: int _find_outline_size(Item *p_item, int p_default); ItemList *_find_list_item(Item *p_item); ItemDropcap *_find_dc_item(Item *p_item); - int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list); + int _find_list(Item *p_item, Vector<int> &r_index, Vector<int> &r_count, Vector<ItemList *> &r_list); int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size); PackedFloat32Array _find_tab_stops(Item *p_item); HorizontalAlignment _find_alignment(Item *p_item); @@ -608,6 +611,8 @@ private: Size2 _get_image_size(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Rect2 &p_region = Rect2()); + String _get_prefix(Item *p_item, const Vector<int> &p_list_index, const Vector<ItemList *> &p_list_items); + #ifndef DISABLE_DEPRECATED // Kept for compatibility from 3.x to 4.0. bool _set(const StringName &p_name, const Variant &p_value); diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index dc48945d9b..3c3e6cd30c 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -537,17 +537,20 @@ void TextureProgressBar::_notification(int p_what) { uvs.push_back(uv); } - Point2 center_point = get_relative_center(); - points.push_back(progress_offset + s * center_point); - if (valid_atlas_progress) { - center_point.x = Math::remap(center_point.x, 0, 1, region_rect.position.x / atlas_size.x, (region_rect.position.x + region_rect.size.x) / atlas_size.x); - center_point.y = Math::remap(center_point.y, 0, 1, region_rect.position.y / atlas_size.y, (region_rect.position.y + region_rect.size.y) / atlas_size.y); - } - uvs.push_back(center_point); + // Filter out an edge case where almost equal `from`, `to` were mapped to the same UV. + if (points.size() >= 2) { + Point2 center_point = get_relative_center(); + points.push_back(progress_offset + s * center_point); + if (valid_atlas_progress) { + center_point.x = Math::remap(center_point.x, 0, 1, region_rect.position.x / atlas_size.x, (region_rect.position.x + region_rect.size.x) / atlas_size.x); + center_point.y = Math::remap(center_point.y, 0, 1, region_rect.position.y / atlas_size.y, (region_rect.position.y + region_rect.size.y) / atlas_size.y); + } + uvs.push_back(center_point); - Vector<Color> colors; - colors.push_back(tint_progress); - draw_polygon(points, colors, uvs, progress); + Vector<Color> colors; + colors.push_back(tint_progress); + draw_polygon(points, colors, uvs, progress); + } } // Draw a reference cross. diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 884fc6de07..c6e5a4603e 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2583,6 +2583,7 @@ void Node::get_storable_properties(HashSet<StringName> &r_storable_properties) c } String Node::to_string() { + // Keep this method in sync with `Object::to_string`. ERR_THREAD_GUARD_V(String()); if (get_script_instance()) { bool valid; @@ -2591,7 +2592,12 @@ String Node::to_string() { return ret; } } - + if (_get_extension() && _get_extension()->to_string) { + String ret; + GDExtensionBool is_valid; + _get_extension()->to_string(_get_extension_instance(), &is_valid, &ret); + return ret; + } return (get_name() ? String(get_name()) + ":" : "") + Object::to_string(); } diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 39e5b6de33..7502f97e89 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1354,6 +1354,10 @@ void Window::_notification(int p_what) { _update_window_size(); // Inform DisplayServer of minimum and maximum size. _update_viewport_size(); // Then feed back to the viewport. _update_window_callbacks(); + // Simulate mouse-enter event when mouse is over the window, since OS event might arrive before setting callbacks. + if (!mouse_in_window && Rect2(position, size).has_point(DisplayServer::get_singleton()->mouse_get_position())) { + _event_callback(DisplayServer::WINDOW_EVENT_MOUSE_ENTER); + } RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_VISIBLE); if (DisplayServer::get_singleton()->window_get_flag(DisplayServer::WindowFlags(FLAG_TRANSPARENT), window_id)) { set_transparent_background(true); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 277d568aaf..222f5c8a25 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -5166,11 +5166,11 @@ VisualShaderNodeVarying::PortType VisualShaderNodeVarying::get_port_type(VisualS case VisualShader::VARYING_TYPE_UINT: return PORT_TYPE_SCALAR_UINT; case VisualShader::VARYING_TYPE_VECTOR_2D: - return PORT_TYPE_VECTOR_2D; + return p_port == 0 ? PORT_TYPE_VECTOR_2D : PORT_TYPE_SCALAR; case VisualShader::VARYING_TYPE_VECTOR_3D: - return PORT_TYPE_VECTOR_3D; + return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; case VisualShader::VARYING_TYPE_VECTOR_4D: - return PORT_TYPE_VECTOR_4D; + return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR; case VisualShader::VARYING_TYPE_BOOLEAN: return PORT_TYPE_BOOLEAN; case VisualShader::VARYING_TYPE_TRANSFORM: diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index cb8719fbef..018b7be81c 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -6497,6 +6497,58 @@ bool VisualShaderNodeTextureParameter::is_show_prop_names() const { return true; } +String VisualShaderNodeTextureParameter::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { + if (texture_source != SOURCE_NONE) { + String texture_source_str; + + switch (texture_source) { + case SOURCE_SCREEN: { + texture_source_str = "Screen"; + } break; + case SOURCE_DEPTH: { + texture_source_str = "Depth"; + } break; + case SOURCE_NORMAL_ROUGHNESS: { + texture_source_str = "NormalRoughness"; + } break; + default: + break; + } + + if (texture_type == TYPE_NORMAL_MAP || texture_type == TYPE_ANISOTROPY) { + String texture_type_str; + + switch (texture_type) { + case TYPE_NORMAL_MAP: { + texture_type_str = "Normal Map"; + } break; + case TYPE_ANISOTROPY: { + texture_type_str = "Anisotropic"; + } break; + default: + break; + } + return vformat(RTR("'%s' type is incompatible with '%s' source."), texture_type_str, texture_source_str); + } else if (color_default != COLOR_DEFAULT_WHITE) { + String color_default_str; + + switch (color_default) { + case COLOR_DEFAULT_BLACK: { + color_default_str = "Black"; + } break; + case COLOR_DEFAULT_TRANSPARENT: { + color_default_str = "Transparent"; + } break; + default: + break; + } + return vformat(RTR("'%s' default color is incompatible with '%s' source."), color_default_str, texture_source_str); + } + } + + return ""; +} + HashMap<StringName, String> VisualShaderNodeTextureParameter::get_editable_properties_names() const { HashMap<StringName, String> names; names.insert("texture_type", RTR("Type")); diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index a23ae72def..7a37ffa0e0 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -2543,6 +2543,7 @@ public: virtual HashMap<StringName, String> get_editable_properties_names() const override; virtual bool is_show_prop_names() const override; + virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override; Vector<StringName> get_editable_properties() const override; diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp index 6d187bbd64..5315b0acc9 100644 --- a/scene/theme/default_theme.cpp +++ b/scene/theme/default_theme.cpp @@ -187,6 +187,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("h_separation", "Button", Math::round(4 * scale)); theme->set_constant("icon_max_width", "Button", 0); + theme->set_constant("align_to_largest_stylebox", "Button", 0); // Disabled. + // MenuBar theme->set_stylebox(CoreStringName(normal), "MenuBar", button_normal); theme->set_stylebox("hover", "MenuBar", button_hover); diff --git a/thirdparty/README.md b/thirdparty/README.md index 6878af863f..2b48e19143 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -681,8 +681,8 @@ Collection of single-file libraries used in Godot components. * License: MIT - `qoa.h` * Upstream: https://github.com/phoboslab/qoa - * Version: git (e4c751d61af2c395ea828c5888e728c1953bf09f, 2024) - * Modifications: Inlined functions and patched compiler warnings. + * Version: git (5c2a86d615661f34636cf179abf4fa278d3257e0, 2024) + * Modifications: Inlined functions, patched uninitialized variables and untyped mallocs. * License: MIT - `r128.{c,h}` * Upstream: https://github.com/fahickman/r128 diff --git a/thirdparty/embree/kernels/geometry/pointi.h b/thirdparty/embree/kernels/geometry/pointi.h index f81edb9035..aba8ec4ab3 100644 --- a/thirdparty/embree/kernels/geometry/pointi.h +++ b/thirdparty/embree/kernels/geometry/pointi.h @@ -210,9 +210,9 @@ namespace embree }; /*! output operator */ - friend __forceinline embree_ostream operator<<(embree_ostream cout, const PointMi& line) + friend __forceinline embree_ostream operator<<(embree_ostream cout, const PointMi& point) { - return cout << "Line" << M << "i {" << line.v0 << ", " << line.geomID() << ", " << line.primID() << "}"; + return cout << "Point" << M << "i {" << point.geomID() << ", " << point.primID() << "}"; } public: diff --git a/thirdparty/embree/kernels/subdiv/bezier_curve.h b/thirdparty/embree/kernels/subdiv/bezier_curve.h index 257e0afd40..5e3b5c83b3 100644 --- a/thirdparty/embree/kernels/subdiv/bezier_curve.h +++ b/thirdparty/embree/kernels/subdiv/bezier_curve.h @@ -135,7 +135,7 @@ namespace embree } friend embree_ostream operator<<(embree_ostream cout, const QuadraticBezierCurve& a) { - return cout << "QuadraticBezierCurve ( (" << a.u.lower << ", " << a.u.upper << "), " << a.v0 << ", " << a.v1 << ", " << a.v2 << ")"; + return cout << "QuadraticBezierCurve (" << a.v0 << ", " << a.v1 << ", " << a.v2 << ")"; } }; diff --git a/thirdparty/misc/patches/qoa-min-fix.patch b/thirdparty/misc/patches/qoa-min-fix.patch index 1043d8bbe7..38303a1521 100644 --- a/thirdparty/misc/patches/qoa-min-fix.patch +++ b/thirdparty/misc/patches/qoa-min-fix.patch @@ -1,5 +1,5 @@ diff --git a/qoa.h b/qoa.h -index aa8fb59434..2dde8df098 100644 +index 592082933a..c890b88bd6 100644 --- a/qoa.h +++ b/qoa.h @@ -140,14 +140,14 @@ typedef struct { @@ -24,32 +24,10 @@ index aa8fb59434..2dde8df098 100644 #ifndef QOA_NO_STDIO -@@ -366,7 +366,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned - ), bytes, &p); - - -- for (int c = 0; c < channels; c++) { -+ for (unsigned int c = 0; c < channels; c++) { - /* Write the current LMS state */ - qoa_uint64_t weights = 0; - qoa_uint64_t history = 0; -@@ -380,9 +380,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned - - /* We encode all samples with the channels interleaved on a slice level. - E.g. for stereo: (ch-0, slice 0), (ch 1, slice 0), (ch 0, slice 1), ...*/ -- for (int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) { -+ for (unsigned int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) { - -- for (int c = 0; c < channels; c++) { -+ for (unsigned int c = 0; c < channels; c++) { - int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index); - int slice_start = sample_index * channels + c; - int slice_end = (sample_index + slice_len) * channels + c; -@@ -391,10 +391,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned - 16 scalefactors, encode all samples for the current slice and - meassure the total squared error. */ - qoa_uint64_t best_rank = -1; -- qoa_uint64_t best_error = -1; +@@ -394,9 +394,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned + #ifdef QOA_RECORD_TOTAL_ERROR + qoa_uint64_t best_error = -1; + #endif - qoa_uint64_t best_slice; - qoa_lms_t best_lms; - int best_scalefactor; @@ -59,92 +37,16 @@ index aa8fb59434..2dde8df098 100644 for (int sfi = 0; sfi < 16; sfi++) { /* There is a strong correlation between the scalefactors of -@@ -408,7 +407,6 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned - qoa_lms_t lms = qoa->lms[c]; - qoa_uint64_t slice = scalefactor; - qoa_uint64_t current_rank = 0; -- qoa_uint64_t current_error = 0; - - for (int si = slice_start; si < slice_end; si += channels) { - int sample = sample_data[si]; -@@ -438,7 +436,6 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned - qoa_uint64_t error_sq = error * error; - - current_rank += error_sq + weights_penalty * weights_penalty; -- current_error += error_sq; - if (current_rank > best_rank) { - break; - } -@@ -449,7 +446,6 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned - - if (current_rank < best_rank) { - best_rank = current_rank; -- best_error = current_error; - best_slice = slice; - best_lms = lms; - best_scalefactor = scalefactor; -@@ -492,9 +488,9 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) +@@ -500,7 +500,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */ num_slices * 8 * qoa->channels; /* 8 byte slices */ - unsigned char *bytes = QOA_MALLOC(encoded_size); + unsigned char *bytes = (unsigned char *)QOA_MALLOC(encoded_size); -- for (int c = 0; c < qoa->channels; c++) { -+ for (unsigned int c = 0; c < qoa->channels; c++) { + for (unsigned int c = 0; c < qoa->channels; c++) { /* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the - prediction of the first few ms of a file. */ - qoa->lms[c].weights[0] = 0; -@@ -517,7 +513,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) - #endif - - int frame_len = QOA_FRAME_LEN; -- for (int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) { -+ for (unsigned int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) { - frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index); - const short *frame_samples = sample_data + sample_index * qoa->channels; - unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p); -@@ -580,14 +576,14 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa - - /* Read and verify the frame header */ - qoa_uint64_t frame_header = qoa_read_u64(bytes, &p); -- int channels = (frame_header >> 56) & 0x0000ff; -- int samplerate = (frame_header >> 32) & 0xffffff; -- int samples = (frame_header >> 16) & 0x00ffff; -- int frame_size = (frame_header ) & 0x00ffff; -+ unsigned int channels = (frame_header >> 56) & 0x0000ff; -+ unsigned int samplerate = (frame_header >> 32) & 0xffffff; -+ unsigned int samples = (frame_header >> 16) & 0x00ffff; -+ unsigned int frame_size = (frame_header ) & 0x00ffff; - - int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels; - int num_slices = data_size / 8; -- int max_total_samples = num_slices * QOA_SLICE_LEN; -+ unsigned int max_total_samples = num_slices * QOA_SLICE_LEN; - - if ( - channels != qoa->channels || -@@ -600,7 +596,7 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa - - - /* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */ -- for (int c = 0; c < channels; c++) { -+ for (unsigned int c = 0; c < channels; c++) { - qoa_uint64_t history = qoa_read_u64(bytes, &p); - qoa_uint64_t weights = qoa_read_u64(bytes, &p); - for (int i = 0; i < QOA_LMS_LEN; i++) { -@@ -613,8 +609,8 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa - - - /* Decode all slices for all channels in this frame */ -- for (int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) { -- for (int c = 0; c < channels; c++) { -+ for (unsigned int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) { -+ for (unsigned int c = 0; c < channels; c++) { - qoa_uint64_t slice = qoa_read_u64(bytes, &p); - - int scalefactor = (slice >> 60) & 0xf; -@@ -647,7 +643,7 @@ short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) { +@@ -655,7 +655,7 @@ short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) { /* Calculate the required size of the sample buffer and allocate */ int total_samples = qoa->samples * qoa->channels; diff --git a/thirdparty/misc/qoa.h b/thirdparty/misc/qoa.h index 2dde8df098..c890b88bd6 100644 --- a/thirdparty/misc/qoa.h +++ b/thirdparty/misc/qoa.h @@ -391,6 +391,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned 16 scalefactors, encode all samples for the current slice and meassure the total squared error. */ qoa_uint64_t best_rank = -1; + #ifdef QOA_RECORD_TOTAL_ERROR + qoa_uint64_t best_error = -1; + #endif qoa_uint64_t best_slice = -1; qoa_lms_t best_lms = {{-1, -1, -1, -1}, {-1, -1, -1, -1}}; int best_scalefactor = -1; @@ -407,6 +410,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned qoa_lms_t lms = qoa->lms[c]; qoa_uint64_t slice = scalefactor; qoa_uint64_t current_rank = 0; + #ifdef QOA_RECORD_TOTAL_ERROR + qoa_uint64_t current_error = 0; + #endif for (int si = slice_start; si < slice_end; si += channels) { int sample = sample_data[si]; @@ -436,6 +442,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned qoa_uint64_t error_sq = error * error; current_rank += error_sq + weights_penalty * weights_penalty; + #ifdef QOA_RECORD_TOTAL_ERROR + current_error += error_sq; + #endif if (current_rank > best_rank) { break; } @@ -446,6 +455,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned if (current_rank < best_rank) { best_rank = current_rank; + #ifdef QOA_RECORD_TOTAL_ERROR + best_error = current_error; + #endif best_slice = slice; best_lms = lms; best_scalefactor = scalefactor; @@ -581,8 +593,8 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa unsigned int samples = (frame_header >> 16) & 0x00ffff; unsigned int frame_size = (frame_header ) & 0x00ffff; - int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels; - int num_slices = data_size / 8; + unsigned int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels; + unsigned int num_slices = data_size / 8; unsigned int max_total_samples = num_slices * QOA_SLICE_LEN; if ( |