diff options
128 files changed, 2272 insertions, 1442 deletions
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index d4b50facb2..63962a310b 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -355,7 +355,7 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); StringName parent_class_name = *reinterpret_cast<const StringName *>(p_parent_class_name); - ERR_FAIL_COND_MSG(!String(class_name).is_valid_ascii_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier."); + ERR_FAIL_COND_MSG(!String(class_name).is_valid_unicode_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier."); ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered."); Extension *parent_extension = nullptr; diff --git a/core/extension/gdextension_compat_hashes.cpp b/core/extension/gdextension_compat_hashes.cpp index ebbf795070..b07f5b1858 100644 --- a/core/extension/gdextension_compat_hashes.cpp +++ b/core/extension/gdextension_compat_hashes.cpp @@ -103,6 +103,14 @@ void GDExtensionCompatHashes::initialize() { mappings.insert("AcceptDialog", { { "add_button", 4158837846, 3328440682 }, }); + mappings.insert("AnimatedSprite2D", { + { "play", 2372066587, 3269405555 }, + { "play_backwards", 1421762485, 3323268493 }, + }); + mappings.insert("AnimatedSprite3D", { + { "play", 2372066587, 3269405555 }, + { "play_backwards", 1421762485, 3323268493 }, + }); mappings.insert("Animation", { { "add_track", 2393815928, 3843682357 }, { "track_insert_key", 1985425300, 808952278 }, @@ -146,6 +154,12 @@ void GDExtensionCompatHashes::initialize() { { "travel", 3683006648, 3823612587 }, { "start", 3683006648, 3823612587 }, }); + mappings.insert("AnimationPlayer", { + { "play", 3697947785, 3118260607 }, + { "play", 2221377757, 3118260607 }, + { "play_backwards", 3890664824, 2787282401 }, + { "play_with_capture", 3180464118, 1572969103 }, + }); mappings.insert("ArrayMesh", { { "add_surface_from_arrays", 172284304, 1796411378 }, }); @@ -247,13 +261,20 @@ void GDExtensionCompatHashes::initialize() { }); mappings.insert("DisplayServer", { { "global_menu_add_submenu_item", 3806306913, 2828985934 }, - { "global_menu_add_item", 3415468211, 3401266716 }, - { "global_menu_add_check_item", 3415468211, 3401266716 }, - { "global_menu_add_icon_item", 1700867534, 4245856523 }, - { "global_menu_add_icon_check_item", 1700867534, 4245856523 }, - { "global_menu_add_radio_check_item", 3415468211, 3401266716 }, - { "global_menu_add_icon_radio_check_item", 1700867534, 4245856523 }, - { "global_menu_add_multistate_item", 635750054, 3431222859 }, + { "global_menu_add_item", 3415468211, 3616842746 }, + { "global_menu_add_item", 3401266716, 3616842746 }, + { "global_menu_add_check_item", 3415468211, 3616842746 }, + { "global_menu_add_check_item", 3401266716, 3616842746 }, + { "global_menu_add_icon_item", 1700867534, 3867083847 }, + { "global_menu_add_icon_item", 4245856523, 3867083847 }, + { "global_menu_add_icon_check_item", 1700867534, 3867083847 }, + { "global_menu_add_icon_check_item", 4245856523, 3867083847 }, + { "global_menu_add_radio_check_item", 3415468211, 3616842746 }, + { "global_menu_add_radio_check_item", 3401266716, 3616842746 }, + { "global_menu_add_icon_radio_check_item", 1700867534, 3867083847 }, + { "global_menu_add_icon_radio_check_item", 4245856523, 3867083847 }, + { "global_menu_add_multistate_item", 635750054, 3297554655 }, + { "global_menu_add_multistate_item", 3431222859, 3297554655 }, { "global_menu_add_separator", 1041533178, 3214812433 }, { "tts_speak", 3741216677, 903992738 }, { "is_touchscreen_available", 4162880507, 3323674545 }, @@ -286,6 +307,12 @@ void GDExtensionCompatHashes::initialize() { { "virtual_keyboard_show", 860410478, 3042891259 }, #endif }); + mappings.insert("EditorExportPlatform", { + { "export_project_files", 425454869, 1063735070 }, + }); + mappings.insert("EditorProperty", { + { "emit_changed", 3069422438, 1822500399 }, + }); mappings.insert("ENetConnection", { { "create_host_bound", 866250949, 1515002313 }, { "connect_to_host", 385984708, 2171300490 }, @@ -453,18 +480,35 @@ void GDExtensionCompatHashes::initialize() { mappings.insert("MultiplayerAPI", { { "rpc", 1833408346, 2077486355 }, }); + mappings.insert("NativeMenu", { + { "add_item", 2553375659, 980552939 }, + { "add_check_item", 2553375659, 980552939 }, + { "add_icon_item", 2987595282, 1372188274 }, + { "add_icon_check_item", 2987595282, 1372188274 }, + { "add_radio_check_item", 2553375659, 980552939 }, + { "add_icon_radio_check_item", 2987595282, 1372188274 }, + { "add_multistate_item", 1558592568, 2674635658 }, + }); mappings.insert("NavigationMeshGenerator", { - { "parse_source_geometry_data", 3703028813, 685862123 }, - { "bake_from_source_geometry_data", 3669016597, 2469318639 }, + { "parse_source_geometry_data", 3703028813, 3172802542 }, + { "parse_source_geometry_data", 685862123, 3172802542 }, + { "bake_from_source_geometry_data", 3669016597, 1286748856 }, + { "bake_from_source_geometry_data", 2469318639, 1286748856 }, }); mappings.insert("NavigationServer2D", { { "map_get_path", 56240621, 3146466012 }, + { "parse_source_geometry_data", 1176164995, 1766905497 }, + { "bake_from_source_geometry_data", 2909414286, 2179660022 }, + { "bake_from_source_geometry_data_async", 2909414286, 2179660022 }, }); mappings.insert("NavigationServer3D", { { "map_get_path", 2121045993, 1187418690 }, - { "parse_source_geometry_data", 3703028813, 685862123 }, - { "bake_from_source_geometry_data", 3669016597, 2469318639 }, - { "bake_from_source_geometry_data_async", 3669016597, 2469318639 }, + { "parse_source_geometry_data", 3703028813, 3172802542 }, + { "parse_source_geometry_data", 685862123, 3172802542 }, + { "bake_from_source_geometry_data", 3669016597, 1286748856 }, + { "bake_from_source_geometry_data", 2469318639, 1286748856 }, + { "bake_from_source_geometry_data_async", 3669016597, 1286748856 }, + { "bake_from_source_geometry_data_async", 2469318639, 1286748856 }, }); mappings.insert("Node", { { "add_child", 3070154285, 3863233950 }, @@ -631,6 +675,11 @@ void GDExtensionCompatHashes::initialize() { mappings.insert("ProjectSettings", { { "load_resource_pack", 3001721055, 708980503 }, }); + mappings.insert("RDShaderFile", { + { "bake_from_source_geometry_data_async", 2469318639, 1286748856 }, + { "set_bytecode", 1558064255, 1526857008 }, + { "get_spirv", 3340165340, 2689310080 }, + }); mappings.insert("RegEx", { { "search", 4087180739, 3365977994 }, { "search_all", 3354100289, 849021363 }, diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 41a8a569d0..b4826c356e 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -845,27 +845,29 @@ Error ResourceLoaderBinary::load() { } } - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = res->get(name, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + if (ClassDB::has_property(res->get_class_name(), name)) { + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = res->get(name, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + } } } - } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = res->get(name, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = res->get(name, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + } } } } diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 636c2c16bf..0692ece1e6 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -30,12 +30,7 @@ #include "expression.h" -#include "core/io/marshalls.h" -#include "core/math/math_funcs.h" #include "core/object/class_db.h" -#include "core/object/ref_counted.h" -#include "core/os/os.h" -#include "core/variant/variant_parser.h" Error Expression::_get_token(Token &r_token) { while (true) { @@ -392,7 +387,6 @@ Error Expression::_get_token(Token &r_token) { if (is_digit(c)) { } else if (c == 'e') { reading = READING_EXP; - } else { reading = READING_DONE; } @@ -419,7 +413,9 @@ Error Expression::_get_token(Token &r_token) { is_first_char = false; } - str_ofs--; + if (c != 0) { + str_ofs--; + } r_token.type = TK_CONSTANT; diff --git a/core/object/object.cpp b/core/object/object.cpp index d6b7d7a7fe..000d5328b4 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "object.h" -#include "object.compat.inc" #include "core/extension/gdextension_manager.h" #include "core/io/resource.h" diff --git a/core/object/object.h b/core/object/object.h index 19e6fc5d47..6d22f320af 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -693,11 +693,7 @@ protected: virtual void _notificationv(int p_notification, bool p_reversed) {} static void _bind_methods(); -#ifndef DISABLE_DEPRECATED - static void _bind_compatibility_methods(); -#else static void _bind_compatibility_methods() {} -#endif bool _set(const StringName &p_name, const Variant &p_property) { return false; }; bool _get(const StringName &p_name, Variant &r_property) const { return false; }; void _get_property_list(List<PropertyInfo> *p_list) const {}; diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 0294dbfbbc..dff19b3a41 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -162,6 +162,11 @@ void StringName::unref() { _data = nullptr; } +uint32_t StringName::get_empty_hash() { + static uint32_t empty_hash = String::hash(""); + return empty_hash; +} + bool StringName::operator==(const String &p_name) const { if (_data) { return _data->operator==(p_name); diff --git a/core/string/string_name.h b/core/string/string_name.h index 288e2c7520..d4b70d311d 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -83,6 +83,7 @@ class StringName { static inline Mutex mutex; static void setup(); static void cleanup(); + static uint32_t get_empty_hash(); static inline bool configured = false; #ifdef DEBUG_ENABLED struct DebugSortReferences { @@ -139,7 +140,7 @@ public: if (_data) { return _data->hash; } else { - return 0; + return get_empty_hash(); } } _FORCE_INLINE_ const void *data_unique_pointer() const { diff --git a/core/string/translation.compat.inc b/core/string/translation.compat.inc deleted file mode 100644 index 68bd1831e4..0000000000 --- a/core/string/translation.compat.inc +++ /dev/null @@ -1,41 +0,0 @@ -/**************************************************************************/ -/* translation.compat.inc */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef DISABLE_DEPRECATED - -void Translation::_bind_compatibility_methods() { - ClassDB::bind_compatibility_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL("")); -} - -#endif diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 33d4a1bcde..020949371f 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "translation.h" -#include "translation.compat.inc" #include "core/os/os.h" #include "core/os/thread.h" diff --git a/core/string/translation.h b/core/string/translation.h index 2c5baae8b7..4e8cffc90c 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -51,10 +51,6 @@ class Translation : public Resource { protected: static void _bind_methods(); -#ifndef DISABLE_DEPRECATED - static void _bind_compatibility_methods(); -#endif - GDVIRTUAL2RC(StringName, _get_message, StringName, StringName); GDVIRTUAL4RC(StringName, _get_plural_message, StringName, StringName, int, StringName); diff --git a/core/string/translation_server.compat.inc b/core/string/translation_server.compat.inc deleted file mode 100644 index 9d1ee8b9df..0000000000 --- a/core/string/translation_server.compat.inc +++ /dev/null @@ -1,38 +0,0 @@ -/**************************************************************************/ -/* translation_server.compat.inc */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef DISABLE_DEPRECATED - -void TranslationServer::_bind_compatibility_methods() { - ClassDB::bind_compatibility_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL("")); -} - -#endif diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp index 4ac79ad10a..d4aa152340 100644 --- a/core/string/translation_server.cpp +++ b/core/string/translation_server.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "translation_server.h" -#include "translation_server.compat.inc" #include "core/config/project_settings.h" #include "core/io/resource_loader.h" diff --git a/core/string/translation_server.h b/core/string/translation_server.h index ebe81d9712..bb285ab19c 100644 --- a/core/string/translation_server.h +++ b/core/string/translation_server.h @@ -74,10 +74,6 @@ class TranslationServer : public Object { static void _bind_methods(); -#ifndef DISABLE_DEPRECATED - static void _bind_compatibility_methods(); -#endif - struct LocaleScriptInfo { String name; String script; diff --git a/doc/classes/EditorContextMenuPlugin.xml b/doc/classes/EditorContextMenuPlugin.xml index 7eeee3d7fd..71c4ca0f9b 100644 --- a/doc/classes/EditorContextMenuPlugin.xml +++ b/doc/classes/EditorContextMenuPlugin.xml @@ -14,7 +14,7 @@ <return type="void" /> <param index="0" name="paths" type="PackedStringArray" /> <description> - Called when creating a context menu, custom options can be added by using the [method add_context_menu_item] function. + Called when creating a context menu, custom options can be added by using the [method add_context_menu_item] or [method add_context_menu_item_from_shortcut] functions. [param paths] contains currently selected paths (depending on menu), which can be used to conditionally add options. </description> </method> <method name="add_context_menu_item"> @@ -22,14 +22,29 @@ <param index="0" name="name" type="String" /> <param index="1" name="callback" type="Callable" /> <param index="2" name="icon" type="Texture2D" default="null" /> - <param index="3" name="shortcut" type="Shortcut" default="null" /> <description> - Add custom options to the context menu of the currently specified slot. - To trigger a [param shortcut] before the context menu is created, please additionally call the [method add_menu_shortcut] function. + Add custom option to the context menu of the plugin's specified slot. When the option is activated, [param callback] will be called. Callback should take single [Array] argument; array contents depend on context menu slot. [codeblock] func _popup_menu(paths): add_context_menu_item("File Custom options", handle, ICON) [/codeblock] + If you want to assign shortcut to the menu item, use [method add_context_menu_item_from_shortcut] instead. + </description> + </method> + <method name="add_context_menu_item_from_shortcut"> + <return type="void" /> + <param index="0" name="name" type="String" /> + <param index="1" name="shortcut" type="Shortcut" /> + <param index="2" name="icon" type="Texture2D" default="null" /> + <description> + Add custom option to the context menu of the plugin's specified slot. The option will have the [param shortcut] assigned and reuse its callback. The shortcut has to be registered beforehand with [method add_menu_shortcut]. + [codeblock] + func _init(): + add_menu_shortcut(SHORTCUT, handle) + + func _popup_menu(paths): + add_context_menu_item_from_shortcut("File Custom options", SHORTCUT, ICON) + [/codeblock] </description> </method> <method name="add_menu_shortcut"> @@ -37,13 +52,26 @@ <param index="0" name="shortcut" type="Shortcut" /> <param index="1" name="callback" type="Callable" /> <description> - To register the shortcut for the context menu, call this function within the [method Object._init] function, even if the context menu has not been created yet. - Note that this method should only be invoked from [method Object._init]; otherwise, the shortcut will not be registered correctly. + Registers a shortcut associated with the plugin's context menu. This method should be called once (e.g. in plugin's [method Object._init]). [param callback] will be called when user presses the specified [param shortcut] while the menu's context is in effect (e.g. FileSystem dock is focused). Callback should take single [Array] argument; array contents depend on context menu slot. [codeblock] func _init(): - add_menu_shortcut(SHORTCUT, handle); + add_menu_shortcut(SHORTCUT, handle) [/codeblock] </description> </method> </methods> + <constants> + <constant name="CONTEXT_SLOT_SCENE_TREE" value="0" enum="ContextMenuSlot"> + Context menu of Scene dock. [method _popup_menu] will be called with a list of paths to currently selected nodes, while option callback will receive the list of currently selected nodes. + </constant> + <constant name="CONTEXT_SLOT_FILESYSTEM" value="1" enum="ContextMenuSlot"> + Context menu of FileSystem dock. [method _popup_menu] and option callback will be called with list of paths of the currently selected files. + </constant> + <constant name="CONTEXT_SLOT_FILESYSTEM_CREATE" value="3" enum="ContextMenuSlot"> + The "Create..." submenu of FileSystem dock's context menu. [method _popup_menu] and option callback will be called with list of paths of the currently selected files. + </constant> + <constant name="CONTEXT_SLOT_SCRIPT_EDITOR" value="2" enum="ContextMenuSlot"> + Context menu of Scene dock. [method _popup_menu] will be called with the path to the currently edited script, while option callback will receive reference to that script. + </constant> + </constants> </class> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index b3191e5378..de49764f0d 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -403,11 +403,11 @@ </method> <method name="add_context_menu_plugin"> <return type="void" /> - <param index="0" name="slot" type="int" enum="EditorPlugin.ContextMenuSlot" /> + <param index="0" name="slot" type="int" enum="EditorContextMenuPlugin.ContextMenuSlot" /> <param index="1" name="plugin" type="EditorContextMenuPlugin" /> <description> - Adds a plugin to the context menu. [param slot] is the position in the context menu where the plugin will be added. - Context menus are supported for three commonly used areas: the file system, scene tree, and editor script list panel. + Adds a plugin to the context menu. [param slot] is the context menu where the plugin will be added. + See [enum EditorContextMenuPlugin.ContextMenuSlot] for available context menus. A plugin instance can belong only to a single context menu slot. </description> </method> <method name="add_control_to_bottom_panel"> @@ -635,10 +635,9 @@ </method> <method name="remove_context_menu_plugin"> <return type="void" /> - <param index="0" name="slot" type="int" enum="EditorPlugin.ContextMenuSlot" /> - <param index="1" name="plugin" type="EditorContextMenuPlugin" /> + <param index="0" name="plugin" type="EditorContextMenuPlugin" /> <description> - Removes a context menu plugin from the specified slot. + Removes the specified context menu plugin. </description> </method> <method name="remove_control_from_bottom_panel"> @@ -891,17 +890,5 @@ <constant name="AFTER_GUI_INPUT_CUSTOM" value="2" enum="AfterGUIInput"> Pass the [InputEvent] to other editor plugins except the main [Node3D] one. This can be used to prevent node selection changes and work with sub-gizmos instead. </constant> - <constant name="CONTEXT_SLOT_SCENE_TREE" value="0" enum="ContextMenuSlot"> - Context menu slot for the SceneTree. - </constant> - <constant name="CONTEXT_SLOT_FILESYSTEM" value="1" enum="ContextMenuSlot"> - Context menu slot for the FileSystem. - </constant> - <constant name="CONTEXT_SLOT_SCRIPT_EDITOR" value="2" enum="ContextMenuSlot"> - Context menu slot for the ScriptEditor file list. - </constant> - <constant name="CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE" value="3" enum="ContextMenuSlot"> - Context menu slot for the FileSystem create submenu. - </constant> </constants> </class> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index f938460c2f..a37dd47914 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -4,7 +4,14 @@ An input field for single-line text. </brief_description> <description> - [LineEdit] provides an input field for editing a single line of text. It features many built-in shortcuts that are always available ([kbd]Ctrl[/kbd] here maps to [kbd]Cmd[/kbd] on macOS): + [LineEdit] provides an input field for editing a single line of text. + - When the [LineEdit] control is focused using the keyboard arrow keys, it will only gain focus and not enter edit mode. + - To enter edit mode, click on the control with the mouse or press the "ui_text_submit" action (default: [kbd]Enter[/kbd] or [kbd]Kp Enter[/kbd]). + - To exit edit mode, press "ui_text_submit" or "ui_cancel" (default: [kbd]Escape[/kbd]) actions. + - Check [method is_editing] and [signal editing_toggled] for more information. + [b]Important:[/b] + - Focusing the [LineEdit] with "ui_focus_next" (default: [kbd]Tab[/kbd]) or "ui_focus_prev" (default: [kbd]Shift + Tab[/kbd]) or [method Control.grab_focus] still enters edit mode (for compatibility). + [LineEdit] features many built-in shortcuts that are always available ([kbd]Ctrl[/kbd] here maps to [kbd]Cmd[/kbd] on macOS): - [kbd]Ctrl + C[/kbd]: Copy - [kbd]Ctrl + X[/kbd]: Cut - [kbd]Ctrl + V[/kbd] or [kbd]Ctrl + Y[/kbd]: Paste/"yank" @@ -139,6 +146,12 @@ Inserts [param text] at the caret. If the resulting value is longer than [member max_length], nothing happens. </description> </method> + <method name="is_editing" qualifiers="const"> + <return type="bool" /> + <description> + Returns whether the [LineEdit] is being edited. + </description> + </method> <method name="is_menu_visible" qualifiers="const"> <return type="bool" /> <description> @@ -301,6 +314,12 @@ </member> </members> <signals> + <signal name="editing_toggled"> + <param index="0" name="toggled_on" type="bool" /> + <description> + Emitted when the [LineEdit] switches in or out of edit mode. + </description> + </signal> <signal name="text_change_rejected"> <param index="0" name="rejected_substring" type="String" /> <description> diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml index a0d03d7a01..7e78006240 100644 --- a/doc/classes/NavigationServer2D.xml +++ b/doc/classes/NavigationServer2D.xml @@ -486,7 +486,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector2" /> <description> - Returns the point closest to the provided [param to_point] on the navigation mesh surface. + Returns the navigation mesh surface point closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_closest_point_owner" qualifiers="const"> @@ -494,7 +494,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector2" /> <description> - Returns the owner region RID for the point returned by [method map_get_closest_point]. + Returns the owner region RID for the navigation mesh surface point closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_edge_connection_margin" qualifiers="const"> @@ -768,6 +768,14 @@ Creates a new region. </description> </method> + <method name="region_get_closest_point" qualifiers="const"> + <return type="Vector2" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="to_point" type="Vector2" /> + <description> + Returns the navigation mesh surface point closest to the provided [param to_point] on the navigation [param region]. + </description> + </method> <method name="region_get_connection_pathway_end" qualifiers="const"> <return type="Vector2" /> <param index="0" name="region" type="RID" /> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 42f6235f8b..7e206046d6 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -532,7 +532,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector3" /> <description> - Returns the point closest to the provided [param to_point] on the navigation mesh surface. + Returns the navigation mesh surface point closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_closest_point_normal" qualifiers="const"> @@ -540,7 +540,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector3" /> <description> - Returns the normal for the point returned by [method map_get_closest_point]. + Returns the navigation mesh surface normal closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_closest_point_owner" qualifiers="const"> @@ -548,7 +548,7 @@ <param index="0" name="map" type="RID" /> <param index="1" name="to_point" type="Vector3" /> <description> - Returns the owner region RID for the point returned by [method map_get_closest_point]. + Returns the owner region RID for the navigation mesh surface point closest to the provided [param to_point] on the navigation [param map]. </description> </method> <method name="map_get_closest_point_to_segment" qualifiers="const"> @@ -558,7 +558,8 @@ <param index="2" name="end" type="Vector3" /> <param index="3" name="use_collision" type="bool" default="false" /> <description> - Returns the closest point between the navigation surface and the segment. + Returns the navigation mesh surface point closest to the provided [param start] and [param end] segment on the navigation [param map]. + If [param use_collision] is [code]true[/code], a closest point test is only done when the segment intersects with the navigation mesh surface. </description> </method> <method name="map_get_edge_connection_margin" qualifiers="const"> @@ -908,6 +909,33 @@ Creates a new region. </description> </method> + <method name="region_get_closest_point" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="to_point" type="Vector3" /> + <description> + Returns the navigation mesh surface point closest to the provided [param to_point] on the navigation [param region]. + </description> + </method> + <method name="region_get_closest_point_normal" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="to_point" type="Vector3" /> + <description> + Returns the navigation mesh surface normal closest to the provided [param to_point] on the navigation [param region]. + </description> + </method> + <method name="region_get_closest_point_to_segment" qualifiers="const"> + <return type="Vector3" /> + <param index="0" name="region" type="RID" /> + <param index="1" name="start" type="Vector3" /> + <param index="2" name="end" type="Vector3" /> + <param index="3" name="use_collision" type="bool" default="false" /> + <description> + Returns the navigation mesh surface point closest to the provided [param start] and [param end] segment on the navigation [param region]. + If [param use_collision] is [code]true[/code], a closest point test is only done when the segment intersects with the navigation mesh surface. + </description> + </method> <method name="region_get_connection_pathway_end" qualifiers="const"> <return type="Vector3" /> <param index="0" name="region" type="RID" /> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 497070fa81..08427ffe83 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2947,6 +2947,9 @@ <member name="xr/openxr/enabled" type="bool" setter="" getter="" default="false"> If [code]true[/code], Godot will setup and initialize OpenXR on startup. </member> + <member name="xr/openxr/enabled.editor" type="bool" setter="" getter="" default="false"> + If [code]true[/code], Godot will setup and initialize OpenXR on editor startup. + </member> <member name="xr/openxr/environment_blend_mode" type="int" setter="" getter="" default=""0""> Specify how OpenXR should blend in the environment. This is specific to certain AR and passthrough devices where camera images are blended in by the XR compositor. </member> diff --git a/doc/classes/ResourceImporterScene.xml b/doc/classes/ResourceImporterScene.xml index 900e028b25..1565a244fe 100644 --- a/doc/classes/ResourceImporterScene.xml +++ b/doc/classes/ResourceImporterScene.xml @@ -68,6 +68,9 @@ <member name="nodes/root_type" type="String" setter="" getter="" default=""""> Override for the root node type. If empty, the root node will use what the scene specifies, or [Node3D] if the scene does not specify a root type. Using a node type that inherits from [Node3D] is recommended. Otherwise, you'll lose the ability to position the node directly in the 3D editor. </member> + <member name="nodes/use_node_type_suffixes" type="bool" setter="" getter="" default="true"> + If [code]true[/code], use suffixes in the node names to determine the node type, such as [code]-col[/code] for collision shapes. Disabling this makes editor-imported files more similar to the original files, and more similar to importing files at runtime. See [url=$DOCS_URL/tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.html]Node type customization using name suffixes[/url] for more information. + </member> <member name="skins/use_named_skins" type="bool" setter="" getter="" default="true"> If checked, use named [Skin]s for animation. The [MeshInstance3D] node contains 3 properties of relevance here: a skeleton [NodePath] pointing to the [Skeleton3D] node (usually [code]..[/code]), a mesh, and a skin: - The [Skeleton3D] node contains a list of bones with names, their pose and rest, a name and a parent bone. diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml index eb3c170583..479456ae66 100644 --- a/doc/classes/Theme.xml +++ b/doc/classes/Theme.xml @@ -551,7 +551,7 @@ </member> <member name="default_font_size" type="int" setter="set_default_font_size" getter="get_default_font_size" default="-1"> The default font size of this theme resource. Used as the default value when trying to fetch a font size value that doesn't exist in this theme or is in invalid state. If the default font size is also missing or invalid, the engine fallback value is used (see [member ThemeDB.fallback_font_size]). - Values below [code]0[/code] are invalid and can be used to unset the property. Use [method has_default_font_size] to check if this value is valid. + Values below [code]1[/code] are invalid and can be used to unset the property. Use [method has_default_font_size] to check if this value is valid. </member> </members> <constants> diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index a37eba3b15..7d5af48384 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -348,7 +348,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_ } } -_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) { +_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) { switch (type) { case ShaderLanguage::TYPE_BOOL: { uint32_t *gui = (uint32_t *)data; @@ -572,7 +572,7 @@ void ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_t Variant ShaderData::get_default_parameter(const StringName &p_parameter) const { if (uniforms.has(p_parameter)) { ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + Vector<ShaderLanguage::Scalar> default_value = uniform.default_value; return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); } return Variant(); diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index de82d74aff..5058554659 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -1769,14 +1769,14 @@ AABB MeshStorage::_multimesh_get_custom_aabb(RID p_multimesh) const { return multimesh->custom_aabb; } -AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); if (multimesh->custom_aabb != AABB()) { return multimesh->custom_aabb; } if (multimesh->aabb_dirty) { - const_cast<MeshStorage *>(this)->_update_dirty_multimeshes(); + _update_dirty_multimeshes(); } return multimesh->aabb; } diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index a2edbb9c48..31858cd372 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -510,7 +510,7 @@ public: virtual RID _multimesh_get_mesh(RID p_multimesh) const override; virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override; - virtual AABB _multimesh_get_aabb(RID p_multimesh) const override; + virtual AABB _multimesh_get_aabb(RID p_multimesh) override; virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 0243d863f8..9d6aa13332 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -118,11 +118,12 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { return ERR_INVALID_PARAMETER; } - struct _stat st; - if (_wstat((LPCWSTR)(path.utf16().get_data()), &st) == 0) { - if (!S_ISREG(st.st_mode)) { - return ERR_FILE_CANT_OPEN; - } + if (path.ends_with(":\\") || path.ends_with(":")) { + return ERR_FILE_CANT_OPEN; + } + DWORD file_attr = GetFileAttributesW((LPCWSTR)(path.utf16().get_data())); + if (file_attr != INVALID_FILE_ATTRIBUTES && (file_attr & FILE_ATTRIBUTE_DIRECTORY)) { + return ERR_FILE_CANT_OPEN; } #ifdef TOOLS_ENABLED @@ -412,15 +413,40 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { file = file.substr(0, file.length() - 1); } - struct _stat st; - int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st); + HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); - if (rv == 0) { - return st.st_mtime; - } else { - print_verbose("Failed to get modified time for: " + p_file + ""); - return 0; + if (handle != INVALID_HANDLE_VALUE) { + FILETIME ft_create, ft_write; + + bool status = GetFileTime(handle, &ft_create, nullptr, &ft_write); + + CloseHandle(handle); + + if (status) { + uint64_t ret = 0; + + // If write time is invalid, fallback to creation time. + if (ft_write.dwHighDateTime == 0 && ft_write.dwLowDateTime == 0) { + ret = ft_create.dwHighDateTime; + ret <<= 32; + ret |= ft_create.dwLowDateTime; + } else { + ret = ft_write.dwHighDateTime; + ret <<= 32; + ret |= ft_write.dwLowDateTime; + } + + const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000; + const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL; + + if (ret >= TICKS_TO_UNIX_EPOCH) { + return (ret - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND; + } + } } + + print_verbose("Failed to get modified time for: " + p_file); + return 0; } BitField<FileAccess::UnixPermissionFlags> FileAccessWindows::_get_unix_permissions(const String &p_file) { diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 5273b61f37..ce2c1a5a16 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -34,6 +34,7 @@ #include "core/templates/hash_set.h" #include "editor/editor_help.h" #include "editor/editor_inspector.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -1191,7 +1192,7 @@ void ConnectionsDock::_go_to_method(TreeItem &p_item) { } if (scr.is_valid() && ScriptEditor::get_singleton()->script_goto_method(scr, cd.method)) { - EditorNode::get_singleton()->editor_select(EditorNode::EDITOR_SCRIPT); + EditorNode::get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } } @@ -1199,7 +1200,7 @@ void ConnectionsDock::_handle_class_menu_option(int p_option) { switch (p_option) { case CLASS_MENU_OPEN_DOCS: ScriptEditor::get_singleton()->goto_help("class:" + class_menu_doc_class_name); - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); break; } } @@ -1229,7 +1230,7 @@ void ConnectionsDock::_handle_signal_menu_option(int p_option) { } break; case SIGNAL_MENU_OPEN_DOCS: { ScriptEditor::get_singleton()->goto_help("class_signal:" + String(meta["class"]) + ":" + String(meta["name"])); - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } break; } } diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index e5caa6a352..ee16c61c89 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -522,138 +522,6 @@ EditorPlugin *EditorData::get_extension_editor_plugin(const StringName &p_class_ return plugin == nullptr ? nullptr : *plugin; } -void EditorData::add_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) { - ContextMenu cm; - cm.p_slot = p_slot; - cm.plugin = p_plugin; - p_plugin->start_idx = context_menu_plugins.size() * EditorContextMenuPlugin::MAX_ITEMS; - context_menu_plugins.push_back(cm); -} - -void EditorData::remove_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) { - for (int i = context_menu_plugins.size() - 1; i > -1; i--) { - if (context_menu_plugins[i].p_slot == p_slot && context_menu_plugins[i].plugin == p_plugin) { - context_menu_plugins.remove_at(i); - } - } -} - -int EditorData::match_context_menu_shortcut(ContextMenuSlot p_slot, const Ref<InputEvent> &p_event) { - for (ContextMenu &cm : context_menu_plugins) { - if (cm.p_slot != p_slot) { - continue; - } - HashMap<Ref<Shortcut>, Callable> &cms = cm.plugin->context_menu_shortcuts; - int shortcut_idx = 0; - for (KeyValue<Ref<Shortcut>, Callable> &E : cms) { - const Ref<Shortcut> &p_shortcut = E.key; - if (p_shortcut->matches_event(p_event)) { - return EditorData::CONTEXT_MENU_ITEM_ID_BASE + cm.plugin->start_idx + shortcut_idx; - } - shortcut_idx++; - } - } - return 0; -} - -void EditorData::add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector<String> &p_paths) { - bool add_separator = false; - - for (ContextMenu &cm : context_menu_plugins) { - if (cm.p_slot != p_slot) { - continue; - } - cm.plugin->clear_context_menu_items(); - cm.plugin->add_options(p_paths); - HashMap<String, EditorContextMenuPlugin::ContextMenuItem> &items = cm.plugin->context_menu_items; - if (items.size() > 0 && !add_separator) { - add_separator = true; - p_popup->add_separator(); - } - for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &E : items) { - EditorContextMenuPlugin::ContextMenuItem &item = E.value; - - if (item.icon.is_valid()) { - p_popup->add_icon_item(item.icon, item.item_name, item.idx); - const int icon_size = p_popup->get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); - p_popup->set_item_icon_max_width(-1, icon_size); - } else { - p_popup->add_item(item.item_name, item.idx); - } - if (item.shortcut.is_valid()) { - p_popup->set_item_shortcut(-1, item.shortcut, true); - } - } - } -} - -template <typename T> -void EditorData::invoke_plugin_callback(ContextMenuSlot p_slot, int p_option, const T &p_arg) { - Variant arg = p_arg; - Variant *argptr = &arg; - - for (int i = 0; i < context_menu_plugins.size(); i++) { - if (context_menu_plugins[i].p_slot != p_slot || context_menu_plugins[i].plugin.is_null()) { - continue; - } - Ref<EditorContextMenuPlugin> plugin = context_menu_plugins[i].plugin; - - // Shortcut callback. - int shortcut_idx = 0; - int shortcut_base_idx = EditorData::CONTEXT_MENU_ITEM_ID_BASE + plugin->start_idx; - for (KeyValue<Ref<Shortcut>, Callable> &E : plugin->context_menu_shortcuts) { - if (shortcut_base_idx + shortcut_idx == p_option) { - const Callable &callable = E.value; - Callable::CallError ce; - Variant result; - callable.callp((const Variant **)&argptr, 1, result, ce); - } - shortcut_idx++; - } - if (p_option < shortcut_base_idx + shortcut_idx) { - return; - } - - HashMap<String, EditorContextMenuPlugin::ContextMenuItem> &items = plugin->context_menu_items; - for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &E : items) { - EditorContextMenuPlugin::ContextMenuItem &item = E.value; - - if (p_option != item.idx || !item.callable.is_valid()) { - continue; - } - - Callable::CallError ce; - Variant result; - item.callable.callp((const Variant **)&argptr, 1, result, ce); - - if (ce.error != Callable::CallError::CALL_OK) { - String err = Variant::get_callable_error_text(item.callable, nullptr, 0, ce); - ERR_PRINT("Error calling function from context menu: " + err); - } - } - } - // Invoke submenu items. - if (p_slot == CONTEXT_SLOT_FILESYSTEM) { - invoke_plugin_callback(CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE, p_option, p_arg); - } -} - -void EditorData::filesystem_options_pressed(ContextMenuSlot p_slot, int p_option, const Vector<String> &p_selected) { - invoke_plugin_callback(p_slot, p_option, p_selected); -} - -void EditorData::scene_tree_options_pressed(ContextMenuSlot p_slot, int p_option, const List<Node *> &p_selected) { - TypedArray<Node> nodes; - for (Node *selected : p_selected) { - nodes.append(selected); - } - invoke_plugin_callback(p_slot, p_option, nodes); -} - -void EditorData::script_editor_options_pressed(ContextMenuSlot p_slot, int p_option, const Ref<Resource> &p_script) { - invoke_plugin_callback(p_slot, p_option, p_script); -} - void EditorData::add_custom_type(const String &p_type, const String &p_inherits, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon) { ERR_FAIL_COND_MSG(p_script.is_null(), "It's not a reference to a valid Script object."); CustomType ct; diff --git a/editor/editor_data.h b/editor/editor_data.h index 754d44c479..5b49304c73 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -37,7 +37,6 @@ class ConfigFile; class EditorPlugin; class EditorUndoRedoManager; -class EditorContextMenuPlugin; class PopupMenu; /** @@ -125,22 +124,6 @@ public: uint64_t last_checked_version = 0; }; - enum ContextMenuSlot { - CONTEXT_SLOT_SCENE_TREE, - CONTEXT_SLOT_FILESYSTEM, - CONTEXT_SLOT_SCRIPT_EDITOR, - CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE, - }; - - inline static constexpr int CONTEXT_MENU_ITEM_ID_BASE = 1000; - - struct ContextMenu { - int p_slot; - Ref<EditorContextMenuPlugin> plugin; - }; - - Vector<ContextMenu> context_menu_plugins; - private: Vector<EditorPlugin *> editor_plugins; HashMap<StringName, EditorPlugin *> extension_editor_plugins; @@ -195,18 +178,6 @@ public: bool has_extension_editor_plugin(const StringName &p_class_name); EditorPlugin *get_extension_editor_plugin(const StringName &p_class_name); - // Context menu plugin. - void add_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin); - void remove_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin); - int match_context_menu_shortcut(ContextMenuSlot p_slot, const Ref<InputEvent> &p_event); - - void add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector<String> &p_paths); - void filesystem_options_pressed(ContextMenuSlot p_slot, int p_option, const Vector<String> &p_selected); - void scene_tree_options_pressed(ContextMenuSlot p_slot, int p_option, const List<Node *> &p_selected); - void script_editor_options_pressed(ContextMenuSlot p_slot, int p_option, const Ref<Resource> &p_script); - template <typename T> - void invoke_plugin_callback(ContextMenuSlot p_slot, int p_option, const T &p_arg); - void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks should have this signature: void (Object* undo_redo, Object *modified_object, String property, Variant new_value) void remove_undo_redo_inspector_hook_callback(Callable p_callable); const Vector<Callable> get_undo_redo_inspector_hook_callback(); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index fe758bf99b..f5f7b8f51c 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -39,6 +39,7 @@ #include "core/string/string_builder.h" #include "core/version_generated.gen.h" #include "editor/doc_data_compressed.gen.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_property_name_processor.h" @@ -2310,7 +2311,7 @@ void EditorHelp::_update_doc() { void EditorHelp::_request_help(const String &p_string) { Error err = _goto_desc(p_string); if (err == OK) { - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } } @@ -3605,7 +3606,7 @@ void EditorHelpBit::_update_labels() { } void EditorHelpBit::_go_to_help(const String &p_what) { - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); ScriptEditor::get_singleton()->goto_help(p_what); emit_signal(SNAME("request_hide")); } diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index 987c7f9c31..11be765bc2 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -32,6 +32,7 @@ #include "core/os/keyboard.h" #include "editor/editor_feature_profile.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -201,7 +202,7 @@ void EditorHelpSearch::_confirmed() { } // Activate the script editor and emit the signal with the documentation link to display. - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); emit_signal(SNAME("go_to_help"), item->get_metadata(0)); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index a1cae374aa..4a2c41bc9c 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -34,6 +34,7 @@ #include "core/os/keyboard.h" #include "editor/doc_tools.h" #include "editor/editor_feature_profile.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_property_name_processor.h" #include "editor/editor_settings.h" @@ -1037,7 +1038,7 @@ void EditorProperty::menu_option(int p_option) { } break; case MENU_OPEN_DOCUMENTATION: { ScriptEditor::get_singleton()->goto_help(doc_path); - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } break; } } @@ -1297,7 +1298,7 @@ void EditorInspectorCategory::_handle_menu_option(int p_option) { switch (p_option) { case MENU_OPEN_DOCS: ScriptEditor::get_singleton()->goto_help("class:" + doc_class_name); - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); break; } } diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp index 86b66ef410..fa6198f695 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -33,6 +33,7 @@ #include "editor/editor_command_palette.h" #include "editor/editor_feature_profile.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_resource_preview.h" @@ -215,7 +216,7 @@ Control *EditorInterface::get_base_control() const { } VBoxContainer *EditorInterface::get_editor_main_screen() const { - return EditorNode::get_singleton()->get_main_screen_control(); + return EditorNode::get_singleton()->get_editor_main_screen()->get_control(); } ScriptEditor *EditorInterface::get_script_editor() const { @@ -232,7 +233,7 @@ SubViewport *EditorInterface::get_editor_viewport_3d(int p_idx) const { } void EditorInterface::set_main_screen_editor(const String &p_name) { - EditorNode::get_singleton()->select_editor_by_name(p_name); + EditorNode::get_singleton()->get_editor_main_screen()->select_by_name(p_name); } void EditorInterface::set_distraction_free_mode(bool p_enter) { diff --git a/editor/editor_main_screen.cpp b/editor/editor_main_screen.cpp new file mode 100644 index 0000000000..77bbee5a7f --- /dev/null +++ b/editor/editor_main_screen.cpp @@ -0,0 +1,291 @@ +/**************************************************************************/ +/* editor_main_screen.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "editor_main_screen.h" + +#include "core/io/config_file.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "editor/editor_string_names.h" +#include "editor/plugins/editor_plugin.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" + +void EditorMainScreen::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + if (EDITOR_3D < buttons.size() && buttons[EDITOR_3D]->is_visible()) { + // If the 3D editor is enabled, use this as the default. + select(EDITOR_3D); + return; + } + + // Switch to the first main screen plugin that is enabled. Usually this is + // 2D, but may be subsequent ones if 2D is disabled in the feature profile. + for (int i = 0; i < buttons.size(); i++) { + Button *editor_button = buttons[i]; + if (editor_button->is_visible()) { + select(i); + return; + } + } + + select(-1); + } break; + case NOTIFICATION_THEME_CHANGED: { + for (int i = 0; i < buttons.size(); i++) { + Button *tb = buttons[i]; + EditorPlugin *p_editor = editor_table[i]; + Ref<Texture2D> icon = p_editor->get_icon(); + + if (icon.is_valid()) { + tb->set_icon(icon); + } else if (has_theme_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { + tb->set_icon(get_theme_icon(p_editor->get_name(), EditorStringName(EditorIcons))); + } + } + } break; + } +} + +void EditorMainScreen::set_button_container(HBoxContainer *p_button_hb) { + button_hb = p_button_hb; +} + +void EditorMainScreen::save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const { + int selected_main_editor_idx = -1; + for (int i = 0; i < buttons.size(); i++) { + if (buttons[i]->is_pressed()) { + selected_main_editor_idx = i; + break; + } + } + if (selected_main_editor_idx != -1) { + p_config_file->set_value(p_section, "selected_main_editor_idx", selected_main_editor_idx); + } else { + p_config_file->set_value(p_section, "selected_main_editor_idx", Variant()); + } +} + +void EditorMainScreen::load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section) { + int selected_main_editor_idx = p_config_file->get_value(p_section, "selected_main_editor_idx", -1); + if (selected_main_editor_idx >= 0 && selected_main_editor_idx < buttons.size()) { + callable_mp(this, &EditorMainScreen::select).call_deferred(selected_main_editor_idx); + } +} + +void EditorMainScreen::set_button_enabled(int p_index, bool p_enabled) { + ERR_FAIL_INDEX(p_index, buttons.size()); + buttons[p_index]->set_visible(p_enabled); + if (!p_enabled && buttons[p_index]->is_pressed()) { + select(EDITOR_2D); + } +} + +bool EditorMainScreen::is_button_enabled(int p_index) const { + ERR_FAIL_INDEX_V(p_index, buttons.size(), false); + return buttons[p_index]->is_visible(); +} + +int EditorMainScreen::_get_current_main_editor() const { + for (int i = 0; i < editor_table.size(); i++) { + if (editor_table[i] == selected_plugin) { + return i; + } + } + + return 0; +} + +void EditorMainScreen::select_next() { + int editor = _get_current_main_editor(); + + do { + if (editor == editor_table.size() - 1) { + editor = 0; + } else { + editor++; + } + } while (!buttons[editor]->is_visible()); + + select(editor); +} + +void EditorMainScreen::select_prev() { + int editor = _get_current_main_editor(); + + do { + if (editor == 0) { + editor = editor_table.size() - 1; + } else { + editor--; + } + } while (!buttons[editor]->is_visible()); + + select(editor); +} + +void EditorMainScreen::select_by_name(const String &p_name) { + ERR_FAIL_COND(p_name.is_empty()); + + for (int i = 0; i < buttons.size(); i++) { + if (buttons[i]->get_text() == p_name) { + select(i); + return; + } + } + + ERR_FAIL_MSG("The editor name '" + p_name + "' was not found."); +} + +void EditorMainScreen::select(int p_index) { + if (EditorNode::get_singleton()->is_changing_scene()) { + return; + } + + ERR_FAIL_INDEX(p_index, editor_table.size()); + + if (!buttons[p_index]->is_visible()) { // Button hidden, no editor. + return; + } + + for (int i = 0; i < buttons.size(); i++) { + buttons[i]->set_pressed_no_signal(i == p_index); + } + + EditorPlugin *new_editor = editor_table[p_index]; + ERR_FAIL_NULL(new_editor); + + if (selected_plugin == new_editor) { + return; + } + + if (selected_plugin) { + selected_plugin->make_visible(false); + } + + selected_plugin = new_editor; + selected_plugin->make_visible(true); + selected_plugin->selected_notify(); + + EditorData &editor_data = EditorNode::get_editor_data(); + int plugin_count = editor_data.get_editor_plugin_count(); + for (int i = 0; i < plugin_count; i++) { + editor_data.get_editor_plugin(i)->notify_main_screen_changed(selected_plugin->get_name()); + } + + EditorNode::get_singleton()->update_distraction_free_mode(); +} + +int EditorMainScreen::get_selected_index() const { + for (int i = 0; i < editor_table.size(); i++) { + if (selected_plugin == editor_table[i]) { + return i; + } + } + return -1; +} + +int EditorMainScreen::get_plugin_index(EditorPlugin *p_editor) const { + int screen = -1; + for (int i = 0; i < editor_table.size(); i++) { + if (p_editor == editor_table[i]) { + screen = i; + break; + } + } + return screen; +} + +EditorPlugin *EditorMainScreen::get_selected_plugin() const { + return selected_plugin; +} + +VBoxContainer *EditorMainScreen::get_control() const { + return main_screen_vbox; +} + +void EditorMainScreen::add_main_plugin(EditorPlugin *p_editor) { + Button *tb = memnew(Button); + tb->set_toggle_mode(true); + tb->set_theme_type_variation("MainScreenButton"); + tb->set_name(p_editor->get_name()); + tb->set_text(p_editor->get_name()); + + Ref<Texture2D> icon = p_editor->get_icon(); + if (icon.is_null() && has_theme_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { + icon = get_editor_theme_icon(p_editor->get_name()); + } + if (icon.is_valid()) { + tb->set_icon(icon); + // Make sure the control is updated if the icon is reimported. + icon->connect_changed(callable_mp((Control *)tb, &Control::update_minimum_size)); + } + + tb->connect(SceneStringName(pressed), callable_mp(this, &EditorMainScreen::select).bind(buttons.size())); + + buttons.push_back(tb); + button_hb->add_child(tb); + editor_table.push_back(p_editor); +} + +void EditorMainScreen::remove_main_plugin(EditorPlugin *p_editor) { + // Remove the main editor button and update the bindings of + // all buttons behind it to point to the correct main window. + for (int i = buttons.size() - 1; i >= 0; i--) { + if (p_editor->get_name() == buttons[i]->get_text()) { + if (buttons[i]->is_pressed()) { + select(EDITOR_SCRIPT); + } + + memdelete(buttons[i]); + buttons.remove_at(i); + + break; + } else { + buttons[i]->disconnect(SceneStringName(pressed), callable_mp(this, &EditorMainScreen::select)); + buttons[i]->connect(SceneStringName(pressed), callable_mp(this, &EditorMainScreen::select).bind(i - 1)); + } + } + + if (selected_plugin == p_editor) { + selected_plugin = nullptr; + } + + editor_table.erase(p_editor); +} + +EditorMainScreen::EditorMainScreen() { + main_screen_vbox = memnew(VBoxContainer); + main_screen_vbox->set_name("MainScreen"); + main_screen_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL); + main_screen_vbox->add_theme_constant_override("separation", 0); + add_child(main_screen_vbox); +} diff --git a/core/object/object.compat.inc b/editor/editor_main_screen.h index bf1e99fc9b..153a182bc2 100644 --- a/core/object/object.compat.inc +++ b/editor/editor_main_screen.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* object.compat.inc */ +/* editor_main_screen.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,64 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef DISABLE_DEPRECATED +#ifndef EDITOR_MAIN_SCREEN_H +#define EDITOR_MAIN_SCREEN_H -#include "core/object/class_db.h" +#include "scene/gui/panel_container.h" -void Object::_bind_compatibility_methods() { - ClassDB::bind_compatibility_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL("")); -} +class Button; +class ConfigFile; +class EditorPlugin; +class HBoxContainer; +class VBoxContainer; -#endif +class EditorMainScreen : public PanelContainer { + GDCLASS(EditorMainScreen, PanelContainer); + +public: + enum EditorTable { + EDITOR_2D = 0, + EDITOR_3D, + EDITOR_SCRIPT, + EDITOR_ASSETLIB, + }; + +private: + VBoxContainer *main_screen_vbox = nullptr; + EditorPlugin *selected_plugin = nullptr; + + HBoxContainer *button_hb = nullptr; + Vector<Button *> buttons; + Vector<EditorPlugin *> editor_table; + + int _get_current_main_editor() const; + +protected: + void _notification(int p_what); + +public: + void set_button_container(HBoxContainer *p_button_hb); + + void save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const; + void load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section); + + void set_button_enabled(int p_index, bool p_enabled); + bool is_button_enabled(int p_index) const; + + void select_next(); + void select_prev(); + void select_by_name(const String &p_name); + void select(int p_index); + int get_selected_index() const; + int get_plugin_index(EditorPlugin *p_editor) const; + EditorPlugin *get_selected_plugin() const; + + VBoxContainer *get_control() const; + + void add_main_plugin(EditorPlugin *p_editor); + void remove_main_plugin(EditorPlugin *p_editor); + + EditorMainScreen(); +}; + +#endif // EDITOR_MAIN_SCREEN_H diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 39291138a6..363d07008a 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -45,6 +45,7 @@ #include "core/string/translation_server.h" #include "core/version.h" #include "editor/editor_string_names.h" +#include "editor/plugins/editor_context_menu_plugin.h" #include "main/main.h" #include "scene/3d/bone_attachment_3d.h" #include "scene/animation/animation_tree.h" @@ -88,6 +89,7 @@ #include "editor/editor_interface.h" #include "editor/editor_layouts_dialog.h" #include "editor/editor_log.h" +#include "editor/editor_main_screen.h" #include "editor/editor_native_shader_source_visualizer.h" #include "editor/editor_paths.h" #include "editor/editor_properties.h" @@ -348,19 +350,19 @@ void EditorNode::shortcut_input(const Ref<InputEvent> &p_event) { if (ED_IS_SHORTCUT("editor/filter_files", p_event)) { FileSystemDock::get_singleton()->focus_on_filter(); } else if (ED_IS_SHORTCUT("editor/editor_2d", p_event)) { - editor_select(EDITOR_2D); + editor_main_screen->select(EditorMainScreen::EDITOR_2D); } else if (ED_IS_SHORTCUT("editor/editor_3d", p_event)) { - editor_select(EDITOR_3D); + editor_main_screen->select(EditorMainScreen::EDITOR_3D); } else if (ED_IS_SHORTCUT("editor/editor_script", p_event)) { - editor_select(EDITOR_SCRIPT); + editor_main_screen->select(EditorMainScreen::EDITOR_SCRIPT); } else if (ED_IS_SHORTCUT("editor/editor_help", p_event)) { emit_signal(SNAME("request_help_search"), ""); } else if (ED_IS_SHORTCUT("editor/editor_assetlib", p_event) && AssetLibraryEditorPlugin::is_available()) { - editor_select(EDITOR_ASSETLIB); + editor_main_screen->select(EditorMainScreen::EDITOR_ASSETLIB); } else if (ED_IS_SHORTCUT("editor/editor_next", p_event)) { - _editor_select_next(); + editor_main_screen->select_next(); } else if (ED_IS_SHORTCUT("editor/editor_prev", p_event)) { - _editor_select_prev(); + editor_main_screen->select_prev(); } else if (ED_IS_SHORTCUT("editor/command_palette", p_event)) { _open_command_palette(); } else if (ED_IS_SHORTCUT("editor/toggle_last_opened_bottom_panel", p_event)) { @@ -489,26 +491,6 @@ void EditorNode::_gdextensions_reloaded() { EditorHelp::generate_doc(); } -void EditorNode::_select_default_main_screen_plugin() { - if (EDITOR_3D < main_editor_buttons.size() && main_editor_buttons[EDITOR_3D]->is_visible()) { - // If the 3D editor is enabled, use this as the default. - editor_select(EDITOR_3D); - return; - } - - // Switch to the first main screen plugin that is enabled. Usually this is - // 2D, but may be subsequent ones if 2D is disabled in the feature profile. - for (int i = 0; i < main_editor_buttons.size(); i++) { - Button *editor_button = main_editor_buttons[i]; - if (editor_button->is_visible()) { - editor_select(i); - return; - } - } - - editor_select(-1); -} - void EditorNode::_update_theme(bool p_skip_creation) { if (!p_skip_creation) { theme = EditorThemeManager::generate_theme(theme); @@ -546,7 +528,7 @@ void EditorNode::_update_theme(bool p_skip_creation) { main_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, theme->get_constant(SNAME("window_border_margin"), EditorStringName(Editor))); main_vbox->add_theme_constant_override("separation", theme->get_constant(SNAME("top_bar_separation"), EditorStringName(Editor))); - scene_root_parent->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles))); + editor_main_screen->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles))); bottom_panel->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles))); distraction_free->set_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons))); distraction_free->add_theme_style_override(SceneStringName(pressed), theme->get_stylebox(CoreStringName(normal), "FlatMenuButton")); @@ -560,18 +542,6 @@ void EditorNode::_update_theme(bool p_skip_creation) { bottom_panel->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))); } - for (int i = 0; i < main_editor_buttons.size(); i++) { - Button *tb = main_editor_buttons[i]; - EditorPlugin *p_editor = editor_table[i]; - Ref<Texture2D> icon = p_editor->get_icon(); - - if (icon.is_valid()) { - tb->set_icon(icon); - } else if (theme->has_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { - tb->set_icon(theme->get_icon(p_editor->get_name(), EditorStringName(EditorIcons))); - } - } - _update_renderer_color(); } @@ -748,8 +718,6 @@ void EditorNode::_notification(int p_what) { feature_profile_manager->notify_changed(); - _select_default_main_screen_plugin(); - // Save the project after opening to mark it as last modified, except in headless mode. if (DisplayServer::get_singleton()->window_can_draw()) { ProjectSettings::get_singleton()->save(); @@ -1293,38 +1261,10 @@ void EditorNode::_node_renamed() { } } -void EditorNode::_editor_select_next() { - int editor = _get_current_main_editor(); - - do { - if (editor == editor_table.size() - 1) { - editor = 0; - } else { - editor++; - } - } while (!main_editor_buttons[editor]->is_visible()); - - editor_select(editor); -} - void EditorNode::_open_command_palette() { command_palette->open_popup(); } -void EditorNode::_editor_select_prev() { - int editor = _get_current_main_editor(); - - do { - if (editor == 0) { - editor = editor_table.size() - 1; - } else { - editor--; - } - } while (!main_editor_buttons[editor]->is_visible()); - - editor_select(editor); -} - Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_deps) { dependency_errors.clear(); @@ -2645,16 +2585,11 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update if (!inspector_only) { EditorPlugin *main_plugin = editor_data.get_handling_main_editor(current_obj); - int plugin_index = 0; - for (; plugin_index < editor_table.size(); plugin_index++) { - if (editor_table[plugin_index] == main_plugin) { - if (!main_editor_buttons[plugin_index]->is_visible()) { - main_plugin = nullptr; // If button is not visible, then no plugin is active. - } - - break; - } + int plugin_index = editor_main_screen->get_plugin_index(main_plugin); + if (main_plugin && plugin_index >= 0 && !editor_main_screen->is_button_enabled(plugin_index)) { + main_plugin = nullptr; } + EditorPlugin *editor_plugin_screen = editor_main_screen->get_selected_plugin(); ObjectID editor_owner_id = editor_owner->get_instance_id(); if (main_plugin && !skip_main_plugin) { @@ -2671,7 +2606,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update editor_plugin_screen->edit(nullptr); active_plugins[editor_owner_id].erase(editor_plugin_screen); // Update screen main_plugin. - editor_select(plugin_index); + editor_main_screen->select(plugin_index); main_plugin->edit(current_obj); } else { editor_plugin_screen->edit(current_obj); @@ -3294,9 +3229,9 @@ void EditorNode::_screenshot(bool p_use_utc) { } void EditorNode::_save_screenshot(NodePath p_path) { - Control *editor_main_screen = EditorInterface::get_singleton()->get_editor_main_screen(); - ERR_FAIL_NULL_MSG(editor_main_screen, "Cannot get the editor main screen control."); - Viewport *viewport = editor_main_screen->get_viewport(); + Control *main_screen_control = editor_main_screen->get_control(); + ERR_FAIL_NULL_MSG(main_screen_control, "Cannot get the editor main screen control."); + Viewport *viewport = main_screen_control->get_viewport(); ERR_FAIL_NULL_MSG(viewport, "Cannot get a viewport from the editor main screen."); Ref<ViewportTexture> texture = viewport->get_texture(); ERR_FAIL_COND_MSG(texture.is_null(), "Cannot get a viewport texture from the editor main screen."); @@ -3501,97 +3436,9 @@ void EditorNode::_update_file_menu_closed() { file_menu->set_item_disabled(file_menu->get_item_index(FILE_OPEN_PREV), false); } -VBoxContainer *EditorNode::get_main_screen_control() { - return main_screen_vbox; -} - -void EditorNode::editor_select(int p_which) { - static bool selecting = false; - if (selecting || changing_scene) { - return; - } - - ERR_FAIL_INDEX(p_which, editor_table.size()); - - if (!main_editor_buttons[p_which]->is_visible()) { // Button hidden, no editor. - return; - } - - selecting = true; - - for (int i = 0; i < main_editor_buttons.size(); i++) { - main_editor_buttons[i]->set_pressed(i == p_which); - } - - selecting = false; - - EditorPlugin *new_editor = editor_table[p_which]; - ERR_FAIL_NULL(new_editor); - - if (editor_plugin_screen == new_editor) { - return; - } - - if (editor_plugin_screen) { - editor_plugin_screen->make_visible(false); - } - - editor_plugin_screen = new_editor; - editor_plugin_screen->make_visible(true); - editor_plugin_screen->selected_notify(); - - int plugin_count = editor_data.get_editor_plugin_count(); - for (int i = 0; i < plugin_count; i++) { - editor_data.get_editor_plugin(i)->notify_main_screen_changed(editor_plugin_screen->get_name()); - } - - if (EDITOR_GET("interface/editor/separate_distraction_mode")) { - if (p_which == EDITOR_SCRIPT) { - set_distraction_free_mode(script_distraction_free); - } else { - set_distraction_free_mode(scene_distraction_free); - } - } -} - -void EditorNode::select_editor_by_name(const String &p_name) { - ERR_FAIL_COND(p_name.is_empty()); - - for (int i = 0; i < main_editor_buttons.size(); i++) { - if (main_editor_buttons[i]->get_text() == p_name) { - editor_select(i); - return; - } - } - - ERR_FAIL_MSG("The editor name '" + p_name + "' was not found."); -} - void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed) { if (p_editor->has_main_screen()) { - Button *tb = memnew(Button); - tb->set_toggle_mode(true); - tb->set_theme_type_variation("MainScreenButton"); - tb->set_name(p_editor->get_name()); - tb->set_text(p_editor->get_name()); - - Ref<Texture2D> icon = p_editor->get_icon(); - if (icon.is_null() && singleton->theme->has_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { - icon = singleton->theme->get_icon(p_editor->get_name(), EditorStringName(EditorIcons)); - } - if (icon.is_valid()) { - tb->set_icon(icon); - // Make sure the control is updated if the icon is reimported. - icon->connect_changed(callable_mp((Control *)tb, &Control::update_minimum_size)); - } - - tb->connect(SceneStringName(pressed), callable_mp(singleton, &EditorNode::editor_select).bind(singleton->main_editor_buttons.size())); - - singleton->main_editor_buttons.push_back(tb); - singleton->main_editor_button_hb->add_child(tb); - singleton->editor_table.push_back(p_editor); - - singleton->distraction_free->move_to_front(); + singleton->editor_main_screen->add_main_plugin(p_editor); } singleton->editor_data.add_editor_plugin(p_editor); singleton->add_child(p_editor); @@ -3602,29 +3449,7 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed void EditorNode::remove_editor_plugin(EditorPlugin *p_editor, bool p_config_changed) { if (p_editor->has_main_screen()) { - // Remove the main editor button and update the bindings of - // all buttons behind it to point to the correct main window. - for (int i = singleton->main_editor_buttons.size() - 1; i >= 0; i--) { - if (p_editor->get_name() == singleton->main_editor_buttons[i]->get_text()) { - if (singleton->main_editor_buttons[i]->is_pressed()) { - singleton->editor_select(EDITOR_SCRIPT); - } - - memdelete(singleton->main_editor_buttons[i]); - singleton->main_editor_buttons.remove_at(i); - - break; - } else { - singleton->main_editor_buttons[i]->disconnect(SceneStringName(pressed), callable_mp(singleton, &EditorNode::editor_select)); - singleton->main_editor_buttons[i]->connect(SceneStringName(pressed), callable_mp(singleton, &EditorNode::editor_select).bind(i - 1)); - } - } - - if (singleton->editor_plugin_screen == p_editor) { - singleton->editor_plugin_screen = nullptr; - } - - singleton->editor_table.erase(p_editor); + singleton->editor_main_screen->remove_main_plugin(p_editor); } p_editor->make_visible(false); p_editor->clear(); @@ -3848,19 +3673,8 @@ void EditorNode::set_edited_scene_root(Node *p_scene, bool p_auto_add) { } } -int EditorNode::_get_current_main_editor() { - for (int i = 0; i < editor_table.size(); i++) { - if (editor_table[i] == editor_plugin_screen) { - return i; - } - } - - return 0; -} - Dictionary EditorNode::_get_main_scene_state() { Dictionary state; - state["main_tab"] = _get_current_main_editor(); state["scene_tree_offset"] = SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value(); state["property_edit_offset"] = InspectorDock::get_inspector_singleton()->get_scroll_offset(); state["node_filter"] = SceneTreeDock::get_singleton()->get_filter(); @@ -3874,32 +3688,19 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { changing_scene = false; - int current_tab = -1; - for (int i = 0; i < editor_table.size(); i++) { - if (editor_plugin_screen == editor_table[i]) { - current_tab = i; - break; - } - } - - if (p_state.has("editor_index")) { - int index = p_state["editor_index"]; - if (current_tab < 2) { // If currently in spatial/2d, only switch to spatial/2d. If currently in script, stay there. - if (index < 2 || !get_edited_scene()) { - editor_select(index); - } - } - } - if (get_edited_scene()) { + int current_tab = editor_main_screen->get_selected_index(); if (current_tab < 2) { - Node *editor_node = SceneTreeDock::get_singleton()->get_tree_editor()->get_selected(); - editor_node = editor_node == nullptr ? get_edited_scene() : editor_node; + // Switch between 2D and 3D if currently in 2D or 3D. + Node *selected_node = SceneTreeDock::get_singleton()->get_tree_editor()->get_selected(); + if (!selected_node) { + selected_node = get_edited_scene(); + } - if (Object::cast_to<CanvasItem>(editor_node)) { - editor_select(EDITOR_2D); - } else if (Object::cast_to<Node3D>(editor_node)) { - editor_select(EDITOR_3D); + if (Object::cast_to<CanvasItem>(selected_node)) { + editor_main_screen->select(EditorMainScreen::EDITOR_2D); + } else if (Object::cast_to<Node3D>(selected_node)) { + editor_main_screen->select(EditorMainScreen::EDITOR_3D); } } } @@ -5357,18 +5158,7 @@ void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_ // Main editor (plugin). - int selected_main_editor_idx = -1; - for (int i = 0; i < main_editor_buttons.size(); i++) { - if (main_editor_buttons[i]->is_pressed()) { - selected_main_editor_idx = i; - break; - } - } - if (selected_main_editor_idx != -1) { - p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_main_editor_idx", selected_main_editor_idx); - } else { - p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_main_editor_idx", Variant()); - } + editor_main_screen->save_layout_to_config(p_config_file, EDITOR_NODE_CONFIG_SECTION); } void EditorNode::_load_central_editor_layout_from_config(Ref<ConfigFile> p_config_file) { @@ -5390,12 +5180,7 @@ void EditorNode::_load_central_editor_layout_from_config(Ref<ConfigFile> p_confi // Main editor (plugin). - if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_main_editor_idx")) { - int selected_main_editor_idx = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "selected_main_editor_idx"); - if (selected_main_editor_idx >= 0 && selected_main_editor_idx < main_editor_buttons.size()) { - callable_mp(this, &EditorNode::editor_select).call_deferred(selected_main_editor_idx); - } - } + editor_main_screen->load_layout_from_config(p_config_file, EDITOR_NODE_CONFIG_SECTION); } void EditorNode::_save_window_settings_to_config(Ref<ConfigFile> p_layout, const String &p_section) { @@ -5716,15 +5501,9 @@ void EditorNode::_cancel_close_scene_tab() { void EditorNode::_toggle_distraction_free_mode() { if (EDITOR_GET("interface/editor/separate_distraction_mode")) { - int screen = -1; - for (int i = 0; i < editor_table.size(); i++) { - if (editor_plugin_screen == editor_table[i]) { - screen = i; - break; - } - } + int screen = editor_main_screen->get_selected_index(); - if (screen == EDITOR_SCRIPT) { + if (screen == EditorMainScreen::EDITOR_SCRIPT) { script_distraction_free = !script_distraction_free; set_distraction_free_mode(script_distraction_free); } else { @@ -5736,6 +5515,18 @@ void EditorNode::_toggle_distraction_free_mode() { } } +void EditorNode::update_distraction_free_mode() { + if (!EDITOR_GET("interface/editor/separate_distraction_mode")) { + return; + } + int screen = editor_main_screen->get_selected_index(); + if (screen == EditorMainScreen::EDITOR_SCRIPT) { + set_distraction_free_mode(script_distraction_free); + } else { + set_distraction_free_mode(scene_distraction_free); + } +} + void EditorNode::set_distraction_free_mode(bool p_enter) { distraction_free->set_pressed(p_enter); @@ -6625,25 +6416,20 @@ void EditorNode::_feature_profile_changed() { editor_dock_manager->set_dock_enabled(ImportDock::get_singleton(), !fs_dock_disabled && !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_IMPORT_DOCK)); editor_dock_manager->set_dock_enabled(history_dock, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_HISTORY_DOCK)); - main_editor_buttons[EDITOR_3D]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)); - main_editor_buttons[EDITOR_SCRIPT]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT)); + editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)); + editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT)); if (AssetLibraryEditorPlugin::is_available()) { - main_editor_buttons[EDITOR_ASSETLIB]->set_visible(!profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB)); - } - if ((profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D) && singleton->main_editor_buttons[EDITOR_3D]->is_pressed()) || - (profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT) && singleton->main_editor_buttons[EDITOR_SCRIPT]->is_pressed()) || - (AssetLibraryEditorPlugin::is_available() && profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB) && singleton->main_editor_buttons[EDITOR_ASSETLIB]->is_pressed())) { - editor_select(EDITOR_2D); + editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB)); } } else { editor_dock_manager->set_dock_enabled(ImportDock::get_singleton(), true); editor_dock_manager->set_dock_enabled(NodeDock::get_singleton(), true); editor_dock_manager->set_dock_enabled(FileSystemDock::get_singleton(), true); editor_dock_manager->set_dock_enabled(history_dock, true); - main_editor_buttons[EDITOR_3D]->set_visible(true); - main_editor_buttons[EDITOR_SCRIPT]->set_visible(true); + editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, true); + editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, true); if (AssetLibraryEditorPlugin::is_available()) { - main_editor_buttons[EDITOR_ASSETLIB]->set_visible(true); + editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, true); } } } @@ -6999,6 +6785,8 @@ EditorNode::EditorNode() { EditorFileSystem *efs = memnew(EditorFileSystem); add_child(efs); + EditorContextMenuPluginManager::create(); + // Used for previews. FileDialog::get_icon_func = _file_dialog_get_icon; FileDialog::register_func = _file_dialog_register; @@ -7178,12 +6966,11 @@ EditorNode::EditorNode() { scene_tabs->add_extra_button(distraction_free); distraction_free->connect(SceneStringName(pressed), callable_mp(this, &EditorNode::_toggle_distraction_free_mode)); - scene_root_parent = memnew(PanelContainer); - scene_root_parent->set_custom_minimum_size(Size2(0, 80) * EDSCALE); - scene_root_parent->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles))); - scene_root_parent->set_draw_behind_parent(true); - srt->add_child(scene_root_parent); - scene_root_parent->set_v_size_flags(Control::SIZE_EXPAND_FILL); + editor_main_screen = memnew(EditorMainScreen); + editor_main_screen->set_custom_minimum_size(Size2(0, 80) * EDSCALE); + editor_main_screen->set_draw_behind_parent(true); + srt->add_child(editor_main_screen); + editor_main_screen->set_v_size_flags(Control::SIZE_EXPAND_FILL); scene_root = memnew(SubViewport); scene_root->set_embedding_subwindows(true); @@ -7191,12 +6978,6 @@ EditorNode::EditorNode() { scene_root->set_disable_input(true); scene_root->set_as_audio_listener_2d(true); - main_screen_vbox = memnew(VBoxContainer); - main_screen_vbox->set_name("MainScreen"); - main_screen_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL); - main_screen_vbox->add_theme_constant_override("separation", 0); - scene_root_parent->add_child(main_screen_vbox); - bool global_menu = !bool(EDITOR_GET("interface/editor/use_embedded_menu")) && NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU); bool can_expand = bool(EDITOR_GET("interface/editor/expand_to_title")) && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EXTEND_TO_TITLE); @@ -7390,8 +7171,9 @@ EditorNode::EditorNode() { left_spacer->add_child(project_title); } - main_editor_button_hb = memnew(HBoxContainer); + HBoxContainer *main_editor_button_hb = memnew(HBoxContainer); main_editor_button_hb->set_mouse_filter(Control::MOUSE_FILTER_STOP); + editor_main_screen->set_button_container(main_editor_button_hb); title_bar->add_child(main_editor_button_hb); // Options are added and handled by DebuggerEditorPlugin. @@ -7864,7 +7646,6 @@ EditorNode::EditorNode() { update_spinner_step_msec = OS::get_singleton()->get_ticks_msec(); update_spinner_step_frame = Engine::get_singleton()->get_frames_drawn(); - editor_plugin_screen = nullptr; editor_plugins_over = memnew(EditorPluginList); editor_plugins_force_over = memnew(EditorPluginList); editor_plugins_force_input_forwarding = memnew(EditorPluginList); @@ -8006,6 +7787,7 @@ EditorNode::~EditorNode() { EditorInspector::cleanup_plugins(); EditorTranslationParser::get_singleton()->clean_parsers(); ResourceImporterScene::clean_up_importer_plugins(); + EditorContextMenuPluginManager::cleanup(); remove_print_handler(&print_handler); EditorHelp::cleanup_doc(); diff --git a/editor/editor_node.h b/editor/editor_node.h index f1b50cf3fa..63d7d58d50 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -42,27 +42,17 @@ typedef void (*EditorPluginInitializeCallback)(); typedef bool (*EditorBuildCallback)(); class AcceptDialog; -class CenterContainer; -class CheckBox; class ColorPicker; class ConfirmationDialog; class Control; class FileDialog; -class HBoxContainer; -class HSplitContainer; -class LinkButton; class MenuBar; class MenuButton; -class Node2D; class OptionButton; class Panel; class PanelContainer; -class PopupPanel; class RichTextLabel; class SubViewport; -class TabBar; -class TabContainer; -class TextureRect; class TextureProgressBar; class Tree; class VBoxContainer; @@ -83,44 +73,33 @@ class EditorCommandPalette; class EditorDockManager; class EditorExport; class EditorExportPreset; -class EditorExtensionManager; class EditorFeatureProfileManager; class EditorFileDialog; class EditorFolding; -class EditorInspector; class EditorLayoutsDialog; class EditorLog; +class EditorMainScreen; class EditorNativeShaderSourceVisualizer; class EditorPluginList; class EditorQuickOpen; -class EditorPropertyResource; class EditorResourcePreview; class EditorResourceConversionPlugin; class EditorRunBar; -class EditorRunNative; class EditorSceneTabs; class EditorSelectionHistory; class EditorSettingsDialog; class EditorTitleBar; -class EditorToaster; -class EditorUndoRedoManager; class ExportTemplateManager; class FBXImporterManager; class FileSystemDock; class HistoryDock; -class ImportDock; -class NodeDock; class OrphanResourcesDialog; -class PluginConfigDialog; class ProgressDialog; class ProjectExportDialog; class ProjectSettingsEditor; -class RunSettingsDialog; class SceneImportSettingsDialog; -class ScriptCreateDialog; class SurfaceUpgradeTool; class SurfaceUpgradeDialog; -class WindowWrapper; struct EditorProgress { String task; @@ -135,13 +114,6 @@ class EditorNode : public Node { GDCLASS(EditorNode, Node); public: - enum EditorTable { - EDITOR_2D = 0, - EDITOR_3D, - EDITOR_SCRIPT, - EDITOR_ASSETLIB - }; - enum SceneNameCasing { SCENE_NAME_CASING_AUTO, SCENE_NAME_CASING_PASCAL_CASE, @@ -286,7 +258,6 @@ private: EditorExport *editor_export = nullptr; EditorLog *log = nullptr; EditorNativeShaderSourceVisualizer *native_shader_source_visualizer = nullptr; - EditorPlugin *editor_plugin_screen = nullptr; EditorPluginList *editor_plugins_force_input_forwarding = nullptr; EditorPluginList *editor_plugins_force_over = nullptr; EditorPluginList *editor_plugins_over = nullptr; @@ -308,7 +279,6 @@ private: HashMap<ObjectID, HashSet<EditorPlugin *>> active_plugins; bool is_main_screen_editing = false; - PanelContainer *scene_root_parent = nullptr; Control *gui_base = nullptr; VBoxContainer *main_vbox = nullptr; OptionButton *renderer = nullptr; @@ -349,7 +319,6 @@ private: Control *right_menu_spacer = nullptr; EditorTitleBar *title_bar = nullptr; EditorRunBar *project_run_bar = nullptr; - VBoxContainer *main_screen_vbox = nullptr; MenuBar *main_menu = nullptr; PopupMenu *apple_menu = nullptr; PopupMenu *file_menu = nullptr; @@ -423,9 +392,7 @@ private: String current_path; MenuButton *update_spinner = nullptr; - HBoxContainer *main_editor_button_hb = nullptr; - Vector<Button *> main_editor_buttons; - Vector<EditorPlugin *> editor_table; + EditorMainScreen *editor_main_screen = nullptr; AudioStreamPreviewGenerator *audio_preview_gen = nullptr; ProgressDialog *progress_dialog = nullptr; @@ -575,8 +542,6 @@ private: void _sources_changed(bool p_exist); void _node_renamed(); - void _editor_select_next(); - void _editor_select_prev(); void _save_editor_states(const String &p_file, int p_idx = -1); void _load_editor_plugin_states_from_config(const Ref<ConfigFile> &p_config_file); void _update_title(); @@ -649,8 +614,6 @@ private: Dictionary _get_main_scene_state(); void _set_main_scene_state(Dictionary p_state, Node *p_for_scene); - int _get_current_main_editor(); - void _save_editor_layout(); void _load_editor_layout(); @@ -688,7 +651,6 @@ private: void _pick_main_scene_custom_action(const String &p_custom_action_name); void _immediate_dialog_confirmed(); - void _select_default_main_screen_plugin(); void _begin_first_scan(); @@ -707,9 +669,6 @@ public: void init_plugins(); void _on_plugin_ready(Object *p_script, const String &p_activate_name); - void editor_select(int p_which); - void set_visible_editor(EditorTable p_table) { editor_select(p_table); } - bool call_build(); // This is a very naive estimation, but we need something now. Will be reworked later. @@ -724,6 +683,7 @@ public: static EditorTitleBar *get_title_bar() { return singleton->title_bar; } static VSplitContainer *get_top_split() { return singleton->top_split; } static EditorBottomPanel *get_bottom_panel() { return singleton->bottom_panel; } + static EditorMainScreen *get_editor_main_screen() { return singleton->editor_main_screen; } static String adjust_scene_name_casing(const String &p_root_name); static String adjust_script_name_casing(const String &p_file_name, ScriptLanguage::ScriptNameCasing p_auto_casing); @@ -755,7 +715,6 @@ public: static void cleanup(); - EditorPlugin *get_editor_plugin_screen() { return editor_plugin_screen; } EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; } EditorPluginList *get_editor_plugins_force_over() { return editor_plugins_force_over; } EditorPluginList *get_editor_plugins_over() { return editor_plugins_over; } @@ -769,6 +728,7 @@ public: void new_inherited_scene() { _menu_option_confirm(FILE_NEW_INHERITED_SCENE, false); } + void update_distraction_free_mode(); void set_distraction_free_mode(bool p_enter); bool is_distraction_free_mode_enabled() const; @@ -791,8 +751,6 @@ public: void push_node_item(Node *p_node); void hide_unused_editors(const Object *p_editing_owner = nullptr); - void select_editor_by_name(const String &p_name); - void open_request(const String &p_path); void edit_foreign_resource(Ref<Resource> p_resource); @@ -802,7 +760,6 @@ public: bool is_changing_scene() const; - VBoxContainer *get_main_screen_control(); SubViewport *get_scene_root() { return scene_root; } // Root of the scene being edited. void set_edited_scene(Node *p_scene); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 123d903220..2e46068e07 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -81,7 +81,6 @@ void EditorPropertyText::_text_submitted(const String &p_string) { } if (text->has_focus()) { - text->release_focus(); _text_changed(p_string); } } @@ -2703,7 +2702,11 @@ void EditorPropertyNodePath::_update_menu() { void EditorPropertyNodePath::_menu_option(int p_idx) { switch (p_idx) { case ACTION_CLEAR: { - emit_changed(get_edited_property(), NodePath()); + if (editing_node) { + emit_changed(get_edited_property(), Variant()); + } else { + emit_changed(get_edited_property(), NodePath()); + } update_property(); } break; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index b1a91fa3dd..d0c89c49c4 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -527,15 +527,18 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Touchscreen bool has_touchscreen_ui = DisplayServer::get_singleton()->is_touchscreen_available(); - EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/increase_scrollbar_touch_area", has_touchscreen_ui, "") - set_restart_if_changed("interface/touchscreen/increase_scrollbar_touch_area", true); - EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_long_press_as_right_click", has_touchscreen_ui, "") - set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true); EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_pan_and_scale_gestures", has_touchscreen_ui, "") set_restart_if_changed("interface/touchscreen/enable_pan_and_scale_gestures", true); EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/touchscreen/scale_gizmo_handles", has_touchscreen_ui ? 3 : 1, "1,5,1") set_restart_if_changed("interface/touchscreen/scale_gizmo_handles", true); + // Disable some touchscreen settings by default for the XR Editor. + bool is_native_touchscreen = has_touchscreen_ui && !OS::get_singleton()->has_feature("xr_editor"); + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_long_press_as_right_click", is_native_touchscreen, "") + set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true); + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/increase_scrollbar_touch_area", is_native_touchscreen, "") + set_restart_if_changed("interface/touchscreen/increase_scrollbar_touch_area", true); + // Scene tabs EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/scene_tabs/display_close_button", 1, "Never,If Tab Active,Always"); // TabBar::CloseButtonDisplayPolicy _initial_set("interface/scene_tabs/show_thumbnail_on_hover", true); diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 7ad589a58d..983f4ee36a 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -270,7 +270,7 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const { Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); ERR_FAIL_COND_V(theme.is_null(), Ref<ImageTexture>()); - if (EditorNode::get_singleton()->get_main_screen_control()->is_layout_rtl()) { + if (EditorNode::get_singleton()->get_gui_base()->is_layout_rtl()) { return theme->get_icon(SNAME("PlayBackwards"), EditorStringName(EditorIcons)); } else { return theme->get_icon(SNAME("Play"), EditorStringName(EditorIcons)); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index f7e81b329c..68d74236bb 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2557,9 +2557,12 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected String dir = ProjectSettings::get_singleton()->globalize_path(fpath); ScriptEditor::get_singleton()->open_text_file_create_dialog(dir); } break; - default: - EditorNode::get_editor_data().filesystem_options_pressed(EditorData::CONTEXT_SLOT_FILESYSTEM, p_option, p_selected); - break; + + default: { + if (!EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_option, p_selected)) { + EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_option, p_selected); + } + } } } @@ -3183,7 +3186,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect new_menu->add_icon_item(get_editor_theme_icon(SNAME("Object")), TTR("Resource..."), FILE_NEW_RESOURCE); new_menu->add_icon_item(get_editor_theme_icon(SNAME("TextFile")), TTR("TextFile..."), FILE_NEW_TEXTFILE); - EditorNode::get_editor_data().add_options_from_plugins(new_menu, EditorData::CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE, p_paths); + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(new_menu, EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_paths); p_popup->add_separator(); } @@ -3323,8 +3326,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect current_path = fpath; } - - EditorNode::get_editor_data().add_options_from_plugins(p_popup, EditorData::CONTEXT_SLOT_FILESYSTEM, p_paths); + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(p_popup, EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_paths); } void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button) { @@ -3559,11 +3561,16 @@ void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) { } else if (ED_IS_SHORTCUT("editor/open_search", p_event)) { focus_on_filter(); } else { - int match_option = EditorNode::get_editor_data().match_context_menu_shortcut(EditorData::CONTEXT_SLOT_FILESYSTEM, p_event); - if (match_option) { - _tree_rmb_option(match_option); + Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_event); + if (!custom_callback.is_valid()) { + custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_event); + } + + if (custom_callback.is_valid()) { + EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, _tree_get_selected(false)); + } else { + return; } - return; } accept_event(); @@ -3631,7 +3638,16 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) { } else if (ED_IS_SHORTCUT("editor/open_search", p_event)) { focus_on_filter(); } else { - return; + Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_event); + if (!custom_callback.is_valid()) { + custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_event); + } + + if (custom_callback.is_valid()) { + EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, files->get_selected_items()); + } else { + return; + } } accept_event(); diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index 5c28213ca7..cb348f713c 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -637,10 +637,10 @@ void _apply_permanent_scale_to_descendants(Node *p_root_node, Vector3 p_scale) { _apply_scale_to_scalable_node_collection(scalable_node_collection, p_scale); } -Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames) { +Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames, const HashMap<StringName, Variant> &p_options) { // Children first. for (int i = 0; i < p_node->get_child_count(); i++) { - Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames); + Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames, p_options); if (!r) { i--; // Was erased. } @@ -750,6 +750,14 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R } } + bool use_node_type_suffixes = true; + if (p_options.has("nodes/use_node_type_suffixes")) { + use_node_type_suffixes = p_options["nodes/use_node_type_suffixes"]; + } + if (!use_node_type_suffixes) { + return p_node; + } + if (_teststr(name, "colonly") || _teststr(name, "convcolonly")) { if (isroot) { return p_node; @@ -2373,6 +2381,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/import_as_skeleton_bones"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/use_node_type_suffixes"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true)); @@ -2854,7 +2863,7 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashM HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map; List<Pair<NodePath, Node *>> node_renames; - _pre_fix_node(scene, scene, collision_map, nullptr, node_renames); + _pre_fix_node(scene, scene, collision_map, nullptr, node_renames, p_options); return scene; } @@ -2992,7 +3001,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p Pair<PackedVector3Array, PackedInt32Array> occluder_arrays; List<Pair<NodePath, Node *>> node_renames; - _pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames); + _pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames, p_options); for (int i = 0; i < post_importer_plugins.size(); i++) { post_importer_plugins.write[i]->pre_process(scene, p_options); diff --git a/editor/import/3d/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h index 9759f328d7..fe757dc2a3 100644 --- a/editor/import/3d/resource_importer_scene.h +++ b/editor/import/3d/resource_importer_scene.h @@ -288,7 +288,7 @@ public: virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; } void _pre_fix_global(Node *p_scene, const HashMap<StringName, Variant> &p_options) const; - Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames); + Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames, const HashMap<StringName, Variant> &p_options); Node *_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps); Node *_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale); Node *_post_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps, bool p_remove_immutable_tracks); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index dc07403213..3a3598c0b9 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -30,6 +30,7 @@ #include "inspector_dock.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -97,7 +98,7 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) { case OBJECT_REQUEST_HELP: { if (current) { - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); emit_signal(SNAME("request_help"), current->get_class()); } } break; diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index e6fe554923..f31346fe67 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -35,6 +35,7 @@ #include "core/io/stream_peer_tls.h" #include "core/os/keyboard.h" #include "core/version.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_settings.h" @@ -1794,7 +1795,7 @@ void AssetLibraryEditorPlugin::make_visible(bool p_visible) { AssetLibraryEditorPlugin::AssetLibraryEditorPlugin() { addon_library = memnew(EditorAssetLibrary); addon_library->set_v_size_flags(Control::SIZE_EXPAND_FILL); - EditorNode::get_singleton()->get_main_screen_control()->add_child(addon_library); + EditorNode::get_singleton()->get_editor_main_screen()->get_control()->add_child(addon_library); addon_library->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); addon_library->hide(); } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index d1032470f2..e9a796dae7 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -34,6 +34,7 @@ #include "core/input/input.h" #include "core/os/keyboard.h" #include "editor/debugger/editor_debugger_node.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -4011,6 +4012,8 @@ void CanvasItemEditor::_project_settings_changed() { void CanvasItemEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { + _update_lock_and_group_button(); + EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true)); EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false)); ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CanvasItemEditor::_project_settings_changed)); @@ -5735,7 +5738,7 @@ void CanvasItemEditorPlugin::_notification(int p_what) { CanvasItemEditorPlugin::CanvasItemEditorPlugin() { canvas_item_editor = memnew(CanvasItemEditor); canvas_item_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); - EditorNode::get_singleton()->get_main_screen_control()->add_child(canvas_item_editor); + EditorNode::get_singleton()->get_editor_main_screen()->get_control()->add_child(canvas_item_editor); canvas_item_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); canvas_item_editor->hide(); } diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp index 69f287c134..e0ce330813 100644 --- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp @@ -208,7 +208,7 @@ void CPUParticles3DEditorPlugin::make_visible(bool p_visible) { CPUParticles3DEditorPlugin::CPUParticles3DEditorPlugin() { particles_editor = memnew(CPUParticles3DEditor); - EditorNode::get_singleton()->get_main_screen_control()->add_child(particles_editor); + EditorNode::get_singleton()->get_gui_base()->add_child(particles_editor); particles_editor->hide(); } diff --git a/editor/plugins/editor_context_menu_plugin.cpp b/editor/plugins/editor_context_menu_plugin.cpp index 86d3c0a896..0648327fab 100644 --- a/editor/plugins/editor_context_menu_plugin.cpp +++ b/editor/plugins/editor_context_menu_plugin.cpp @@ -32,9 +32,11 @@ #include "core/input/shortcut.h" #include "editor/editor_node.h" +#include "editor/editor_string_names.h" +#include "scene/gui/popup_menu.h" #include "scene/resources/texture.h" -void EditorContextMenuPlugin::add_options(const Vector<String> &p_paths) { +void EditorContextMenuPlugin::get_options(const Vector<String> &p_paths) { GDVIRTUAL_CALL(_popup_menu, p_paths); } @@ -42,24 +44,142 @@ void EditorContextMenuPlugin::add_menu_shortcut(const Ref<Shortcut> &p_shortcut, context_menu_shortcuts.insert(p_shortcut, p_callable); } -void EditorContextMenuPlugin::add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture, const Ref<Shortcut> &p_shortcut) { +void EditorContextMenuPlugin::add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture) { ERR_FAIL_COND_MSG(context_menu_items.has(p_name), "Context menu item already registered."); ERR_FAIL_COND_MSG(context_menu_items.size() == MAX_ITEMS, "Maximum number of context menu items reached."); + ContextMenuItem item; item.item_name = p_name; item.callable = p_callable; item.icon = p_texture; - item.shortcut = p_shortcut; - item.idx = EditorData::CONTEXT_MENU_ITEM_ID_BASE + start_idx + context_menu_shortcuts.size() + context_menu_items.size(); context_menu_items.insert(p_name, item); } -void EditorContextMenuPlugin::clear_context_menu_items() { - context_menu_items.clear(); +void EditorContextMenuPlugin::add_context_menu_item_from_shortcut(const String &p_name, const Ref<Shortcut> &p_shortcut, const Ref<Texture2D> &p_texture) { + Callable *callback = context_menu_shortcuts.getptr(p_shortcut); + ERR_FAIL_NULL_MSG(callback, "Shortcut not registered. Use add_menu_shortcut() first."); + + ContextMenuItem item; + item.item_name = p_name; + item.callable = *callback; + item.icon = p_texture; + item.shortcut = p_shortcut; + context_menu_items.insert(p_name, item); } void EditorContextMenuPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("add_menu_shortcut", "shortcut", "callback"), &EditorContextMenuPlugin::add_menu_shortcut); - ClassDB::bind_method(D_METHOD("add_context_menu_item", "name", "callback", "icon", "shortcut"), &EditorContextMenuPlugin::add_context_menu_item, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Shortcut>())); + ClassDB::bind_method(D_METHOD("add_context_menu_item", "name", "callback", "icon"), &EditorContextMenuPlugin::add_context_menu_item, DEFVAL(Ref<Texture2D>())); + ClassDB::bind_method(D_METHOD("add_context_menu_item_from_shortcut", "name", "shortcut", "icon"), &EditorContextMenuPlugin::add_context_menu_item_from_shortcut, DEFVAL(Ref<Texture2D>())); + GDVIRTUAL_BIND(_popup_menu, "paths"); + + BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCENE_TREE); + BIND_ENUM_CONSTANT(CONTEXT_SLOT_FILESYSTEM); + BIND_ENUM_CONSTANT(CONTEXT_SLOT_FILESYSTEM_CREATE); + BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCRIPT_EDITOR); +} + +void EditorContextMenuPluginManager::add_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) { + ERR_FAIL_COND(p_plugin.is_null()); + ERR_FAIL_COND(plugin_list.has(p_plugin)); + + p_plugin->slot = p_slot; + plugin_list.push_back(p_plugin); +} + +void EditorContextMenuPluginManager::remove_plugin(const Ref<EditorContextMenuPlugin> &p_plugin) { + ERR_FAIL_COND(p_plugin.is_null()); + ERR_FAIL_COND(!plugin_list.has(p_plugin)); + + plugin_list.erase(p_plugin); +} + +void EditorContextMenuPluginManager::add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector<String> &p_paths) { + bool separator_added = false; + const int icon_size = p_popup->get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); + int id = EditorContextMenuPlugin::BASE_ID; + + for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) { + if (plugin->slot != p_slot) { + continue; + } + plugin->context_menu_items.clear(); + plugin->get_options(p_paths); + + HashMap<String, EditorContextMenuPlugin::ContextMenuItem> &items = plugin->context_menu_items; + if (items.size() > 0 && !separator_added) { + separator_added = true; + p_popup->add_separator(); + } + + for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &E : items) { + EditorContextMenuPlugin::ContextMenuItem &item = E.value; + item.id = id; + + if (item.icon.is_valid()) { + p_popup->add_icon_item(item.icon, item.item_name, id); + p_popup->set_item_icon_max_width(-1, icon_size); + } else { + p_popup->add_item(item.item_name, id); + } + if (item.shortcut.is_valid()) { + p_popup->set_item_shortcut(-1, item.shortcut, true); + } + id++; + } + } +} + +Callable EditorContextMenuPluginManager::match_custom_shortcut(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<InputEvent> &p_event) { + for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) { + if (plugin->slot != p_slot) { + continue; + } + + for (KeyValue<Ref<Shortcut>, Callable> &E : plugin->context_menu_shortcuts) { + if (E.key->matches_event(p_event)) { + return E.value; + } + } + } + return Callable(); +} + +bool EditorContextMenuPluginManager::activate_custom_option(ContextMenuSlot p_slot, int p_option, const Variant &p_arg) { + for (Ref<EditorContextMenuPlugin> &plugin : plugin_list) { + if (plugin->slot != p_slot) { + continue; + } + + for (KeyValue<String, EditorContextMenuPlugin::ContextMenuItem> &E : plugin->context_menu_items) { + if (E.value.id == p_option) { + invoke_callback(E.value.callable, p_arg); + return true; + } + } + } + return false; +} + +void EditorContextMenuPluginManager::invoke_callback(const Callable &p_callback, const Variant &p_arg) { + const Variant *argptr = &p_arg; + Callable::CallError ce; + Variant result; + p_callback.callp(&argptr, 1, result, ce); + + if (ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_MSG("Failed to execute context menu callback: " + Variant::get_callable_error_text(p_callback, &argptr, 1, ce) + "."); + } +} + +void EditorContextMenuPluginManager::create() { + ERR_FAIL_COND(singleton != nullptr); + singleton = memnew(EditorContextMenuPluginManager); +} + +void EditorContextMenuPluginManager::cleanup() { + ERR_FAIL_NULL(singleton); + memdelete(singleton); + singleton = nullptr; } diff --git a/editor/plugins/editor_context_menu_plugin.h b/editor/plugins/editor_context_menu_plugin.h index db05e6c6c7..0232d254ba 100644 --- a/editor/plugins/editor_context_menu_plugin.h +++ b/editor/plugins/editor_context_menu_plugin.h @@ -34,19 +34,33 @@ #include "core/object/gdvirtual.gen.inc" #include "core/object/ref_counted.h" -class Texture2D; +class InputEvent; +class PopupMenu; class Shortcut; +class Texture2D; class EditorContextMenuPlugin : public RefCounted { GDCLASS(EditorContextMenuPlugin, RefCounted); -public: - int start_idx; + friend class EditorContextMenuPluginManager; inline static constexpr int MAX_ITEMS = 100; +public: + enum ContextMenuSlot { + CONTEXT_SLOT_SCENE_TREE, + CONTEXT_SLOT_FILESYSTEM, + CONTEXT_SLOT_SCRIPT_EDITOR, + CONTEXT_SLOT_FILESYSTEM_CREATE, + }; + inline static constexpr int BASE_ID = 2000; + +private: + int slot = -1; + +public: struct ContextMenuItem { - int idx = 0; + int id = 0; String item_name; Callable callable; Ref<Texture2D> icon; @@ -61,10 +75,37 @@ protected: GDVIRTUAL1(_popup_menu, Vector<String>); public: - virtual void add_options(const Vector<String> &p_paths); + virtual void get_options(const Vector<String> &p_paths); + void add_menu_shortcut(const Ref<Shortcut> &p_shortcut, const Callable &p_callable); - void add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture, const Ref<Shortcut> &p_shortcut); - void clear_context_menu_items(); + void add_context_menu_item(const String &p_name, const Callable &p_callable, const Ref<Texture2D> &p_texture); + void add_context_menu_item_from_shortcut(const String &p_name, const Ref<Shortcut> &p_shortcut, const Ref<Texture2D> &p_texture); +}; + +VARIANT_ENUM_CAST(EditorContextMenuPlugin::ContextMenuSlot); + +class EditorContextMenuPluginManager : public Object { + GDCLASS(EditorContextMenuPluginManager, Object); + + using ContextMenuSlot = EditorContextMenuPlugin::ContextMenuSlot; + static inline EditorContextMenuPluginManager *singleton = nullptr; + + LocalVector<Ref<EditorContextMenuPlugin>> plugin_list; + +public: + static EditorContextMenuPluginManager *get_singleton() { return singleton; } + + void add_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin); + void remove_plugin(const Ref<EditorContextMenuPlugin> &p_plugin); + + void add_options_from_plugins(PopupMenu *p_popup, ContextMenuSlot p_slot, const Vector<String> &p_paths); + Callable match_custom_shortcut(ContextMenuSlot p_slot, const Ref<InputEvent> &p_event); + bool activate_custom_option(ContextMenuSlot p_slot, int p_option, const Variant &p_arg); + + void invoke_callback(const Callable &p_callback, const Variant &p_arg); + + static void create(); + static void cleanup(); }; #endif // EDITOR_CONTEXT_MENU_PLUGIN_H diff --git a/editor/plugins/editor_plugin.cpp b/editor/plugins/editor_plugin.cpp index 0ee67e08ba..c8426bce73 100644 --- a/editor/plugins/editor_plugin.cpp +++ b/editor/plugins/editor_plugin.cpp @@ -47,7 +47,6 @@ #include "editor/import/editor_import_plugin.h" #include "editor/inspector_dock.h" #include "editor/plugins/canvas_item_editor_plugin.h" -#include "editor/plugins/editor_context_menu_plugin.h" #include "editor/plugins/editor_debugger_plugin.h" #include "editor/plugins/editor_resource_conversion_plugin.h" #include "editor/plugins/node_3d_editor_plugin.h" @@ -491,14 +490,12 @@ void EditorPlugin::remove_scene_post_import_plugin(const Ref<EditorScenePostImpo ResourceImporterScene::remove_post_importer_plugin(p_plugin); } -void EditorPlugin::add_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) { - ERR_FAIL_COND(p_plugin.is_null()); - EditorNode::get_editor_data().add_context_menu_plugin(EditorData::ContextMenuSlot(p_slot), p_plugin); +void EditorPlugin::add_context_menu_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) { + EditorContextMenuPluginManager::get_singleton()->add_plugin(p_slot, p_plugin); } -void EditorPlugin::remove_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin) { - ERR_FAIL_COND(p_plugin.is_null()); - EditorNode::get_editor_data().remove_context_menu_plugin(EditorData::ContextMenuSlot(p_slot), p_plugin); +void EditorPlugin::remove_context_menu_plugin(const Ref<EditorContextMenuPlugin> &p_plugin) { + EditorContextMenuPluginManager::get_singleton()->remove_plugin(p_plugin); } int find(const PackedStringArray &a, const String &v) { @@ -641,7 +638,7 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_event_forwarding_always_enabled"), &EditorPlugin::set_input_event_forwarding_always_enabled); ClassDB::bind_method(D_METHOD("set_force_draw_over_forwarding_enabled"), &EditorPlugin::set_force_draw_over_forwarding_enabled); ClassDB::bind_method(D_METHOD("add_context_menu_plugin", "slot", "plugin"), &EditorPlugin::add_context_menu_plugin); - ClassDB::bind_method(D_METHOD("remove_context_menu_plugin", "slot", "plugin"), &EditorPlugin::remove_context_menu_plugin); + ClassDB::bind_method(D_METHOD("remove_context_menu_plugin", "plugin"), &EditorPlugin::remove_context_menu_plugin); ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorPlugin::get_editor_interface); ClassDB::bind_method(D_METHOD("get_script_create_dialog"), &EditorPlugin::get_script_create_dialog); @@ -707,11 +704,6 @@ void EditorPlugin::_bind_methods() { BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_PASS); BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_STOP); BIND_ENUM_CONSTANT(AFTER_GUI_INPUT_CUSTOM); - - BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCENE_TREE); - BIND_ENUM_CONSTANT(CONTEXT_SLOT_FILESYSTEM); - BIND_ENUM_CONSTANT(CONTEXT_SLOT_SCRIPT_EDITOR); - BIND_ENUM_CONSTANT(CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE); } EditorUndoRedoManager *EditorPlugin::get_undo_redo() { diff --git a/editor/plugins/editor_plugin.h b/editor/plugins/editor_plugin.h index 35f65f03cd..b43cbca661 100644 --- a/editor/plugins/editor_plugin.h +++ b/editor/plugins/editor_plugin.h @@ -32,6 +32,7 @@ #define EDITOR_PLUGIN_H #include "core/io/config_file.h" +#include "editor/plugins/editor_context_menu_plugin.h" #include "scene/3d/camera_3d.h" #include "scene/gui/control.h" @@ -53,7 +54,6 @@ class EditorToolAddons; class EditorTranslationParserPlugin; class EditorUndoRedoManager; class ScriptCreateDialog; -class EditorContextMenuPlugin; class EditorPlugin : public Node { GDCLASS(EditorPlugin, Node); @@ -103,13 +103,6 @@ public: AFTER_GUI_INPUT_CUSTOM, }; - enum ContextMenuSlot { - CONTEXT_SLOT_SCENE_TREE, - CONTEXT_SLOT_FILESYSTEM, - CONTEXT_SLOT_SCRIPT_EDITOR, - CONTEXT_SUBMENU_SLOT_FILESYSTEM_CREATE, - }; - protected: void _notification(int p_what); @@ -257,8 +250,8 @@ public: void add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin); void remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin); - void add_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin); - void remove_context_menu_plugin(ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin); + void add_context_menu_plugin(EditorContextMenuPlugin::ContextMenuSlot p_slot, const Ref<EditorContextMenuPlugin> &p_plugin); + void remove_context_menu_plugin(const Ref<EditorContextMenuPlugin> &p_plugin); void enable_plugin(); void disable_plugin(); @@ -270,7 +263,6 @@ public: VARIANT_ENUM_CAST(EditorPlugin::CustomControlContainer); VARIANT_ENUM_CAST(EditorPlugin::DockSlot); VARIANT_ENUM_CAST(EditorPlugin::AfterGUIInput); -VARIANT_ENUM_CAST(EditorPlugin::ContextMenuSlot); typedef EditorPlugin *(*EditorPluginCreateFunc)(); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 7ac924571d..a2c36b1f3c 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -76,7 +76,7 @@ void post_process_preview(Ref<Image> p_image) { } bool EditorTexturePreviewPlugin::handles(const String &p_type) const { - return ClassDB::is_parent_class(p_type, "Texture2D"); + return ClassDB::is_parent_class(p_type, "Texture2D") || ClassDB::is_parent_class(p_type, "Texture3D") || ClassDB::is_parent_class(p_type, "TextureLayered"); } bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const { @@ -85,9 +85,13 @@ bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const { Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const { Ref<Image> img; - Ref<AtlasTexture> atex = p_from; - if (atex.is_valid()) { - Ref<Texture2D> tex = atex->get_atlas(); + + Ref<AtlasTexture> tex_atlas = p_from; + Ref<Texture3D> tex_3d = p_from; + Ref<TextureLayered> tex_lyr = p_from; + + if (tex_atlas.is_valid()) { + Ref<Texture2D> tex = tex_atlas->get_atlas(); if (!tex.is_valid()) { return Ref<Texture2D>(); } @@ -97,11 +101,36 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, return Ref<Texture2D>(); } - if (!atex->get_region().has_area()) { + if (!tex_atlas->get_region().has_area()) { + return Ref<Texture2D>(); + } + + img = atlas->get_region(tex_atlas->get_region()); + + } else if (tex_3d.is_valid()) { + if (tex_3d->get_depth() == 0) { + return Ref<Texture2D>(); + } + + const int mid_depth = (tex_3d->get_depth() - 1) / 2; + + Vector<Ref<Image>> data = tex_3d->get_data(); + if (!data.is_empty() && data[mid_depth].is_valid()) { + img = data[mid_depth]->duplicate(); + } + + } else if (tex_lyr.is_valid()) { + if (tex_lyr->get_layers() == 0) { return Ref<Texture2D>(); } - img = atlas->get_region(atex->get_region()); + const int mid_layer = (tex_lyr->get_layers() - 1) / 2; + + Ref<Image> data = tex_lyr->get_layer_data(mid_layer); + if (data.is_valid()) { + img = data->duplicate(); + } + } else { Ref<Texture2D> tex = p_from; if (tex.is_valid()) { @@ -115,6 +144,7 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from, if (img.is_null() || img->is_empty()) { return Ref<Texture2D>(); } + p_metadata["dimensions"] = img->get_size(); img->clear_mipmaps(); diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index 4e9be0aa53..e711f44ccf 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -452,7 +452,7 @@ void GPUParticles3DEditorPlugin::make_visible(bool p_visible) { GPUParticles3DEditorPlugin::GPUParticles3DEditorPlugin() { particles_editor = memnew(GPUParticles3DEditor); - EditorNode::get_singleton()->get_main_screen_control()->add_child(particles_editor); + EditorNode::get_singleton()->get_gui_base()->add_child(particles_editor); particles_editor->hide(); } diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp index 369d6ab009..eda6cdffb1 100644 --- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp @@ -689,7 +689,7 @@ void MeshInstance3DEditorPlugin::make_visible(bool p_visible) { MeshInstance3DEditorPlugin::MeshInstance3DEditorPlugin() { mesh_editor = memnew(MeshInstance3DEditor); - EditorNode::get_singleton()->get_main_screen_control()->add_child(mesh_editor); + EditorNode::get_singleton()->get_gui_base()->add_child(mesh_editor); mesh_editor->options->hide(); } diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index 58cc670475..d6650bd08f 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -308,7 +308,7 @@ void MeshLibraryEditorPlugin::make_visible(bool p_visible) { MeshLibraryEditorPlugin::MeshLibraryEditorPlugin() { mesh_library_editor = memnew(MeshLibraryEditor); - EditorNode::get_singleton()->get_main_screen_control()->add_child(mesh_library_editor); + EditorNode::get_singleton()->get_gui_base()->add_child(mesh_library_editor); mesh_library_editor->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE); mesh_library_editor->set_end(Point2(0, 22)); mesh_library_editor->hide(); diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index 76ffdb02e1..729ceccd25 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -384,7 +384,7 @@ void MultiMeshEditorPlugin::make_visible(bool p_visible) { MultiMeshEditorPlugin::MultiMeshEditorPlugin() { multimesh_editor = memnew(MultiMeshEditor); - EditorNode::get_singleton()->get_main_screen_control()->add_child(multimesh_editor); + EditorNode::get_singleton()->get_gui_base()->add_child(multimesh_editor); multimesh_editor->options->hide(); } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 44673e7224..f58dfbb5a5 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -37,6 +37,7 @@ #include "core/math/projection.h" #include "core/os/keyboard.h" #include "editor/debugger/editor_debugger_node.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -810,7 +811,7 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) const { Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_ray_query(pos, pos + ray * camera->get_far()); for (Node3D *spat : nodes_with_gizmos) { - if (!spat) { + if (!spat || _is_node_locked(spat)) { continue; } @@ -1557,7 +1558,7 @@ void Node3DEditorViewport::_surface_focus_exit() { view_menu->set_disable_shortcuts(true); } -bool Node3DEditorViewport ::_is_node_locked(const Node *p_node) { +bool Node3DEditorViewport::_is_node_locked(const Node *p_node) const { return p_node->get_meta("_edit_lock_", false); } @@ -1935,11 +1936,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (after != EditorPlugin::AFTER_GUI_INPUT_CUSTOM) { // Single item selection. - Vector<_RayResult> selection; - _find_items_at_pos(b->get_position(), selection, false); - if (!selection.is_empty()) { - clicked = selection[0].item->get_instance_id(); - } + clicked = _select_ray(b->get_position()); selection_in_progress = true; @@ -9376,7 +9373,7 @@ Vector<Node3D *> Node3DEditor::gizmo_bvh_frustum_query(const Vector<Plane> &p_fr Node3DEditorPlugin::Node3DEditorPlugin() { spatial_editor = memnew(Node3DEditor); spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); - EditorNode::get_singleton()->get_main_screen_control()->add_child(spatial_editor); + EditorNode::get_singleton()->get_editor_main_screen()->get_control()->add_child(spatial_editor); spatial_editor->hide(); } diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 2cfe784ca6..c7e6420875 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -460,7 +460,7 @@ private: bool previewing_camera = false; bool previewing_cinema = false; - bool _is_node_locked(const Node *p_node); + bool _is_node_locked(const Node *p_node) const; void _preview_exited_scene(); void _toggle_camera_preview(bool); void _toggle_cinema_preview(bool); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 93c8ae5438..4996964976 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -44,6 +44,7 @@ #include "editor/editor_command_palette.h" #include "editor/editor_help_search.h" #include "editor/editor_interface.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_script.h" @@ -1399,13 +1400,12 @@ void ScriptEditor::_menu_option(int p_option) { } } - // Context menu options. - if (p_option >= EditorData::CONTEXT_MENU_ITEM_ID_BASE) { + if (p_option >= EditorContextMenuPlugin::BASE_ID) { Ref<Resource> resource; if (current) { resource = current->get_edited_resource(); } - EditorNode::get_editor_data().script_editor_options_pressed(EditorData::CONTEXT_SLOT_SCRIPT_EDITOR, p_option, resource); + EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR, p_option, resource); return; } @@ -3315,10 +3315,16 @@ void ScriptEditor::shortcut_input(const Ref<InputEvent> &p_event) { _menu_option(WINDOW_MOVE_DOWN); accept_event(); } - // Context menu shortcuts. - int match_option = EditorNode::get_editor_data().match_context_menu_shortcut(EditorData::CONTEXT_SLOT_SCRIPT_EDITOR, p_event); - if (match_option) { - _menu_option(match_option); + + Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR, p_event); + if (custom_callback.is_valid()) { + Ref<Resource> resource; + ScriptEditorBase *current = _get_current_editor(); + if (current) { + resource = current->get_edited_resource(); + } + EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, resource); + accept_event(); } } @@ -3387,7 +3393,7 @@ void ScriptEditor::_make_script_list_context_menu() { selected_paths.push_back(path); } } - EditorNode::get_editor_data().add_options_from_plugins(context_menu, EditorData::CONTEXT_SLOT_SCRIPT_EDITOR, selected_paths); + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR, selected_paths); context_menu->set_position(get_screen_position() + get_local_mouse_position()); context_menu->reset_size(); @@ -4612,7 +4618,7 @@ ScriptEditorPlugin::ScriptEditorPlugin() { Ref<Shortcut> make_floating_shortcut = ED_SHORTCUT_AND_COMMAND("script_editor/make_floating", TTR("Make Floating")); window_wrapper->set_wrapped_control(script_editor, make_floating_shortcut); - EditorNode::get_singleton()->get_main_screen_control()->add_child(window_wrapper); + EditorNode::get_singleton()->get_editor_main_screen()->get_control()->add_child(window_wrapper); window_wrapper->set_v_size_flags(Control::SIZE_EXPAND_FILL); window_wrapper->hide(); window_wrapper->connect("window_visibility_changed", callable_mp(this, &ScriptEditorPlugin::_window_visibility_changed)); diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp index 97c5c0c7dd..8f54641dcb 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.cpp +++ b/editor/plugins/skeleton_2d_editor_plugin.cpp @@ -129,7 +129,7 @@ void Skeleton2DEditorPlugin::make_visible(bool p_visible) { Skeleton2DEditorPlugin::Skeleton2DEditorPlugin() { sprite_editor = memnew(Skeleton2DEditor); - EditorNode::get_singleton()->get_main_screen_control()->add_child(sprite_editor); + EditorNode::get_singleton()->get_gui_base()->add_child(sprite_editor); make_visible(false); //sprite_editor->options->hide(); diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index 3647fa2d59..c7db243662 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -673,7 +673,7 @@ void Sprite2DEditorPlugin::make_visible(bool p_visible) { Sprite2DEditorPlugin::Sprite2DEditorPlugin() { sprite_editor = memnew(Sprite2DEditor); - EditorNode::get_singleton()->get_main_screen_control()->add_child(sprite_editor); + EditorNode::get_singleton()->get_gui_base()->add_child(sprite_editor); make_visible(false); //sprite_editor->options->hide(); diff --git a/editor/project_manager/project_dialog.cpp b/editor/project_manager/project_dialog.cpp index 9b009c0a89..7acda16890 100644 --- a/editor/project_manager/project_dialog.cpp +++ b/editor/project_manager/project_dialog.cpp @@ -162,7 +162,7 @@ void ProjectDialog::_validate_path() { } } - if (target_path.is_empty() || target_path.is_relative_path()) { + if (target_path.is_relative_path()) { _set_message(TTR("The path specified is invalid."), MESSAGE_ERROR, target_path_input_type); return; } @@ -352,7 +352,7 @@ void ProjectDialog::_install_path_changed() { void ProjectDialog::_browse_project_path() { String path = project_path->get_text(); - if (path.is_empty()) { + if (path.is_relative_path()) { path = EDITOR_GET("filesystem/directories/default_project_path"); } if (mode == MODE_IMPORT && install_path->is_visible_in_tree()) { @@ -382,12 +382,16 @@ void ProjectDialog::_browse_project_path() { void ProjectDialog::_browse_install_path() { ERR_FAIL_COND_MSG(mode != MODE_IMPORT, "Install path is only used for MODE_IMPORT."); + String path = install_path->get_text(); + if (path.is_relative_path() || !DirAccess::dir_exists_absolute(path)) { + path = EDITOR_GET("filesystem/directories/default_project_path"); + } if (create_dir->is_pressed()) { // Select parent directory of install path. - fdialog_install->set_current_dir(install_path->get_text().get_base_dir()); + fdialog_install->set_current_dir(path.get_base_dir()); } else { // Select install path. - fdialog_install->set_current_dir(install_path->get_text()); + fdialog_install->set_current_dir(path); } fdialog_install->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 2a39b11815..9f56c586a2 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -37,6 +37,7 @@ #include "core/os/keyboard.h" #include "editor/debugger/editor_debugger_node.h" #include "editor/editor_feature_profile.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_quick_open.h" @@ -214,11 +215,12 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) { } else if (ED_IS_SHORTCUT("scene_tree/delete", p_event)) { _tool_selected(TOOL_ERASE); } else { - int match_option = EditorNode::get_editor_data().match_context_menu_shortcut(EditorData::CONTEXT_SLOT_SCENE_TREE, p_event); - if (match_option) { - _tool_selected(match_option); + Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TREE, p_event); + if (custom_callback.is_valid()) { + EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, _get_selection_array()); + } else { + return; } - return; } // Tool selection was successful, accept the event to stop propagation. @@ -1197,7 +1199,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { ScriptEditor::get_singleton()->goto_help("class_name:" + class_name); } - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } break; case TOOL_AUTO_EXPAND: { scene_tree->set_auto_expand_selected(!EDITOR_GET("docks/scene_tree/auto_expand_to_selected"), true); @@ -1491,10 +1493,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; default: { - // Editor context plugin. - if (p_tool >= EditorData::CONTEXT_MENU_ITEM_ID_BASE) { - List<Node *> selection = editor_selection->get_selected_node_list(); - EditorNode::get_editor_data().scene_tree_options_pressed(EditorData::CONTEXT_SLOT_SCENE_TREE, p_tool, selection); + if (p_tool >= EditorContextMenuPlugin::BASE_ID) { + EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TREE, p_tool, _get_selection_array()); break; } @@ -3366,6 +3366,18 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) { } } +Array SceneTreeDock::_get_selection_array() { + List<Node *> selection = editor_selection->get_selected_node_list(); + TypedArray<Node> array; + array.resize(selection.size()); + + int i = 0; + for (const Node *E : selection) { + array[i++] = E; + } + return array; +} + void SceneTreeDock::_files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type) { Node *node = get_node(p_to); ERR_FAIL_NULL(node); @@ -3766,7 +3778,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { String node_path = root->get_path().rel_path_to(E->get()->get_path()); p_paths.push_back(node_path); } - EditorNode::get_editor_data().add_options_from_plugins(menu, EditorData::CONTEXT_SLOT_SCENE_TREE, p_paths); + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCENE_TREE, p_paths); menu->reset_size(); menu->set_position(p_menu_pos); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 1807ec5896..d0cdaf8dd1 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -262,6 +262,7 @@ class SceneTreeDock : public VBoxContainer { bool _has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const; void _normalize_drop(Node *&to_node, int &to_pos, int p_type); + Array _get_selection_array(); void _nodes_dragged(const Array &p_nodes, NodePath p_to, int p_type); void _files_dropped(const Vector<String> &p_files, NodePath p_to, int p_type); diff --git a/main/main.cpp b/main/main.cpp index 9c9542325e..af0d7b5804 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2550,6 +2550,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph // XR project settings. GLOBAL_DEF_RST_BASIC("xr/openxr/enabled", false); + GLOBAL_DEF_RST_BASIC("xr/openxr/enabled.editor", false); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "xr/openxr/default_action_map", PROPERTY_HINT_FILE, "*.tres"), "res://openxr_action_map.tres"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/form_factor", PROPERTY_HINT_ENUM, "Head Mounted,Handheld"), "0"); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/view_configuration", PROPERTY_HINT_ENUM, "Mono,Stereo"), "1"); // "Mono,Stereo,Quad,Observer" diff --git a/modules/basis_universal/image_compress_basisu.cpp b/modules/basis_universal/image_compress_basisu.cpp index 8167fe8c73..ab20d00b5b 100644 --- a/modules/basis_universal/image_compress_basisu.cpp +++ b/modules/basis_universal/image_compress_basisu.cpp @@ -84,14 +84,12 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha decompress_format = BASIS_DECOMPRESS_RGBA; } break; case Image::USED_CHANNELS_R: { - decompress_format = BASIS_DECOMPRESS_RGB; + decompress_format = BASIS_DECOMPRESS_R; } break; case Image::USED_CHANNELS_RG: { - // Currently RG textures are compressed as DXT5/ETC2_RGBA8 with a RA -> RG swizzle, - // as BasisUniversal didn't use to support ETC2_RG11 transcoding. params.m_force_alpha = true; image->convert_rg_to_ra_rgba8(); - decompress_format = BASIS_DECOMPRESS_RG_AS_RA; + decompress_format = BASIS_DECOMPRESS_RG; } break; case Image::USED_CHANNELS_RGB: { decompress_format = BASIS_DECOMPRESS_RGB; @@ -219,15 +217,68 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) { // Get supported compression formats. bool bptc_supported = RS::get_singleton()->has_os_feature("bptc"); bool astc_supported = RS::get_singleton()->has_os_feature("astc"); + bool rgtc_supported = RS::get_singleton()->has_os_feature("rgtc"); bool s3tc_supported = RS::get_singleton()->has_os_feature("s3tc"); bool etc2_supported = RS::get_singleton()->has_os_feature("etc2"); bool needs_ra_rg_swap = false; + bool needs_rg_trim = false; + + BasisDecompressFormat decompress_format = (BasisDecompressFormat)(*(uint32_t *)(src_ptr)); - switch (*(uint32_t *)(src_ptr)) { + switch (decompress_format) { + case BASIS_DECOMPRESS_R: { + if (rgtc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC4_R; + image_format = Image::FORMAT_RGTC_R; + } else if (s3tc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC1; + image_format = Image::FORMAT_DXT1; + } else if (etc2_supported) { + basisu_format = basist::transcoder_texture_format::cTFETC2_EAC_R11; + image_format = Image::FORMAT_ETC2_R11; + } else { + // No supported VRAM compression formats, decompress. + basisu_format = basist::transcoder_texture_format::cTFRGBA32; + image_format = Image::FORMAT_RGBA8; + needs_rg_trim = true; + } + + } break; case BASIS_DECOMPRESS_RG: { - // RGTC transcoding is currently performed with RG_AS_RA, fail. - ERR_FAIL_V(image); + if (rgtc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC5_RG; + image_format = Image::FORMAT_RGTC_RG; + } else if (s3tc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC3; + image_format = Image::FORMAT_DXT5_RA_AS_RG; + } else if (etc2_supported) { + basisu_format = basist::transcoder_texture_format::cTFETC2_EAC_RG11; + image_format = Image::FORMAT_ETC2_RG11; + } else { + // No supported VRAM compression formats, decompress. + basisu_format = basist::transcoder_texture_format::cTFRGBA32; + image_format = Image::FORMAT_RGBA8; + needs_ra_rg_swap = true; + needs_rg_trim = true; + } + + } break; + case BASIS_DECOMPRESS_RG_AS_RA: { + if (s3tc_supported) { + basisu_format = basist::transcoder_texture_format::cTFBC3; + image_format = Image::FORMAT_DXT5_RA_AS_RG; + } else if (etc2_supported) { + basisu_format = basist::transcoder_texture_format::cTFETC2; + image_format = Image::FORMAT_ETC2_RA_AS_RG; + } else { + // No supported VRAM compression formats, decompress. + basisu_format = basist::transcoder_texture_format::cTFRGBA32; + image_format = Image::FORMAT_RGBA8; + needs_ra_rg_swap = true; + needs_rg_trim = true; + } + } break; case BASIS_DECOMPRESS_RGB: { if (bptc_supported) { @@ -267,20 +318,7 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) { basisu_format = basist::transcoder_texture_format::cTFRGBA32; image_format = Image::FORMAT_RGBA8; } - } break; - case BASIS_DECOMPRESS_RG_AS_RA: { - if (s3tc_supported) { - basisu_format = basist::transcoder_texture_format::cTFBC3; - image_format = Image::FORMAT_DXT5_RA_AS_RG; - } else if (etc2_supported) { - basisu_format = basist::transcoder_texture_format::cTFETC2; - image_format = Image::FORMAT_ETC2_RA_AS_RG; - } else { - // No supported VRAM compression formats, decompress. - basisu_format = basist::transcoder_texture_format::cTFRGBA32; - image_format = Image::FORMAT_RGBA8; - needs_ra_rg_swap = true; - } + } break; } @@ -324,6 +362,15 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) { image->convert_ra_rgba8_to_rg(); } + if (needs_rg_trim) { + // Remove unnecessary color channels from uncompressed textures. + if (decompress_format == BASIS_DECOMPRESS_R) { + image->convert(Image::FORMAT_R8); + } else if (decompress_format == BASIS_DECOMPRESS_RG || decompress_format == BASIS_DECOMPRESS_RG_AS_RA) { + image->convert(Image::FORMAT_RG8); + } + } + return image; } diff --git a/modules/basis_universal/image_compress_basisu.h b/modules/basis_universal/image_compress_basisu.h index ac5d62ae73..5e36d448f6 100644 --- a/modules/basis_universal/image_compress_basisu.h +++ b/modules/basis_universal/image_compress_basisu.h @@ -38,6 +38,7 @@ enum BasisDecompressFormat { BASIS_DECOMPRESS_RGB, BASIS_DECOMPRESS_RGBA, BASIS_DECOMPRESS_RG_AS_RA, + BASIS_DECOMPRESS_R, }; void basis_universal_init(); diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 8777651545..8c81c0ce4e 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -513,7 +513,7 @@ Ref<ConcavePolygonShape3D> CSGShape3D::bake_collision_shape() { } bool CSGShape3D::_is_debug_collision_shape_visible() { - return is_inside_tree() && (get_tree()->is_debugging_collisions_hint() || Engine::get_singleton()->is_editor_hint()); + return !Engine::get_singleton()->is_editor_hint() && is_inside_tree() && get_tree()->is_debugging_collisions_hint(); } void CSGShape3D::_update_debug_collision_shape() { @@ -604,11 +604,6 @@ void CSGShape3D::_notification(int p_what) { // Update this node's parent only if its own visibility has changed, not the visibility of parent nodes parent_shape->_make_dirty(); } - if (is_visible()) { - _update_debug_collision_shape(); - } else { - _clear_debug_collision_shape(); - } last_visible = is_visible(); } break; diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp index 72676f4a40..3712441e51 100644 --- a/modules/csg/editor/csg_gizmos.cpp +++ b/modules/csg/editor/csg_gizmos.cpp @@ -541,7 +541,7 @@ EditorPluginCSG::EditorPluginCSG() { Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); csg_shape_editor = memnew(CSGShapeEditor); - EditorNode::get_singleton()->get_main_screen_control()->add_child(csg_shape_editor); + EditorNode::get_singleton()->get_gui_base()->add_child(csg_shape_editor); } #endif // TOOLS_ENABLED diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 524f528f76..cf1cd55355 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -3486,10 +3486,10 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c opt = opt.substr(1); } - // The path needs quotes if at least one of its components (excluding `/` separations) + // The path needs quotes if at least one of its components (excluding `%` prefix and `/` separations) // is not a valid identifier. bool path_needs_quote = false; - for (const String &part : opt.split("/")) { + for (const String &part : opt.trim_prefix("%").split("/")) { if (!part.is_valid_ascii_identifier()) { path_needs_quote = true; break; diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_unique.cfg b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_unique.cfg new file mode 100644 index 0000000000..36c150f6e3 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_unique.cfg @@ -0,0 +1,9 @@ +[input] +scene="res://completion/get_node/get_node.tscn" +[output] +include=[ + {"display": "%UniqueA"}, +] +exclude=[ + {"display": "\"%UniqueA\""}, +] diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_unique.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_unique.gd new file mode 100644 index 0000000000..def050e938 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_unique.gd @@ -0,0 +1,5 @@ +extends Node + +func a(): + $➡ + pass diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index ea63e07104..6b010335e6 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -34,6 +34,7 @@ #include "core/input/input.h" #include "core/os/keyboard.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -958,7 +959,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) { _update_selection_transform(); _update_paste_indicator(); - spatial_editor = Object::cast_to<Node3DEditorPlugin>(EditorNode::get_singleton()->get_editor_plugin_screen()); + spatial_editor = Object::cast_to<Node3DEditorPlugin>(EditorNode::get_singleton()->get_editor_main_screen()->get_selected_plugin()); if (!node) { set_process(false); diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index 394213963a..e29fe9295a 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -159,6 +159,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackMP3::get_sample_playback() const { void AudioStreamPlaybackMP3::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { sample_playback = p_playback; + if (sample_playback.is_valid()) { + sample_playback->stream_playback = Ref<AudioStreamPlayback>(this); + } } void AudioStreamPlaybackMP3::set_parameter(const StringName &p_name, const Variant &p_value) { diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 7322a47630..df240a5965 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -41,6 +41,7 @@ #include "core/os/os.h" #include "core/version.h" #include "editor/debugger/editor_debugger_node.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_settings.h" @@ -165,7 +166,7 @@ bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, } void godot_icall_Internal_EditorNodeShowScriptScreen() { - EditorNode::get_singleton()->editor_select(EditorNode::EDITOR_SCRIPT); + EditorNode::get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } void godot_icall_Internal_EditorRunPlay() { diff --git a/modules/navigation/2d/godot_navigation_server_2d.cpp b/modules/navigation/2d/godot_navigation_server_2d.cpp index bf69adc14c..2af125d434 100644 --- a/modules/navigation/2d/godot_navigation_server_2d.cpp +++ b/modules/navigation/2d/godot_navigation_server_2d.cpp @@ -318,6 +318,11 @@ int FORWARD_1_C(region_get_connections_count, RID, p_region, rid_to_rid); Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_start, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int); Vector2 FORWARD_2_R_C(v3_to_v2, region_get_connection_pathway_end, RID, p_region, int, p_connection_id, rid_to_rid, int_to_int); +Vector2 GodotNavigationServer2D::region_get_closest_point(RID p_region, const Vector2 &p_point) const { + Vector3 result = NavigationServer3D::get_singleton()->region_get_closest_point(p_region, v2_to_v3(p_point)); + return v3_to_v2(result); +} + Vector2 GodotNavigationServer2D::region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const { Vector3 result = NavigationServer3D::get_singleton()->region_get_random_point(p_region, p_navigation_layers, p_uniformly); return v3_to_v2(result); diff --git a/modules/navigation/2d/godot_navigation_server_2d.h b/modules/navigation/2d/godot_navigation_server_2d.h index ea77fa5e6e..1579ca2907 100644 --- a/modules/navigation/2d/godot_navigation_server_2d.h +++ b/modules/navigation/2d/godot_navigation_server_2d.h @@ -101,6 +101,7 @@ public: virtual int region_get_connections_count(RID p_region) const override; virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; + virtual Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const override; virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override; virtual RID link_create() override; diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp index 11a5de608b..5dfc39f6f5 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.cpp +++ b/modules/navigation/3d/godot_navigation_server_3d.cpp @@ -536,6 +536,27 @@ Vector3 GodotNavigationServer3D::region_get_connection_pathway_end(RID p_region, return Vector3(); } +Vector3 GodotNavigationServer3D::region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const { + const NavRegion *region = region_owner.get_or_null(p_region); + ERR_FAIL_NULL_V(region, Vector3()); + + return region->get_closest_point_to_segment(p_from, p_to, p_use_collision); +} + +Vector3 GodotNavigationServer3D::region_get_closest_point(RID p_region, const Vector3 &p_point) const { + const NavRegion *region = region_owner.get_or_null(p_region); + ERR_FAIL_NULL_V(region, Vector3()); + + return region->get_closest_point_info(p_point).point; +} + +Vector3 GodotNavigationServer3D::region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const { + const NavRegion *region = region_owner.get_or_null(p_region); + ERR_FAIL_NULL_V(region, Vector3()); + + return region->get_closest_point_info(p_point).normal; +} + Vector3 GodotNavigationServer3D::region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const { const NavRegion *region = region_owner.get_or_null(p_region); ERR_FAIL_NULL_V(region, Vector3()); diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h index 12a1132f07..eae6ea2860 100644 --- a/modules/navigation/3d/godot_navigation_server_3d.h +++ b/modules/navigation/3d/godot_navigation_server_3d.h @@ -178,6 +178,9 @@ public: virtual int region_get_connections_count(RID p_region) const override; virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; + virtual Vector3 region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const override; + virtual Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override; + virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override; virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override; virtual RID link_create() override; diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp index f37ed9b168..7f0cbc7b5e 100644 --- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp +++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp @@ -176,7 +176,7 @@ void NavigationMeshEditorPlugin::make_visible(bool p_visible) { NavigationMeshEditorPlugin::NavigationMeshEditorPlugin() { navigation_mesh_editor = memnew(NavigationMeshEditor); - EditorNode::get_singleton()->get_main_screen_control()->add_child(navigation_mesh_editor); + EditorNode::get_singleton()->get_gui_base()->add_child(navigation_mesh_editor); add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, navigation_mesh_editor->bake_hbox); navigation_mesh_editor->hide(); navigation_mesh_editor->bake_hbox->hide(); diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp index 7a44adecbc..2c91b80af2 100644 --- a/modules/navigation/nav_region.cpp +++ b/modules/navigation/nav_region.cpp @@ -105,7 +105,22 @@ void NavRegion::set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) { polygons_dirty = true; } +Vector3 NavRegion::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const { + RWLockRead read_lock(region_rwlock); + + return NavMeshQueries3D::polygons_get_closest_point_to_segment( + get_polygons(), p_from, p_to, p_use_collision); +} + +gd::ClosestPointQueryResult NavRegion::get_closest_point_info(const Vector3 &p_point) const { + RWLockRead read_lock(region_rwlock); + + return NavMeshQueries3D::polygons_get_closest_point_info(get_polygons(), p_point); +} + Vector3 NavRegion::get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const { + RWLockRead read_lock(region_rwlock); + if (!get_enabled()) { return Vector3(); } @@ -114,6 +129,8 @@ Vector3 NavRegion::get_random_point(uint32_t p_navigation_layers, bool p_uniform } bool NavRegion::sync() { + RWLockWrite write_lock(region_rwlock); + bool something_changed = polygons_dirty /* || something_dirty? */; update_polygons(); diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h index 662a32c47a..c015802b92 100644 --- a/modules/navigation/nav_region.h +++ b/modules/navigation/nav_region.h @@ -38,6 +38,8 @@ #include "scene/resources/navigation_mesh.h" class NavRegion : public NavBase { + RWLock region_rwlock; + NavMap *map = nullptr; Transform3D transform; bool enabled = true; @@ -88,6 +90,8 @@ public: return polygons; } + Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const; + gd::ClosestPointQueryResult get_closest_point_info(const Vector3 &p_point) const; Vector3 get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const; real_t get_surface_area() const { return surface_area; }; diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index ff032c88c6..c89534a60c 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -390,6 +390,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackOggVorbis::get_sample_playback() con void AudioStreamPlaybackOggVorbis::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { sample_playback = p_playback; + if (sample_playback.is_valid()) { + sample_playback->stream_playback = Ref<AudioStreamPlayback>(this); + } } AudioStreamPlaybackOggVorbis::~AudioStreamPlaybackOggVorbis() { diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index f8ac591a78..d78f8db79c 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -2276,6 +2276,11 @@ String EditorExportPlatformAndroid::get_apksigner_path(int p_target_sdk, bool p_ bool failed = false; String version_to_use; + String java_sdk_path = EDITOR_GET("export/android/java_sdk_path"); + if (!java_sdk_path.is_empty()) { + OS::get_singleton()->set_environment("JAVA_HOME", java_sdk_path); + } + List<String> args; args.push_back("--version"); String output; diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt index 50daf44d2a..d296d6ad03 100644 --- a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt +++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt @@ -511,4 +511,12 @@ abstract class BaseGodotEditor : GodotActivity() { val godot = godot ?: return Error.ERR_UNCONFIGURED return verifyApk(godot.fileAccessHandler, apkPath) } + + override fun supportsFeature(featureTag: String): Boolean { + if (featureTag == "xr_editor") { + return isNativeXRDevice(); + } + + return false + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 38bd336e2d..5b1d09e749 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -992,6 +992,10 @@ class Godot(private val context: Context) { */ @Keep private fun hasFeature(feature: String): Boolean { + if (primaryHost?.supportsFeature(feature) ?: false) { + return true; + } + for (plugin in pluginRegistry.allPlugins) { if (plugin.supportsFeature(feature)) { return true diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java index e0f5744368..d60595c0bb 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java @@ -501,4 +501,12 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH } return Error.ERR_UNAVAILABLE; } + + @Override + public boolean supportsFeature(String featureTag) { + if (parentHost != null) { + return parentHost.supportsFeature(featureTag); + } + return false; + } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index f1c84e90a7..344b73f799 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -136,4 +136,13 @@ public interface GodotHost { default Error verifyApk(@NonNull String apkPath) { return Error.ERR_UNAVAILABLE; } + + /** + * Returns whether the given feature tag is supported. + * + * @see <a href="https://docs.godotengine.org/en/stable/tutorials/export/feature_tags.html">Feature tags</a> + */ + default boolean supportsFeature(String featureTag) { + return false; + } } diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 92ac921cee..684a7c34a0 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -666,7 +666,7 @@ def get_ar_version(env): print_warning("Couldn't check version of `ar`.") return ret - match = re.search(r"GNU ar \(GNU Binutils\) (\d+)\.(\d+)(:?\.(\d+))?", output) + match = re.search(r"GNU ar \(GNU Binutils\) (\d+)\.(\d+)(?:\.(\d+))?", output) if match: ret["major"] = int(match[1]) ret["minor"] = int(match[2]) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index f9c636a4a6..e6e8c6aaef 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -101,7 +101,7 @@ static String fix_path(const String &p_path) { } path = path.simplify_path(); path = path.replace("/", "\\"); - if (!path.is_network_share_path() && !path.begins_with(R"(\\?\)")) { + if (path.size() >= MAX_PATH && !path.is_network_share_path() && !path.begins_with(R"(\\?\)")) { path = R"(\\?\)" + path; } return path; diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 20d646fe1e..64259a24b0 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -216,24 +216,7 @@ void Path3D::_bind_methods() { ADD_SIGNAL(MethodInfo("curve_changed")); } -// Update transform, in deferred mode by default to avoid superfluity. -void PathFollow3D::update_transform(bool p_immediate) { - transform_dirty = true; - - if (p_immediate) { - _update_transform(); - } else { - callable_mp(this, &PathFollow3D::_update_transform).call_deferred(); - } -} - -// Update transform immediately . -void PathFollow3D::_update_transform() { - if (!transform_dirty) { - return; - } - transform_dirty = false; - +void PathFollow3D::update_transform() { if (!path) { return; } @@ -286,9 +269,7 @@ void PathFollow3D::_notification(int p_what) { Node *parent = get_parent(); if (parent) { path = Object::cast_to<Path3D>(parent); - if (path) { - update_transform(); - } + update_transform(); } } break; @@ -414,6 +395,9 @@ void PathFollow3D::_bind_methods() { void PathFollow3D::set_progress(real_t p_progress) { ERR_FAIL_COND(!isfinite(p_progress)); + if (progress == p_progress) { + return; + } progress = p_progress; if (path) { @@ -435,10 +419,11 @@ void PathFollow3D::set_progress(real_t p_progress) { } void PathFollow3D::set_h_offset(real_t p_h_offset) { - h_offset = p_h_offset; - if (path) { - update_transform(); + if (h_offset == p_h_offset) { + return; } + h_offset = p_h_offset; + update_transform(); } real_t PathFollow3D::get_h_offset() const { @@ -446,10 +431,11 @@ real_t PathFollow3D::get_h_offset() const { } void PathFollow3D::set_v_offset(real_t p_v_offset) { - v_offset = p_v_offset; - if (path) { - update_transform(); + if (v_offset == p_v_offset) { + return; } + v_offset = p_v_offset; + update_transform(); } real_t PathFollow3D::get_v_offset() const { @@ -476,6 +462,9 @@ real_t PathFollow3D::get_progress_ratio() const { } void PathFollow3D::set_rotation_mode(RotationMode p_rotation_mode) { + if (rotation_mode == p_rotation_mode) { + return; + } rotation_mode = p_rotation_mode; update_configuration_warnings(); @@ -487,6 +476,9 @@ PathFollow3D::RotationMode PathFollow3D::get_rotation_mode() const { } void PathFollow3D::set_use_model_front(bool p_use_model_front) { + if (use_model_front == p_use_model_front) { + return; + } use_model_front = p_use_model_front; update_transform(); } @@ -496,6 +488,9 @@ bool PathFollow3D::is_using_model_front() const { } void PathFollow3D::set_loop(bool p_loop) { + if (loop == p_loop) { + return; + } loop = p_loop; update_transform(); } @@ -505,6 +500,9 @@ bool PathFollow3D::has_loop() const { } void PathFollow3D::set_tilt_enabled(bool p_enabled) { + if (tilt_enabled == p_enabled) { + return; + } tilt_enabled = p_enabled; update_transform(); } diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 0c9111bb8e..fb4f301375 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -90,7 +90,6 @@ protected: void _validate_property(PropertyInfo &p_property) const; void _notification(int p_what); - void _update_transform(); static void _bind_methods(); @@ -124,7 +123,7 @@ public: PackedStringArray get_configuration_warnings() const override; - void update_transform(bool p_immediate = false); + void update_transform(); static Transform3D correct_posture(Transform3D p_transform, PathFollow3D::RotationMode p_rotation_mode); diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 1c5f40f56e..664302d45e 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -1636,6 +1636,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } if (t_obj->call(SNAME("get_is_sample"))) { + if (t->audio_stream_playback->get_sample_playback().is_valid()) { + AudioServer::get_singleton()->stop_sample_playback(t->audio_stream_playback->get_sample_playback()); + } Ref<AudioSamplePlayback> sample_playback; sample_playback.instantiate(); sample_playback->stream = stream; diff --git a/scene/animation/animation_player.compat.inc b/scene/animation/animation_player.compat.inc index 39efacc4ca..974eb2a7d8 100644 --- a/scene/animation/animation_player.compat.inc +++ b/scene/animation/animation_player.compat.inc @@ -58,14 +58,6 @@ void AnimationPlayer::_seek_bind_compat_80813(double p_time, bool p_update) { seek(p_time, p_update, false); } -void AnimationPlayer::_play_compat_84906(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) { - play(p_name, p_custom_blend, p_custom_scale, p_from_end); -} - -void AnimationPlayer::_play_backwards_compat_84906(const StringName &p_name, double p_custom_blend) { - play_backwards(p_name, p_custom_blend); -} - void AnimationPlayer::_bind_compatibility_methods() { ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &AnimationPlayer::_set_process_callback_bind_compat_80813); ClassDB::bind_method(D_METHOD("get_process_callback"), &AnimationPlayer::_get_process_callback_bind_compat_80813); @@ -74,8 +66,6 @@ void AnimationPlayer::_bind_compatibility_methods() { ClassDB::bind_method(D_METHOD("set_root", "path"), &AnimationPlayer::_set_root_bind_compat_80813); ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::_get_root_bind_compat_80813); ClassDB::bind_compatibility_method(D_METHOD("seek", "seconds", "update"), &AnimationPlayer::_seek_bind_compat_80813, DEFVAL(false)); - ClassDB::bind_compatibility_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::_play_compat_84906, DEFVAL(""), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false)); - ClassDB::bind_compatibility_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::_play_backwards_compat_84906, DEFVAL(""), DEFVAL(-1)); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_MANUAL); diff --git a/scene/gui/control.compat.inc b/scene/gui/control.compat.inc deleted file mode 100644 index 96ee720d90..0000000000 --- a/scene/gui/control.compat.inc +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************/ -/* control.compat.inc */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef DISABLE_DEPRECATED - -void Control::_bind_compatibility_methods() { - ClassDB::bind_compatibility_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Control::get_theme_icon, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Control::get_theme_stylebox, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_font", "name", "theme_type"), &Control::get_theme_font, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Control::get_theme_font_size, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_color", "name", "theme_type"), &Control::get_theme_color, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Control::get_theme_constant, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Control::has_theme_icon, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Control::has_theme_stylebox, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_font", "name", "theme_type"), &Control::has_theme_font, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Control::has_theme_font_size, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_color", "name", "theme_type"), &Control::has_theme_color, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Control::has_theme_constant, DEFVAL("")); -} - -#endif diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index e030828595..cecddebe88 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "control.h" -#include "control.compat.inc" #include "container.h" #include "core/config/project_settings.h" diff --git a/scene/gui/control.h b/scene/gui/control.h index c784d4330d..2655b14562 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -348,10 +348,6 @@ protected: void _notification(int p_notification); static void _bind_methods(); -#ifndef DISABLE_DEPRECATED - static void _bind_compatibility_methods(); -#endif - // Exposed virtual methods. GDVIRTUAL1RC(bool, _has_point, Vector2) diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index c2818edd9c..3b5d4fc33e 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -45,6 +45,70 @@ #include "editor/editor_settings.h" #endif +void LineEdit::_edit() { + if (!is_inside_tree()) { + return; + } + + if (!has_focus()) { + grab_focus(); + } + + if (!editable || editing) { + return; + } + + editing = true; + _validate_caret_can_draw(); + + DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; + if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { + DisplayServer::get_singleton()->window_set_ime_active(true, wid); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2) + get_global_position(); + if (get_window()->get_embedder()) { + pos += get_viewport()->get_popup_base_transform().get_origin(); + } + DisplayServer::get_singleton()->window_set_ime_position(pos, wid); + } + + show_virtual_keyboard(); + queue_redraw(); + emit_signal(SNAME("editing_toggled"), true); +} + +void LineEdit::_unedit() { + if (!editing) { + return; + } + + editing = false; + _validate_caret_can_draw(); + + DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; + if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { + DisplayServer::get_singleton()->window_set_ime_position(Point2(), wid); + DisplayServer::get_singleton()->window_set_ime_active(false, wid); + } + ime_text = ""; + ime_selection = Point2(); + _shape(); + set_caret_column(caret_column); // Update scroll_offset. + + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { + DisplayServer::get_singleton()->virtual_keyboard_hide(); + } + + if (deselect_on_focus_loss_enabled && !selection.drag_attempt) { + deselect(); + } + + emit_signal(SNAME("editing_toggled"), false); +} + +bool LineEdit::is_editing() const { + return editing; +} + void LineEdit::_swap_current_input_direction() { if (input_direction == TEXT_DIRECTION_LTR) { input_direction = TEXT_DIRECTION_RTL; @@ -52,7 +116,6 @@ void LineEdit::_swap_current_input_direction() { input_direction = TEXT_DIRECTION_LTR; } set_caret_column(get_caret_column()); - queue_redraw(); } void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) { @@ -240,6 +303,11 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { } void LineEdit::unhandled_key_input(const Ref<InputEvent> &p_event) { + // Return to prevent editing if just focused. + if (!editing) { + return; + } + Ref<InputEventKey> k = p_event; if (k.is_valid()) { @@ -265,26 +333,38 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> b = p_event; - if (b.is_valid()) { - if (ime_text.length() != 0) { - // Ignore mouse clicks in IME input mode. - return; - } - if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) { - _update_context_menu(); - menu->set_position(get_screen_position() + get_local_mouse_position()); - menu->reset_size(); - menu->popup(); - grab_focus(); + // Ignore mouse clicks in IME input mode. + if (b.is_valid() && ime_text.is_empty()) { + if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT) { + if (editable && !selection.enabled) { + set_caret_at_pixel_pos(b->get_position().x); + } + + if (context_menu_enabled) { + _update_context_menu(); + menu->set_position(get_screen_position() + get_local_mouse_position()); + menu->reset_size(); + menu->popup(); + } + + if (editable && !editing) { + _edit(); + } + accept_event(); return; } - if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MouseButton::MIDDLE && is_editable() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { + if (editable && is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MouseButton::MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary().strip_escapes(); deselect(); set_caret_at_pixel_pos(b->get_position().x); + + if (!editing) { + _edit(); + } + if (!paste_buffer.is_empty()) { insert_text_at_caret(paste_buffer); @@ -295,7 +375,6 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { text_changed_dirty = true; } } - grab_focus(); accept_event(); return; } @@ -304,10 +383,13 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { return; } - _reset_caret_blink_timer(); + if (editing) { + _reset_caret_blink_timer(); + } + if (b->is_pressed()) { accept_event(); // Don't pass event further when clicked on text field. - if (!text.is_empty() && is_editable() && _is_over_clear_button(b->get_position())) { + if (editable && !text.is_empty() && _is_over_clear_button(b->get_position())) { clear_button_status.press_attempt = true; clear_button_status.pressing_inside = true; queue_redraw(); @@ -330,7 +412,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { const int triple_click_tolerance = 5; const bool is_triple_click = !b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && b->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance; - if (is_triple_click && text.length()) { + if (is_triple_click && !text.is_empty()) { // Triple-click select all. selection.enabled = true; selection.begin = 0; @@ -377,13 +459,17 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { } } + if (editable && !editing) { + _edit(); + return; + } queue_redraw(); } else { if (selection.enabled && !pass && b->get_button_index() == MouseButton::LEFT && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) { DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); } - if (!text.is_empty() && is_editable() && clear_button_enabled) { + if (editable && !text.is_empty() && clear_button_enabled) { bool press_attempt = clear_button_status.press_attempt; clear_button_status.press_attempt = false; if (press_attempt && clear_button_status.pressing_inside && _is_over_clear_button(b->get_position())) { @@ -416,7 +502,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { - if (!text.is_empty() && is_editable() && clear_button_enabled) { + if (editable && !text.is_empty() && clear_button_enabled) { bool last_press_inside = clear_button_status.pressing_inside; clear_button_status.pressing_inside = clear_button_status.press_attempt && _is_over_clear_button(m->get_position()); if (last_press_inside != clear_button_status.pressing_inside) { @@ -462,221 +548,240 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; - if (k.is_valid()) { - if (!k->is_pressed()) { - if (alt_start && k->get_keycode() == Key::ALT) { - alt_start = false; - if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { - char32_t ucodestr[2] = { (char32_t)alt_code, 0 }; - insert_text_at_caret(ucodestr); - } - accept_event(); - return; - } - return; - } - - // Alt + Unicode input: - if (k->is_alt_pressed()) { - if (!alt_start) { - if (k->get_keycode() == Key::KP_ADD) { - alt_start = true; - alt_code = 0; - accept_event(); - return; - } - } else { - if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); - } - if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); - } - if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { - alt_code = alt_code << 4; - alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; - } - accept_event(); - return; - } - } + if (k.is_null()) { + return; + } - if (context_menu_enabled) { - if (k->is_action("ui_menu", true)) { - _update_context_menu(); - Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); - menu->set_position(get_screen_position() + pos); - menu->reset_size(); - menu->popup(); - menu->grab_focus(); + if (editable && !editing && k->is_action_pressed("ui_text_submit", false)) { + _edit(); + return; + } - accept_event(); - return; - } - } + if (!editing) { + return; + } - // Default is ENTER and KP_ENTER. Cannot use ui_accept as default includes SPACE. - if (k->is_action("ui_text_submit", false)) { - emit_signal(SNAME("text_submitted"), text); - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { - DisplayServer::get_singleton()->virtual_keyboard_hide(); + if (!k->is_pressed()) { + if (alt_start && k->get_keycode() == Key::ALT) { + alt_start = false; + if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { + char32_t ucodestr[2] = { (char32_t)alt_code, 0 }; + insert_text_at_caret(ucodestr); } accept_event(); return; } + return; + } - if (k->is_action("ui_cancel")) { - callable_mp((Control *)this, &Control::release_focus).call_deferred(); - accept_event(); - return; - } - - if (is_shortcut_keys_enabled()) { - if (k->is_action("ui_copy", true)) { - copy_text(); + // Alt + Unicode input: + if (k->is_alt_pressed()) { + if (!alt_start) { + if (k->get_keycode() == Key::KP_ADD) { + alt_start = true; + alt_code = 0; accept_event(); return; } - - if (k->is_action("ui_text_select_all", true)) { - select(); - accept_event(); - return; + } else { + if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); } - - // Cut / Paste - if (k->is_action("ui_cut", true)) { - cut_text(); - accept_event(); - return; + if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); } - - if (k->is_action("ui_paste", true)) { - paste_text(); - accept_event(); - return; + if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; } - - // Undo / Redo - if (k->is_action("ui_undo", true)) { - undo(); - accept_event(); - return; - } - - if (k->is_action("ui_redo", true)) { - redo(); - accept_event(); - return; - } - } - - // BACKSPACE - if (k->is_action("ui_text_backspace_all_to_left", true)) { - _backspace(false, true); - accept_event(); - return; - } - if (k->is_action("ui_text_backspace_word", true)) { - _backspace(true); - accept_event(); - return; - } - if (k->is_action("ui_text_backspace", true)) { - _backspace(); accept_event(); return; } + } + + if (context_menu_enabled) { + if (k->is_action("ui_menu", true)) { + _update_context_menu(); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); + menu->set_position(get_screen_position() + pos); + menu->reset_size(); + menu->popup(); + menu->grab_focus(); - // DELETE - if (k->is_action("ui_text_delete_all_to_right", true)) { - _delete(false, true); - accept_event(); - return; - } - if (k->is_action("ui_text_delete_word", true)) { - _delete(true); accept_event(); return; } - if (k->is_action("ui_text_delete", true)) { - _delete(); - accept_event(); - return; + } + + // Default is ENTER and KP_ENTER. Cannot use ui_accept as default includes SPACE. + if (k->is_action_pressed("ui_text_submit")) { + emit_signal(SNAME("text_submitted"), text); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { + DisplayServer::get_singleton()->virtual_keyboard_hide(); } - // Cursor Movement + if (editing) { + _unedit(); + } - k = k->duplicate(); - bool shift_pressed = k->is_shift_pressed(); - // Remove shift or else actions will not match. Use above variable for selection. - k->set_shift_pressed(false); + accept_event(); + return; + } - if (k->is_action("ui_text_caret_word_left", true)) { - _move_caret_left(shift_pressed, true); - accept_event(); - return; + if (k->is_action("ui_cancel")) { + if (editing) { + _unedit(); } - if (k->is_action("ui_text_caret_left", true)) { - _move_caret_left(shift_pressed); + + accept_event(); + return; + } + + if (is_shortcut_keys_enabled()) { + if (k->is_action("ui_copy", true)) { + copy_text(); accept_event(); return; } - if (k->is_action("ui_text_caret_word_right", true)) { - _move_caret_right(shift_pressed, true); + + if (k->is_action("ui_text_select_all", true)) { + select(); accept_event(); return; } - if (k->is_action("ui_text_caret_right", true)) { - _move_caret_right(shift_pressed, false); + + // Cut / Paste + if (k->is_action("ui_cut", true)) { + cut_text(); accept_event(); return; } - // Up = Home, Down = End - if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) { - _move_caret_start(shift_pressed); + if (k->is_action("ui_paste", true)) { + paste_text(); accept_event(); return; } - if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) { - _move_caret_end(shift_pressed); + + // Undo / Redo + if (k->is_action("ui_undo", true)) { + undo(); accept_event(); return; } - // Misc - if (k->is_action("ui_swap_input_direction", true)) { - _swap_current_input_direction(); + if (k->is_action("ui_redo", true)) { + redo(); accept_event(); return; } + } - _reset_caret_blink_timer(); + // BACKSPACE + if (k->is_action("ui_text_backspace_all_to_left", true)) { + _backspace(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_backspace_word", true)) { + _backspace(true); + accept_event(); + return; + } + if (k->is_action("ui_text_backspace", true)) { + _backspace(); + accept_event(); + return; + } - // Allow unicode handling if: - // * No Modifiers are pressed (except shift) - bool allow_unicode_handling = !(k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); + // DELETE + if (k->is_action("ui_text_delete_all_to_right", true)) { + _delete(false, true); + accept_event(); + return; + } + if (k->is_action("ui_text_delete_word", true)) { + _delete(true); + accept_event(); + return; + } + if (k->is_action("ui_text_delete", true)) { + _delete(); + accept_event(); + return; + } - if (allow_unicode_handling && editable && k->get_unicode() >= 32) { - // Handle Unicode if no modifiers are active. - selection_delete(); - char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; - int prev_len = text.length(); - insert_text_at_caret(ucodestr); - if (text.length() != prev_len) { - if (!text_changed_dirty) { - if (is_inside_tree()) { - callable_mp(this, &LineEdit::_text_changed).call_deferred(); - } - text_changed_dirty = true; + // Cursor Movement + + k = k->duplicate(); + bool shift_pressed = k->is_shift_pressed(); + // Remove shift or else actions will not match. Use above variable for selection. + k->set_shift_pressed(false); + + if (k->is_action("ui_text_caret_word_left", true)) { + _move_caret_left(shift_pressed, true); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_left", true)) { + _move_caret_left(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_word_right", true)) { + _move_caret_right(shift_pressed, true); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_right", true)) { + _move_caret_right(shift_pressed, false); + accept_event(); + return; + } + + // Up = Home, Down = End + if (k->is_action("ui_text_caret_up", true) || k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_page_up", true)) { + _move_caret_start(shift_pressed); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_down", true) || k->is_action("ui_text_caret_line_end", true) || k->is_action("ui_text_caret_page_down", true)) { + _move_caret_end(shift_pressed); + accept_event(); + return; + } + + // Misc + if (k->is_action("ui_swap_input_direction", true)) { + _swap_current_input_direction(); + accept_event(); + return; + } + + _reset_caret_blink_timer(); + + // Allow unicode handling if: + // * No Modifiers are pressed (except shift) + bool allow_unicode_handling = !(k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); + + if (allow_unicode_handling && editable && k->get_unicode() >= 32) { + // Handle Unicode if no modifiers are active. + selection_delete(); + char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; + int prev_len = text.length(); + insert_text_at_caret(ucodestr); + if (text.length() != prev_len) { + if (!text_changed_dirty) { + if (is_inside_tree()) { + callable_mp(this, &LineEdit::_text_changed).call_deferred(); } + text_changed_dirty = true; } - accept_event(); - return; } + accept_event(); + return; } } @@ -1107,7 +1212,7 @@ void LineEdit::_notification(int p_what) { } } - if (has_focus()) { + if (editing) { DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, wid); @@ -1121,8 +1226,6 @@ void LineEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_ENTER: { - _validate_caret_can_draw(); - if (select_all_on_focus) { if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { // Select all when the mouse button is up. @@ -1132,43 +1235,20 @@ void LineEdit::_notification(int p_what) { } } - DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; - if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { - DisplayServer::get_singleton()->window_set_ime_active(true, wid); - Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2) + get_global_position(); - if (get_window()->get_embedder()) { - pos += get_viewport()->get_popup_base_transform().get_origin(); - } - DisplayServer::get_singleton()->window_set_ime_position(pos, wid); + // Only allow editing if the LineEdit is not focused with arrow keys. + if (!(Input::get_singleton()->is_action_pressed("ui_up") || Input::get_singleton()->is_action_pressed("ui_down") || Input::get_singleton()->is_action_pressed("ui_left") || Input::get_singleton()->is_action_pressed("ui_right"))) { + _edit(); } - - show_virtual_keyboard(); } break; case NOTIFICATION_FOCUS_EXIT: { - _validate_caret_can_draw(); - - DisplayServer::WindowID wid = get_window() ? get_window()->get_window_id() : DisplayServer::INVALID_WINDOW_ID; - if (wid != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { - DisplayServer::get_singleton()->window_set_ime_position(Point2(), wid); - DisplayServer::get_singleton()->window_set_ime_active(false, wid); - } - ime_text = ""; - ime_selection = Point2(); - _shape(); - set_caret_column(caret_column); // Update scroll_offset. - - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { - DisplayServer::get_singleton()->virtual_keyboard_hide(); - } - - if (deselect_on_focus_loss_enabled && !selection.drag_attempt) { - deselect(); + if (editing) { + _unedit(); } } break; case MainLoop::NOTIFICATION_OS_IME_UPDATE: { - if (has_focus()) { + if (editing) { ime_text = DisplayServer::get_singleton()->ime_get_text(); ime_selection = DisplayServer::get_singleton()->ime_get_selection(); @@ -1178,8 +1258,6 @@ void LineEdit::_notification(int p_what) { _shape(); set_caret_column(caret_column); // Update scroll_offset. - - queue_redraw(); } } break; @@ -1419,7 +1497,7 @@ Vector2 LineEdit::get_caret_pixel_pos() { Vector2 ret; CaretInfo caret; // Get position of the start of caret. - if (ime_text.length() != 0 && ime_selection.x != 0) { + if (!ime_text.is_empty() && ime_selection.x != 0) { caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x); } else { caret = TS->shaped_text_get_carets(text_rid, caret_column); @@ -1432,7 +1510,7 @@ Vector2 LineEdit::get_caret_pixel_pos() { } // Get position of the end of caret. - if (ime_text.length() != 0) { + if (!ime_text.is_empty()) { if (ime_selection.y != 0) { caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y); } else { @@ -1525,11 +1603,11 @@ void LineEdit::_validate_caret_can_draw() { draw_caret = true; caret_blink_timer = 0.0; } - caret_can_draw = editable && (window_has_focus || (menu && menu->has_focus())) && (has_focus() || caret_force_displayed); + caret_can_draw = editing && (window_has_focus || (menu && menu->has_focus())) && (has_focus() || caret_force_displayed); } void LineEdit::delete_char() { - if ((text.length() <= 0) || (caret_column == 0)) { + if (text.is_empty() || caret_column == 0) { return; } @@ -1661,7 +1739,7 @@ void LineEdit::clear() { _text_changed(); // This should reset virtual keyboard state if needed. - if (has_focus()) { + if (editing) { show_virtual_keyboard(); } } @@ -1834,7 +1912,8 @@ Size2 LineEdit::get_minimum_size() const { Size2 min_size; // Minimum size of text. - float em_space_size = font->get_char_size('M', font_size).x; + // W is wider than M in most fonts, Using M may result in hiding the last digit when using float values in SpinBox, ie. ColorPicker RAW values. + float em_space_size = font->get_char_size('W', font_size).x; min_size.width = theme_cache.minimum_character_width * em_space_size; if (expand_to_text_length) { @@ -1932,7 +2011,8 @@ void LineEdit::select_all() { return; } - if (!text.length()) { + if (text.is_empty()) { + set_caret_column(0); return; } @@ -1948,6 +2028,10 @@ void LineEdit::set_editable(bool p_editable) { } editable = p_editable; + + if (!editable && editing) { + _unedit(); + } _validate_caret_can_draw(); update_minimum_size(); @@ -2328,6 +2412,7 @@ void LineEdit::_emit_text_change() { emit_signal(SceneStringName(text_changed), text); text_changed_dirty = false; } + PackedStringArray LineEdit::get_configuration_warnings() const { PackedStringArray warnings = Control::get_configuration_warnings(); if (secret_character.length() > 1) { @@ -2347,13 +2432,13 @@ void LineEdit::_shape() { TS->shaped_text_clear(text_rid); String t; - if (text.length() == 0 && ime_text.length() == 0) { + if (text.is_empty() && ime_text.is_empty()) { t = placeholder_translated; } else if (pass) { - String s = (secret_character.length() > 0) ? secret_character.left(1) : U"•"; + String s = secret_character.is_empty() ? U"•" : secret_character.left(1); t = s.repeat(text.length() + ime_text.length()); } else { - if (ime_text.length() > 0) { + if (!ime_text.is_empty()) { t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length()); } else { t = text; @@ -2562,6 +2647,7 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &LineEdit::set_horizontal_alignment); ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &LineEdit::get_horizontal_alignment); + ClassDB::bind_method(D_METHOD("is_editing"), &LineEdit::is_editing); ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear); ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all); @@ -2642,6 +2728,7 @@ void LineEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring"))); ADD_SIGNAL(MethodInfo("text_submitted", PropertyInfo(Variant::STRING, "new_text"))); + ADD_SIGNAL(MethodInfo("editing_toggled", PropertyInfo(Variant::BOOL, "toggled_on"))); BIND_ENUM_CONSTANT(MENU_CUT); BIND_ENUM_CONSTANT(MENU_COPY); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 993bc727e4..984512745a 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -86,6 +86,7 @@ public: private: HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; + bool editing = false; bool editable = false; bool pass = false; bool text_changed_dirty = false; @@ -205,6 +206,9 @@ private: float base_scale = 1.0; } theme_cache; + void _edit(); + void _unedit(); + void _clear_undo_stack(); void _clear_redo(); void _create_undo_state(); @@ -257,6 +261,8 @@ protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; public: + bool is_editing() const; + void set_horizontal_alignment(HorizontalAlignment p_alignment); HorizontalAlignment get_horizontal_alignment() const; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index ac81f0de56..01c2b9bffe 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -46,7 +46,7 @@ void SpinBox::_update_text(bool p_keep_line_edit) { value = TS->format_number(value); } - if (!line_edit->has_focus()) { + if (!line_edit->is_editing()) { if (!prefix.is_empty()) { value = prefix + " " + value; } @@ -197,13 +197,13 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { } } break; case MouseButton::WHEEL_UP: { - if (line_edit->has_focus()) { + if (line_edit->is_editing()) { set_value(get_value() + step * mb->get_factor()); accept_event(); } } break; case MouseButton::WHEEL_DOWN: { - if (line_edit->has_focus()) { + if (line_edit->is_editing()) { set_value(get_value() - step * mb->get_factor()); accept_event(); } @@ -253,34 +253,26 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { } } -void SpinBox::_line_edit_focus_enter() { - int col = line_edit->get_caret_column(); - _update_text(); - line_edit->set_caret_column(col); +void SpinBox::_line_edit_editing_toggled(bool p_toggled_on) { + if (p_toggled_on) { + int col = line_edit->get_caret_column(); + _update_text(); + line_edit->set_caret_column(col); - // LineEdit text might change and it clears any selection. Have to re-select here. - if (line_edit->is_select_all_on_focus() && !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { - line_edit->select_all(); - } -} + // LineEdit text might change and it clears any selection. Have to re-select here. + if (line_edit->is_select_all_on_focus() && !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { + line_edit->select_all(); + } + } else { + // Discontinue because the focus_exit was caused by canceling. + if (Input::get_singleton()->is_action_pressed("ui_cancel")) { + _update_text(); + return; + } -void SpinBox::_line_edit_focus_exit() { - // Discontinue because the focus_exit was caused by left-clicking the arrows. - const Viewport *viewport = get_viewport(); - if (!viewport || viewport->gui_get_focus_owner() == get_line_edit()) { - return; - } - // Discontinue because the focus_exit was caused by right-click context menu. - if (line_edit->is_menu_visible()) { - return; - } - // Discontinue because the focus_exit was caused by canceling. - if (Input::get_singleton()->is_action_pressed("ui_cancel")) { - _update_text(); - return; + line_edit->deselect(); + _text_submitted(line_edit->get_text()); } - - _text_submitted(line_edit->get_text()); } inline void SpinBox::_compute_sizes() { @@ -602,8 +594,7 @@ SpinBox::SpinBox() { line_edit->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT); line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), CONNECT_DEFERRED); - line_edit->connect(SceneStringName(focus_entered), callable_mp(this, &SpinBox::_line_edit_focus_enter), CONNECT_DEFERRED); - line_edit->connect(SceneStringName(focus_exited), callable_mp(this, &SpinBox::_line_edit_focus_exit), CONNECT_DEFERRED); + line_edit->connect("editing_toggled", callable_mp(this, &SpinBox::_line_edit_editing_toggled), CONNECT_DEFERRED); line_edit->connect(SceneStringName(gui_input), callable_mp(this, &SpinBox::_line_edit_input)); range_click_timer = memnew(Timer); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 592805f43a..294dc3e5d5 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -86,8 +86,7 @@ class SpinBox : public Range { bool down_button_disabled = false; } state_cache; - void _line_edit_focus_enter(); - void _line_edit_focus_exit(); + void _line_edit_editing_toggled(bool p_toggled_on); inline void _compute_sizes(); inline int _get_widest_button_icon_width(); diff --git a/scene/main/window.compat.inc b/scene/main/window.compat.inc deleted file mode 100644 index 0bba01bb1b..0000000000 --- a/scene/main/window.compat.inc +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************/ -/* window.compat.inc */ -/**************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/**************************************************************************/ - -#ifndef DISABLE_DEPRECATED - -void Window::_bind_compatibility_methods() { - ClassDB::bind_compatibility_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Window::get_theme_icon, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Window::get_theme_stylebox, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_font", "name", "theme_type"), &Window::get_theme_font, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Window::get_theme_font_size, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_color", "name", "theme_type"), &Window::get_theme_color, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Window::get_theme_constant, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Window::has_theme_icon, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Window::has_theme_stylebox, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_font", "name", "theme_type"), &Window::has_theme_font, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Window::has_theme_font_size, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL("")); - ClassDB::bind_compatibility_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL("")); -} - -#endif diff --git a/scene/main/window.cpp b/scene/main/window.cpp index aaa34a4840..dafbbb867b 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "window.h" -#include "window.compat.inc" #include "core/config/project_settings.h" #include "core/debugger/engine_debugger.h" diff --git a/scene/main/window.h b/scene/main/window.h index 33d593711f..84d2febe51 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -247,10 +247,6 @@ protected: void _notification(int p_what); static void _bind_methods(); -#ifndef DISABLE_DEPRECATED - static void _bind_compatibility_methods(); -#endif - bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; diff --git a/scene/resources/audio_stream_polyphonic.cpp b/scene/resources/audio_stream_polyphonic.cpp index 999b0c9f0a..c7b8b1c723 100644 --- a/scene/resources/audio_stream_polyphonic.cpp +++ b/scene/resources/audio_stream_polyphonic.cpp @@ -247,6 +247,11 @@ AudioStreamPlaybackPolyphonic::ID AudioStreamPlaybackPolyphonic::play_stream(con sp->volume_vector.write[2] = AudioFrame(linear_volume, linear_volume); sp->volume_vector.write[3] = AudioFrame(linear_volume, linear_volume); sp->bus = p_bus; + + if (streams[i].stream_playback->get_sample_playback().is_valid()) { + AudioServer::get_singleton()->stop_playback_stream(sp); + } + streams[i].stream_playback->set_sample_playback(sp); AudioServer::get_singleton()->start_sample_playback(sp); } @@ -315,6 +320,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackPolyphonic::get_sample_playback() co void AudioStreamPlaybackPolyphonic::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { sample_playback = p_playback; + if (sample_playback.is_valid()) { + sample_playback->stream_playback = Ref<AudioStreamPlayback>(this); + } } void AudioStreamPlaybackPolyphonic::_bind_methods() { diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp index d0e0f0eef3..de67a93bd1 100644 --- a/scene/resources/audio_stream_wav.cpp +++ b/scene/resources/audio_stream_wav.cpp @@ -475,6 +475,9 @@ Ref<AudioSamplePlayback> AudioStreamPlaybackWAV::get_sample_playback() const { void AudioStreamPlaybackWAV::set_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { sample_playback = p_playback; + if (sample_playback.is_valid()) { + sample_playback->stream_playback = Ref<AudioStreamPlayback>(this); + } } AudioStreamPlaybackWAV::AudioStreamPlaybackWAV() {} diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 29f9835ba9..d531eea311 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -612,27 +612,29 @@ Error ResourceLoaderText::load() { } } - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = res->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + if (ClassDB::has_property(res->get_class_name(), assign)) { + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = res->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + } } } - } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = res->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = res->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + } } } } @@ -752,27 +754,29 @@ Error ResourceLoaderText::load() { } } - if (value.get_type() == Variant::ARRAY) { - Array set_array = value; - bool is_get_valid = false; - Variant get_value = resource->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::ARRAY) { - Array get_array = get_value; - if (!set_array.is_same_typed(get_array)) { - value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + if (ClassDB::has_property(resource->get_class_name(), assign)) { + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = resource->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + } } } - } - if (value.get_type() == Variant::DICTIONARY) { - Dictionary set_dict = value; - bool is_get_valid = false; - Variant get_value = resource->get(assign, &is_get_valid); - if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { - Dictionary get_dict = get_value; - if (!set_dict.is_same_typed(get_dict)) { - value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), - get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + if (value.get_type() == Variant::DICTIONARY) { + Dictionary set_dict = value; + bool is_get_valid = false; + Variant get_value = resource->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) { + Dictionary get_dict = get_value; + if (!set_dict.is_same_typed(get_dict)) { + value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(), + get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script()); + } } } } diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index c41545aeba..7f2b68a796 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -48,6 +48,7 @@ class AudioSamplePlayback : public RefCounted { public: Ref<AudioStream> stream; + Ref<AudioStreamPlayback> stream_playback; float offset = 0.0f; float pitch_scale = 1.0; diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 332f8984a2..e06079efe8 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -501,12 +501,7 @@ void AudioServer::_mix_step() { switch (playback->state.load()) { case AudioStreamPlaybackListNode::AWAITING_DELETION: case AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION: - playback_list.erase(playback, [](AudioStreamPlaybackListNode *p) { - delete p->prev_bus_details; - delete p->bus_details.load(); - p->stream_playback.unref(); - delete p; - }); + _delete_stream_playback_list_node(playback); break; case AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE: { // Pause the stream. @@ -697,6 +692,23 @@ AudioServer::AudioStreamPlaybackListNode *AudioServer::_find_playback_list_node( return nullptr; } +void AudioServer::_delete_stream_playback(Ref<AudioStreamPlayback> p_playback) { + ERR_FAIL_COND(p_playback.is_null()); + AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); + if (playback_node) { + _delete_stream_playback_list_node(playback_node); + } +} + +void AudioServer::_delete_stream_playback_list_node(AudioStreamPlaybackListNode *p_playback_node) { + playback_list.erase(p_playback_node, [](AudioStreamPlaybackListNode *p) { + delete p->prev_bus_details; + delete p->bus_details.load(); + p->stream_playback.unref(); + delete p; + }); +} + bool AudioServer::thread_has_channel_mix_buffer(int p_bus, int p_buffer) const { if (p_bus < 0 || p_bus >= buses.size()) { return false; @@ -1227,8 +1239,12 @@ void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) { ERR_FAIL_COND(p_playback.is_null()); // Handle sample playback. - if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) { - AudioServer::get_singleton()->stop_sample_playback(p_playback->get_sample_playback()); + if (p_playback->get_is_sample()) { + if (p_playback->get_sample_playback().is_valid()) { + AudioServer::get_singleton()->stop_sample_playback(p_playback->get_sample_playback()); + } else { + _delete_stream_playback(p_playback); + } return; } @@ -1370,8 +1386,12 @@ void AudioServer::set_playback_highshelf_params(Ref<AudioStreamPlayback> p_playb bool AudioServer::is_playback_active(Ref<AudioStreamPlayback> p_playback) { ERR_FAIL_COND_V(p_playback.is_null(), false); - if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) { - return sample_playback_list.has(p_playback->get_sample_playback()); + if (p_playback->get_is_sample()) { + if (p_playback->get_sample_playback().is_valid()) { + return sample_playback_list.has(p_playback->get_sample_playback()); + } else { + return false; + } } AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); @@ -1845,8 +1865,12 @@ void AudioServer::start_sample_playback(const Ref<AudioSamplePlayback> &p_playba void AudioServer::stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) { ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null."); - AudioDriver::get_singleton()->stop_sample_playback(p_playback); - sample_playback_list.erase(p_playback); + if (sample_playback_list.has(p_playback)) { + sample_playback_list.erase(p_playback); + AudioDriver::get_singleton()->stop_sample_playback(p_playback); + p_playback->stream_playback->set_sample_playback(nullptr); + stop_playback_stream(p_playback->stream_playback); + } } void AudioServer::set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) { diff --git a/servers/audio_server.h b/servers/audio_server.h index 2d6fc60860..16fcc029b3 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -297,6 +297,8 @@ private: SafeList<AudioStreamPlaybackListNode *> playback_list; SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard; + void _delete_stream_playback(Ref<AudioStreamPlayback> p_playback); + void _delete_stream_playback_list_node(AudioStreamPlaybackListNode *p_node); // TODO document if this is necessary. SafeList<AudioStreamPlaybackBusDetails *> bus_details_graveyard_frame_old; diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp index c5ce82265b..ceb5e909a3 100644 --- a/servers/navigation_server_2d.cpp +++ b/servers/navigation_server_2d.cpp @@ -86,6 +86,7 @@ void NavigationServer2D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer2D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer2D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("region_get_closest_point", "region", "to_point"), &NavigationServer2D::region_get_closest_point); ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer2D::region_get_random_point); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer2D::link_create); diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h index a8d9678a6f..250183300f 100644 --- a/servers/navigation_server_2d.h +++ b/servers/navigation_server_2d.h @@ -149,6 +149,7 @@ public: virtual Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; + virtual Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const = 0; virtual Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; /// Creates a new link between positions in the nav map. diff --git a/servers/navigation_server_2d_dummy.h b/servers/navigation_server_2d_dummy.h index 465cfcca98..0664b37ef0 100644 --- a/servers/navigation_server_2d_dummy.h +++ b/servers/navigation_server_2d_dummy.h @@ -83,6 +83,7 @@ public: int region_get_connections_count(RID p_region) const override { return 0; } Vector2 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector2(); } Vector2 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector2(); } + Vector2 region_get_closest_point(RID p_region, const Vector2 &p_point) const override { return Vector2(); } Vector2 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector2(); }; RID link_create() override { return RID(); } diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp index f4ffcf5a3e..572309c429 100644 --- a/servers/navigation_server_3d.cpp +++ b/servers/navigation_server_3d.cpp @@ -99,6 +99,9 @@ void NavigationServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("region_get_connections_count", "region"), &NavigationServer3D::region_get_connections_count); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_start", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_start); ClassDB::bind_method(D_METHOD("region_get_connection_pathway_end", "region", "connection"), &NavigationServer3D::region_get_connection_pathway_end); + ClassDB::bind_method(D_METHOD("region_get_closest_point_to_segment", "region", "start", "end", "use_collision"), &NavigationServer3D::region_get_closest_point_to_segment, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("region_get_closest_point", "region", "to_point"), &NavigationServer3D::region_get_closest_point); + ClassDB::bind_method(D_METHOD("region_get_closest_point_normal", "region", "to_point"), &NavigationServer3D::region_get_closest_point_normal); ClassDB::bind_method(D_METHOD("region_get_random_point", "region", "navigation_layers", "uniformly"), &NavigationServer3D::region_get_random_point); ClassDB::bind_method(D_METHOD("link_create"), &NavigationServer3D::link_create); diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h index cdacf8e7e4..6dbbd35648 100644 --- a/servers/navigation_server_3d.h +++ b/servers/navigation_server_3d.h @@ -168,6 +168,9 @@ public: virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const = 0; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const = 0; + virtual Vector3 region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const = 0; + virtual Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const = 0; + virtual Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const = 0; virtual Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const = 0; /// Creates a new link between positions in the nav map. diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h index 5c9e97d226..210c404365 100644 --- a/servers/navigation_server_3d_dummy.h +++ b/servers/navigation_server_3d_dummy.h @@ -93,6 +93,9 @@ public: int region_get_connections_count(RID p_region) const override { return 0; } Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override { return Vector3(); } Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override { return Vector3(); } + Vector3 region_get_closest_point_to_segment(RID p_region, const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const override { return Vector3(); } + Vector3 region_get_closest_point(RID p_region, const Vector3 &p_point) const override { return Vector3(); } + Vector3 region_get_closest_point_normal(RID p_region, const Vector3 &p_point) const override { return Vector3(); } Vector3 region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const override { return Vector3(); } RID link_create() override { return RID(); } diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h index ec19562147..b0953b5dce 100644 --- a/servers/rendering/dummy/storage/mesh_storage.h +++ b/servers/rendering/dummy/storage/mesh_storage.h @@ -163,7 +163,7 @@ public: virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override { return AABB(); } virtual RID _multimesh_get_mesh(RID p_multimesh) const override { return RID(); } - virtual AABB _multimesh_get_aabb(RID p_multimesh) const override { return AABB(); } + virtual AABB _multimesh_get_aabb(RID p_multimesh) override { return AABB(); } virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform3D(); } virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); } diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index 63dc54e24c..9f390c99f9 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -342,7 +342,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_ } } -_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) { +_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) { switch (type) { case ShaderLanguage::TYPE_BOOL: { uint32_t *gui = (uint32_t *)data; @@ -566,7 +566,7 @@ void MaterialStorage::ShaderData::set_default_texture_parameter(const StringName Variant MaterialStorage::ShaderData::get_default_parameter(const StringName &p_parameter) const { if (uniforms.has(p_parameter)) { ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + Vector<ShaderLanguage::Scalar> default_value = uniform.default_value; return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); } return Variant(); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 9bd62ba065..9ae39691dc 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -2041,7 +2041,7 @@ AABB MeshStorage::_multimesh_get_custom_aabb(RID p_multimesh) const { return multimesh->custom_aabb; } -AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) const { +AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_NULL_V(multimesh, AABB()); if (multimesh->custom_aabb != AABB()) { @@ -2049,7 +2049,7 @@ AABB MeshStorage::_multimesh_get_aabb(RID p_multimesh) const { } if (multimesh->aabb_dirty) { - const_cast<MeshStorage *>(this)->_update_dirty_multimeshes(); + _update_dirty_multimeshes(); } return multimesh->aabb; } diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 4344db783d..f811314fb6 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -652,7 +652,7 @@ public: virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override; virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override; - virtual AABB _multimesh_get_aabb(RID p_multimesh) const override; + virtual AABB _multimesh_get_aabb(RID p_multimesh) override; virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index f0f267c246..276cb4b210 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -1641,8 +1641,8 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye copy_region.texture_region_size.z = d; command_buffer_texture_copy_regions_vector.push_back(copy_region); - w = (w >> 1); - h = (h >> 1); + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); d = MAX(1u, d >> 1); } diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 49e005ca96..43703f8656 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -185,7 +185,7 @@ static String f2sp0(float p_float) { return num; } -static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) { +static String get_constant_text(SL::DataType p_type, const Vector<SL::Scalar> &p_values) { switch (p_type) { case SL::TYPE_BOOL: return p_values[0].boolean ? "true" : "false"; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 4eaf7fcb55..2249cd2010 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -1362,7 +1362,7 @@ void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, Iden } #endif // DEBUG_ENABLED -bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, ConstantNode::Value *r_constant_value) { +bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, Vector<Scalar> *r_constant_values) { if (is_shader_inc) { for (int i = 0; i < RenderingServer::SHADER_MAX; i++) { for (const KeyValue<StringName, FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) { @@ -1424,8 +1424,8 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_struct_name) { *r_struct_name = p_block->variables[p_identifier].struct_name; } - if (r_constant_value) { - *r_constant_value = p_block->variables[p_identifier].value; + if (r_constant_values && !p_block->variables[p_identifier].values.is_empty()) { + *r_constant_values = p_block->variables[p_identifier].values; } if (r_type) { *r_type = IDENTIFIER_LOCAL_VAR; @@ -1507,13 +1507,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_struct_name) { *r_struct_name = shader->constants[p_identifier].struct_name; } - if (r_constant_value) { - if (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->type == Node::NODE_TYPE_CONSTANT) { - ConstantNode *cnode = static_cast<ConstantNode *>(shader->constants[p_identifier].initializer); - - if (cnode->values.size() == 1) { - *r_constant_value = cnode->values[0]; - } + if (r_constant_values) { + if (shader->constants[p_identifier].initializer && !shader->constants[p_identifier].initializer->get_values().is_empty()) { + *r_constant_values = shader->constants[p_identifier].initializer->get_values(); } } if (r_type) { @@ -1544,7 +1540,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea return false; } -bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) { +bool ShaderLanguage::_validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) { bool valid = false; DataType ret_type = TYPE_VOID; int ret_size = 0; @@ -2007,9 +2003,384 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type if (r_ret_size) { *r_ret_size = ret_size; } + + if (valid && (!p_block || p_block->use_op_eval)) { + // Need to be placed here and not in the `_reduce_expression` because otherwise expressions like `1 + 2 / 2` will not work correctly. + valid = _eval_operator(p_block, p_op); + } + return valid; } +Vector<ShaderLanguage::Scalar> ShaderLanguage::_get_node_values(const BlockNode *p_block, Node *p_node) { + Vector<Scalar> result; + + switch (p_node->type) { + case Node::NODE_TYPE_VARIABLE: { + _find_identifier(p_block, false, FunctionInfo(), static_cast<VariableNode *>(p_node)->name, nullptr, nullptr, nullptr, nullptr, nullptr, &result); + } break; + default: { + result = p_node->get_values(); + } break; + } + + return result; +} + +bool ShaderLanguage::_eval_operator(const BlockNode *p_block, OperatorNode *p_op) { + bool is_valid = true; + + switch (p_op->op) { + case OP_EQUAL: + case OP_NOT_EQUAL: + case OP_LESS: + case OP_LESS_EQUAL: + case OP_GREATER: + case OP_GREATER_EQUAL: + case OP_AND: + case OP_OR: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_MOD: + case OP_SHIFT_LEFT: + case OP_SHIFT_RIGHT: + case OP_BIT_AND: + case OP_BIT_OR: + case OP_BIT_XOR: { + DataType a = p_op->arguments[0]->get_datatype(); + DataType b = p_op->arguments[1]->get_datatype(); + + bool is_op_vec_transform = false; + if (p_op->op == OP_MUL) { + DataType ta = a; + DataType tb = b; + + if (ta > tb) { + SWAP(ta, tb); + } + if (ta == TYPE_VEC2 && tb == TYPE_MAT2) { + is_op_vec_transform = true; + } else if (ta == TYPE_VEC3 && tb == TYPE_MAT3) { + is_op_vec_transform = true; + } else if (ta == TYPE_VEC4 && tb == TYPE_MAT4) { + is_op_vec_transform = true; + } + } + + Vector<Scalar> va = _get_node_values(p_block, p_op->arguments[0]); + Vector<Scalar> vb = _get_node_values(p_block, p_op->arguments[1]); + + if (is_op_vec_transform) { + p_op->values = _eval_vector_transform(va, vb, a, b, p_op->get_datatype()); + } else { + p_op->values = _eval_vector(va, vb, a, b, p_op->get_datatype(), p_op->op, is_valid); + } + } break; + case OP_NOT: + case OP_NEGATE: + case OP_BIT_INVERT: { + p_op->values = _eval_unary_vector(_get_node_values(p_block, p_op->arguments[0]), p_op->get_datatype(), p_op->op); + } break; + default: { + } break; + } + + return is_valid; +} + +ShaderLanguage::Scalar ShaderLanguage::_eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type) { + Scalar scalar; + + switch (p_op) { + case OP_NOT: { + scalar.boolean = !p_a.boolean; + } break; + case OP_NEGATE: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = -p_a.sint; + } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + // Intentionally wrap the unsigned int value, because GLSL does. + scalar.uint = 0 - p_a.uint; + } else { // float types + scalar.real = -scalar.real; + } + } break; + case OP_BIT_INVERT: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = ~p_a.sint; + } else { // uint types + scalar.uint = ~p_a.uint; + } + } break; + default: { + } break; + } + + return scalar; +} + +ShaderLanguage::Scalar ShaderLanguage::_eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid) { + Scalar scalar; + + switch (p_op) { + case OP_EQUAL: { + scalar.boolean = p_a.boolean == p_b.boolean; + } break; + case OP_NOT_EQUAL: { + scalar.boolean = p_a.boolean != p_b.boolean; + } break; + case OP_LESS: { + if (p_ret_type == TYPE_INT) { + scalar.boolean = p_a.sint < p_b.sint; + } else if (p_ret_type == TYPE_UINT) { + scalar.boolean = p_a.uint < p_b.uint; + } else { // float type + scalar.boolean = p_a.real < p_b.real; + } + } break; + case OP_LESS_EQUAL: { + if (p_ret_type == TYPE_INT) { + scalar.boolean = p_a.sint <= p_b.sint; + } else if (p_ret_type == TYPE_UINT) { + scalar.boolean = p_a.uint <= p_b.uint; + } else { // float type + scalar.boolean = p_a.real <= p_b.real; + } + } break; + case OP_GREATER: { + if (p_ret_type == TYPE_INT) { + scalar.boolean = p_a.sint > p_b.sint; + } else if (p_ret_type == TYPE_UINT) { + scalar.boolean = p_a.uint > p_b.uint; + } else { // float type + scalar.boolean = p_a.real > p_b.real; + } + } break; + case OP_GREATER_EQUAL: { + if (p_ret_type == TYPE_INT) { + scalar.boolean = p_a.sint >= p_b.sint; + } else if (p_ret_type == TYPE_UINT) { + scalar.boolean = p_a.uint >= p_b.uint; + } else { // float type + scalar.boolean = p_a.real >= p_b.real; + } + } break; + case OP_AND: { + scalar.boolean = p_a.boolean && p_b.boolean; + } break; + case OP_OR: { + scalar.boolean = p_a.boolean || p_b.boolean; + } break; + case OP_ADD: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint + p_b.sint; + } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + scalar.uint = p_a.uint + p_b.uint; + } else { // float + matrix types + scalar.real = p_a.real + p_b.real; + } + } break; + case OP_SUB: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint - p_b.sint; + } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + scalar.uint = p_a.uint - p_b.uint; + } else { // float + matrix types + scalar.real = p_a.real - p_b.real; + } + } break; + case OP_MUL: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint * p_b.sint; + } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + scalar.uint = p_a.uint * p_b.uint; + } else { // float + matrix types + scalar.real = p_a.real * p_b.real; + } + } break; + case OP_DIV: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + if (p_b.sint == 0) { + _set_error(RTR("Division by zero error.")); + r_is_valid = false; + break; + } + scalar.sint = p_a.sint / p_b.sint; + } else if (p_ret_type == TYPE_UINT && p_ret_type <= TYPE_UVEC4) { + if (p_b.uint == 0U) { + _set_error(RTR("Division by zero error.")); + r_is_valid = false; + break; + } + scalar.uint = p_a.uint / p_b.uint; + } else { // float + matrix types + scalar.real = p_a.real / p_b.real; + } + } break; + case OP_MOD: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + if (p_b.sint == 0) { + _set_error(RTR("Modulo by zero error.")); + r_is_valid = false; + break; + } + scalar.sint = p_a.sint % p_b.sint; + } else { // uint types + if (p_b.uint == 0U) { + _set_error(RTR("Modulo by zero error.")); + r_is_valid = false; + break; + } + scalar.uint = p_a.uint % p_b.uint; + } + } break; + case OP_SHIFT_LEFT: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint << p_b.sint; + } else { // uint types + scalar.uint = p_a.uint << p_b.uint; + } + } break; + case OP_SHIFT_RIGHT: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint >> p_b.sint; + } else { // uint types + scalar.uint = p_a.uint >> p_b.uint; + } + } break; + case OP_BIT_AND: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint & p_b.sint; + } else { // uint types + scalar.uint = p_a.uint & p_b.uint; + } + } break; + case OP_BIT_OR: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint | p_b.sint; + } else { // uint types + scalar.uint = p_a.uint | p_b.uint; + } + } break; + case OP_BIT_XOR: { + if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) { + scalar.sint = p_a.sint ^ p_b.sint; + } else { // uint types + scalar.uint = p_a.uint ^ p_b.uint; + } + } break; + default: { + } break; + } + + return scalar; +} + +Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op) { + uint32_t size = get_datatype_component_count(p_ret_type); + if (p_va.size() != p_ret_type) { + return Vector<Scalar>(); // Non-evaluable values should not be parsed further. + } + Vector<Scalar> value; + value.resize(size); + + Scalar *w = value.ptrw(); + for (uint32_t i = 0U; i < size; i++) { + w[i] = _eval_unary_scalar(p_va[i], p_op, p_ret_type); + } + return value; +} + +Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid) { + uint32_t left_size = get_datatype_component_count(p_left_type); + uint32_t right_size = get_datatype_component_count(p_right_type); + + if (p_va.size() != left_size || p_vb.size() != right_size) { + return Vector<Scalar>(); // Non-evaluable values should not be parsed further. + } + + uint32_t ret_size = get_datatype_component_count(p_ret_type); + Vector<Scalar> value; + value.resize(ret_size); + + Scalar *w = value.ptrw(); + for (uint32_t i = 0U; i < ret_size; i++) { + w[i] = _eval_scalar(p_va[MIN(i, left_size - 1)], p_vb[MIN(i, right_size - 1)], p_op, p_ret_type, r_is_valid); + if (!r_is_valid) { + return value; + } + } + return value; +} + +Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type) { + uint32_t left_size = get_datatype_component_count(p_left_type); + uint32_t right_size = get_datatype_component_count(p_right_type); + + if (p_va.size() != left_size || p_vb.size() != right_size) { + return Vector<Scalar>(); // Non-evaluable values should not be parsed further. + } + + uint32_t ret_size = get_datatype_component_count(p_ret_type); + Vector<Scalar> value; + value.resize_zeroed(ret_size); + + Scalar *w = value.ptrw(); + switch (p_ret_type) { + case TYPE_VEC2: { + if (left_size == 2) { // v * m + Vector2 v = Vector2(p_va[0].real, p_va[1].real); + + w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y); + w[1].real = (p_vb[2].real * v.x + p_vb[3].real * v.y); + } else { // m * v + Vector2 v = Vector2(p_vb[0].real, p_vb[1].real); + + w[0].real = (p_va[0].real * v.x + p_va[2].real * v.y); + w[1].real = (p_va[1].real * v.x + p_va[3].real * v.y); + } + } break; + case TYPE_VEC3: { + if (left_size == 3) { // v * m + Vector3 v = Vector3(p_va[0].real, p_va[1].real, p_va[2].real); + + w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z); + w[1].real = (p_vb[3].real * v.x + p_vb[4].real * v.y + p_vb[5].real * v.z); + w[2].real = (p_vb[6].real * v.x + p_vb[7].real * v.y + p_vb[8].real * v.z); + } else { // m * v + Vector3 v = Vector3(p_vb[0].real, p_vb[1].real, p_vb[2].real); + + w[0].real = (p_va[0].real * v.x + p_va[3].real * v.y + p_va[6].real * v.z); + w[1].real = (p_va[1].real * v.x + p_va[4].real * v.y + p_va[7].real * v.z); + w[2].real = (p_va[2].real * v.x + p_va[5].real * v.y + p_va[8].real * v.z); + } + } break; + case TYPE_VEC4: { + if (left_size == 4) { // v * m + Vector4 v = Vector4(p_va[0].real, p_va[1].real, p_va[2].real, p_va[3].real); + + w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z + p_vb[3].real * v.w); + w[1].real = (p_vb[4].real * v.x + p_vb[5].real * v.y + p_vb[6].real * v.z + p_vb[7].real * v.w); + w[2].real = (p_vb[8].real * v.x + p_vb[9].real * v.y + p_vb[10].real * v.z + p_vb[11].real * v.w); + w[3].real = (p_vb[12].real * v.x + p_vb[13].real * v.y + p_vb[14].real * v.z + p_vb[15].real * v.w); + } else { // m * v + Vector4 v = Vector4(p_vb[0].real, p_vb[1].real, p_vb[2].real, p_vb[3].real); + + w[0].real = (p_vb[0].real * v.x + p_vb[4].real * v.y + p_vb[8].real * v.z + p_vb[12].real * v.w); + w[1].real = (p_vb[1].real * v.x + p_vb[5].real * v.y + p_vb[9].real * v.z + p_vb[13].real * v.w); + w[2].real = (p_vb[2].real * v.x + p_vb[6].real * v.y + p_vb[10].real * v.z + p_vb[14].real * v.w); + w[3].real = (p_vb[3].real * v.x + p_vb[7].real * v.y + p_vb[11].real * v.z + p_vb[15].real * v.w); + } + } break; + default: { + } break; + } + + return value; +} + const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { // Constructors. @@ -3271,34 +3642,15 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI int max = builtin_func_const_args[constarg_idx].max; bool error = false; - if (p_func->arguments[arg]->type == Node::NODE_TYPE_VARIABLE) { - const VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg]); - - bool is_const = false; - ConstantNode::Value value; - value.sint = -1; - - _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, &is_const, nullptr, nullptr, &value); - if (!is_const || value.sint < min || value.sint > max) { + Vector<Scalar> values = _get_node_values(p_block, p_func->arguments[arg]); + if (p_func->arguments[arg]->get_datatype() == TYPE_INT && !values.is_empty()) { + if (values[0].sint < min || values[0].sint > max) { error = true; } } else { - if (p_func->arguments[arg]->type == Node::NODE_TYPE_CONSTANT) { - const ConstantNode *cn = static_cast<ConstantNode *>(p_func->arguments[arg]); - - if (cn->get_datatype() == TYPE_INT && cn->values.size() == 1) { - int value = cn->values[0].sint; - - if (value < min || value > max) { - error = true; - } - } else { - error = true; - } - } else { - error = true; - } + error = true; } + if (error) { _set_error(vformat(RTR("Expected integer constant within [%d..%d] range."), min, max)); return false; @@ -3760,7 +4112,7 @@ bool ShaderLanguage::is_token_hint(TokenType p_type) { return int(p_type) > int(TK_RENDER_MODE) && int(p_type) < int(TK_SHADER_TYPE); } -bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value) { +bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value) { if (p_constant->datatype == p_to_type) { if (p_value) { for (int i = 0; i < p_constant->values.size(); i++) { @@ -3828,7 +4180,7 @@ bool ShaderLanguage::is_sampler_type(DataType p_type) { return p_type > TYPE_MAT4 && p_type < TYPE_STRUCT; } -Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) { +Variant ShaderLanguage::constant_value_to_variant(const Vector<Scalar> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) { int array_size = p_array_size; if (p_value.size() > 0) { @@ -4437,6 +4789,52 @@ uint32_t ShaderLanguage::get_datatype_size(ShaderLanguage::DataType p_type) { ERR_FAIL_V(0); } +uint32_t ShaderLanguage::get_datatype_component_count(ShaderLanguage::DataType p_type) { + switch (p_type) { + case TYPE_BOOL: + return 1U; + case TYPE_BVEC2: + return 2U; + case TYPE_BVEC3: + return 3U; + case TYPE_BVEC4: + return 4U; + case TYPE_INT: + return 1U; + case TYPE_IVEC2: + return 2U; + case TYPE_IVEC3: + return 3U; + case TYPE_IVEC4: + return 4U; + case TYPE_UINT: + return 1U; + case TYPE_UVEC2: + return 2U; + case TYPE_UVEC3: + return 3U; + case TYPE_UVEC4: + return 4U; + case TYPE_FLOAT: + return 1U; + case TYPE_VEC2: + return 2U; + case TYPE_VEC3: + return 3U; + case TYPE_VEC4: + return 4U; + case TYPE_MAT2: + return 4U; + case TYPE_MAT3: + return 9U; + case TYPE_MAT4: + return 16U; + default: + break; + } + return 0U; +} + void ShaderLanguage::get_keyword_list(List<String> *r_keywords) { HashSet<String> kws; @@ -4929,45 +5327,30 @@ Error ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo & *r_unknown_size = true; } } else { - int array_size = 0; + _set_tkpos(pos); - if (!tk.is_integer_constant() || ((int)tk.constant) <= 0) { - _set_tkpos(pos); - Node *n = _parse_and_reduce_expression(p_block, p_function_info); - if (n) { - if (n->type == Node::NODE_TYPE_VARIABLE) { - VariableNode *vn = static_cast<VariableNode *>(n); - if (vn) { - ConstantNode::Value v; - DataType data_type; - bool is_const = false; + int array_size = 0; + Node *expr = _parse_and_reduce_expression(p_block, p_function_info); - _find_identifier(p_block, false, p_function_info, vn->name, &data_type, nullptr, &is_const, nullptr, nullptr, &v); + if (expr) { + Vector<Scalar> values = _get_node_values(p_block, expr); - if (is_const) { - if (data_type == TYPE_INT) { - int32_t value = v.sint; - if (value > 0) { - array_size = value; - } - } else if (data_type == TYPE_UINT) { - uint32_t value = v.uint; - if (value > 0U) { - array_size = value; - } - } - } - } - } else if (n->type == Node::NODE_TYPE_OPERATOR) { - _set_error(vformat(RTR("Array size expressions are not supported."))); - return ERR_PARSE_ERROR; - } - if (r_size_expression != nullptr) { - *r_size_expression = n; + if (!values.is_empty()) { + switch (expr->get_datatype()) { + case TYPE_INT: { + array_size = values[0].sint; + } break; + case TYPE_UINT: { + array_size = (int)values[0].uint; + } break; + default: { + } break; } } - } else if (((int)tk.constant) > 0) { - array_size = (uint32_t)tk.constant; + + if (r_size_expression != nullptr) { + *r_size_expression = expr; + } } if (array_size <= 0) { @@ -5064,7 +5447,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc idx++; } if (!auto_size && !undefined_size && an->initializer.size() != array_size) { - _set_error(RTR("Array size mismatch.")); + _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), array_size, an->initializer.size())); return nullptr; } } else { @@ -5185,7 +5568,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc } } if (an->initializer.size() != p_array_size) { - _set_error(RTR("Array size mismatch.")); + _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), p_array_size, an->initializer.size())); return nullptr; } } else { @@ -5230,7 +5613,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_FLOAT_CONSTANT) { ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.real = tk.constant; constant->values.push_back(v); constant->datatype = TYPE_FLOAT; @@ -5238,7 +5621,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_INT_CONSTANT) { ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.sint = tk.constant; constant->values.push_back(v); constant->datatype = TYPE_INT; @@ -5246,7 +5629,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_UINT_CONSTANT) { ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.uint = tk.constant; constant->values.push_back(v); constant->datatype = TYPE_UINT; @@ -5255,7 +5638,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_TRUE) { //handle true constant ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.boolean = true; constant->values.push_back(v); constant->datatype = TYPE_BOOL; @@ -5264,7 +5647,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (tk.type == TK_FALSE) { //handle false constant ConstantNode *constant = alloc_node<ConstantNode>(); - ConstantNode::Value v; + Scalar v; v.boolean = false; constant->values.push_back(v); constant->datatype = TYPE_BOOL; @@ -6527,7 +6910,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons op->op = tk.type == TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT; op->arguments.push_back(expr); - if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { _set_error(RTR("Invalid base type for increment/decrement operator.")); return nullptr; } @@ -6876,7 +7259,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[i].is_op = false; expression.write[i].node = op; - if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (error_set) { + return nullptr; + } + String at; for (int j = 0; j < op->arguments.size(); j++) { if (j > 0) { @@ -6914,7 +7301,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[next_op - 1].is_op = false; expression.write[next_op - 1].node = op; - if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (error_set) { + return nullptr; + } + String at; for (int i = 0; i < op->arguments.size(); i++) { if (i > 0) { @@ -6950,6 +7341,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } if (_is_operator_assign(op->op)) { + if (p_block && expression[next_op - 1].node->type == Node::NODE_TYPE_VARIABLE) { + VariableNode *vn = static_cast<VariableNode *>(expression[next_op - 1].node); + p_block->use_op_eval = vn->is_const; + } + String assign_message; if (!_validate_assign(expression[next_op - 1].node, p_function_info, &assign_message)) { _set_error(assign_message); @@ -6972,7 +7368,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons //replace all 3 nodes by this operator and make it an expression - if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (error_set) { + return nullptr; + } + String at; for (int i = 0; i < op->arguments.size(); i++) { if (i > 0) { @@ -6998,6 +7398,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } + if (p_block) { + p_block->use_op_eval = true; + } + if (p_previous_expression_info != nullptr) { p_previous_expression_info->expression->push_back(expression[0]); } @@ -7020,7 +7424,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha DataType base = get_scalar_type(type); int cardinality = get_cardinality(type); - Vector<ConstantNode::Value> values; + Vector<Scalar> values; for (int i = 1; i < op->arguments.size(); i++) { op->arguments.write[i] = _reduce_expression(p_block, op->arguments[i]); @@ -7032,7 +7436,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha values.push_back(cn->values[j]); } } else if (get_scalar_type(cn->datatype) == cn->datatype) { - ConstantNode::Value v; + Scalar v; if (!convert_constant(cn, base, &v)) { return p_node; } @@ -7048,8 +7452,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha if (values.size() == 1) { if (type >= TYPE_MAT2 && type <= TYPE_MAT4) { - ConstantNode::Value value = values[0]; - ConstantNode::Value zero; + Scalar value = values[0]; + Scalar zero; zero.real = 0.0f; int size = 2 + (type - TYPE_MAT2); @@ -7060,7 +7464,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha } } } else { - ConstantNode::Value value = values[0]; + Scalar value = values[0]; for (int i = 1; i < cardinality; i++) { values.push_back(value); } @@ -7081,10 +7485,10 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha DataType base = get_scalar_type(cn->datatype); - Vector<ConstantNode::Value> values; + Vector<Scalar> values; for (int i = 0; i < cn->values.size(); i++) { - ConstantNode::Value nv; + Scalar nv; switch (base) { case TYPE_BOOL: { nv.boolean = !cn->values[i].boolean; @@ -7515,7 +7919,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun decl.size = decl.initializer.size(); var.array_size = decl.initializer.size(); } else if (decl.initializer.size() != var.array_size) { - _set_error(RTR("Array size mismatch.")); + _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), var.array_size, decl.initializer.size())); return ERR_PARSE_ERROR; } tk = _get_token(); @@ -7537,7 +7941,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun array_size = var.array_size; } else if (tk.type == TK_OP_ASSIGN) { - //variable created with assignment! must parse an expression + p_block->use_op_eval = is_const; + + // Variable created with assignment! Must parse an expression. Node *n = _parse_and_reduce_expression(p_block, p_function_info); if (!n) { return ERR_PARSE_ERROR; @@ -7552,11 +7958,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun } } - if (n->type == Node::NODE_TYPE_CONSTANT) { - ConstantNode *const_node = static_cast<ConstantNode *>(n); - if (const_node && const_node->values.size() == 1) { - var.value = const_node->values[0]; - } + if (is_const) { + var.values = n->get_values(); } if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) { @@ -7734,13 +8137,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun if (!vn) { return ERR_PARSE_ERROR; } - ConstantNode::Value v; + Vector<Scalar> v = { Scalar() }; _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, nullptr, nullptr, nullptr, &v); - if (constants.has(v.sint)) { - _set_error(vformat(RTR("Duplicated case label: %d."), v.sint)); + if (constants.has(v[0].sint)) { + _set_error(vformat(RTR("Duplicated case label: %d."), v[0].sint)); return ERR_PARSE_ERROR; } - constants.insert(v.sint); + constants.insert(v[0].sint); } } else if (flow->flow_op == FLOW_OP_DEFAULT) { continue; @@ -7801,7 +8204,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun vn->name = tk.text; n = vn; } else { - ConstantNode::Value v; + Scalar v; if (tk.type == TK_UINT_CONSTANT) { v.uint = (uint32_t)tk.constant; } else { @@ -9678,7 +10081,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f decl.size = decl.initializer.size(); constant.array_size = decl.initializer.size(); } else if (decl.initializer.size() != constant.array_size) { - _set_error(RTR("Array size mismatch.")); + _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), constant.array_size, decl.initializer.size())); return ERR_PARSE_ERROR; } } else { diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 63dca99654..ba02e181b9 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -355,6 +355,13 @@ public: } }; + union Scalar { + bool boolean = false; + float real; + int32_t sint; + uint32_t uint; + }; + struct Node { Node *next = nullptr; @@ -379,6 +386,7 @@ public: virtual String get_datatype_name() const { return ""; } virtual int get_array_size() const { return 0; } virtual bool is_indexed() const { return false; } + virtual Vector<Scalar> get_values() const { return Vector<Scalar>(); } Node(Type t) : type(t) {} @@ -402,11 +410,13 @@ public: Operator op = OP_EQUAL; StringName struct_name; Vector<Node *> arguments; + Vector<Scalar> values; virtual DataType get_datatype() const override { return return_cache; } virtual String get_datatype_name() const override { return String(struct_name); } virtual int get_array_size() const override { return return_array_size; } virtual bool is_indexed() const override { return op == OP_INDEX; } + virtual Vector<Scalar> get_values() const override { return values; } OperatorNode() : Node(NODE_TYPE_OPERATOR) {} @@ -485,19 +495,15 @@ public: String struct_name = ""; int array_size = 0; - union Value { - bool boolean = false; - float real; - int32_t sint; - uint32_t uint; - }; - - Vector<Value> values; + Vector<Scalar> values; Vector<VariableDeclarationNode::Declaration> array_declarations; virtual DataType get_datatype() const override { return datatype; } virtual String get_datatype_name() const override { return struct_name; } virtual int get_array_size() const override { return array_size; } + virtual Vector<Scalar> get_values() const override { + return values; + } ConstantNode() : Node(NODE_TYPE_CONSTANT) {} @@ -529,13 +535,14 @@ public: int line; //for completion int array_size; bool is_const; - ConstantNode::Value value; + Vector<Scalar> values; }; HashMap<StringName, Variable> variables; List<Node *> statements; bool single_statement = false; bool use_comma_between_statements = false; + bool use_op_eval = true; BlockNode() : Node(NODE_TYPE_BLOCK) {} @@ -657,7 +664,7 @@ public: DataType type = TYPE_VOID; DataPrecision precision = PRECISION_DEFAULT; int array_size = 0; - Vector<ConstantNode::Value> default_value; + Vector<Scalar> default_value; Scope scope = SCOPE_LOCAL; Hint hint = HINT_NONE; bool use_color = false; @@ -803,15 +810,16 @@ public: static bool is_token_operator_assign(TokenType p_type); static bool is_token_hint(TokenType p_type); - static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value = nullptr); + static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value = nullptr); static DataType get_scalar_type(DataType p_type); static int get_cardinality(DataType p_type); static bool is_scalar_type(DataType p_type); static bool is_float_type(DataType p_type); static bool is_sampler_type(DataType p_type); - static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE); + static Variant constant_value_to_variant(const Vector<Scalar> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE); static PropertyInfo uniform_to_property_info(const ShaderNode::Uniform &p_uniform); static uint32_t get_datatype_size(DataType p_type); + static uint32_t get_datatype_component_count(DataType p_type); static void get_keyword_list(List<String> *r_keywords); static bool is_control_flow_keyword(String p_keyword); @@ -1070,13 +1078,21 @@ private: IdentifierType last_type = IDENTIFIER_MAX; - bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, ConstantNode::Value *r_constant_value = nullptr); + bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, Vector<Scalar> *r_constant_values = nullptr); #ifdef DEBUG_ENABLED void _parse_used_identifier(const StringName &p_identifier, IdentifierType p_type, const StringName &p_function); #endif // DEBUG_ENABLED bool _is_operator_assign(Operator p_op) const; bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr); - bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr); + bool _validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr); + + Vector<Scalar> _get_node_values(const BlockNode *p_block, Node *p_node); + bool _eval_operator(const BlockNode *p_block, OperatorNode *p_op); + Scalar _eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type); + Scalar _eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid); + Vector<Scalar> _eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op); + Vector<Scalar> _eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid); + Vector<Scalar> _eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type); struct BuiltinEntry { const char *name; diff --git a/servers/rendering/storage/mesh_storage.cpp b/servers/rendering/storage/mesh_storage.cpp index 221ebaa277..6680920c98 100644 --- a/servers/rendering/storage/mesh_storage.cpp +++ b/servers/rendering/storage/mesh_storage.cpp @@ -285,7 +285,7 @@ int RendererMeshStorage::multimesh_get_visible_instances(RID p_multimesh) const return _multimesh_get_visible_instances(p_multimesh); } -AABB RendererMeshStorage::multimesh_get_aabb(RID p_multimesh) const { +AABB RendererMeshStorage::multimesh_get_aabb(RID p_multimesh) { return _multimesh_get_aabb(p_multimesh); } diff --git a/servers/rendering/storage/mesh_storage.h b/servers/rendering/storage/mesh_storage.h index ecd2a967d0..5e3a4738e6 100644 --- a/servers/rendering/storage/mesh_storage.h +++ b/servers/rendering/storage/mesh_storage.h @@ -151,7 +151,7 @@ public: virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible); virtual int multimesh_get_visible_instances(RID p_multimesh) const; - virtual AABB multimesh_get_aabb(RID p_multimesh) const; + virtual AABB multimesh_get_aabb(RID p_multimesh); virtual RID _multimesh_allocate() = 0; virtual void _multimesh_initialize(RID p_rid) = 0; @@ -183,7 +183,7 @@ public: virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) = 0; virtual int _multimesh_get_visible_instances(RID p_multimesh) const = 0; - virtual AABB _multimesh_get_aabb(RID p_multimesh) const = 0; + virtual AABB _multimesh_get_aabb(RID p_multimesh) = 0; // Multimesh is responsible for allocating / destroying a MultiMeshInterpolator object. // This allows shared functionality for interpolation across backends. diff --git a/tests/core/math/test_expression.h b/tests/core/math/test_expression.h index 512d7932f9..c3e4280491 100644 --- a/tests/core/math/test_expression.h +++ b/tests/core/math/test_expression.h @@ -122,6 +122,59 @@ TEST_CASE("[Expression] Floating-point arithmetic") { "Float multiplication-addition-subtraction-division should return the expected result."); } +TEST_CASE("[Expression] Floating-point notation") { + Expression expression; + + CHECK_MESSAGE( + expression.parse("2.") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(2.0), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse("(2.)") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(2.0), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse(".3") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(0.3), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse("2.+5.") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(7.0), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse(".3-.8") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(-0.5), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse("2.+.2") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(2.2), + "The expression should return the expected result."); + + CHECK_MESSAGE( + expression.parse(".0*0.") == OK, + "The expression should parse successfully."); + CHECK_MESSAGE( + double(expression.execute()) == doctest::Approx(0.0), + "The expression should return the expected result."); +} + TEST_CASE("[Expression] Scientific notation") { Expression expression; diff --git a/tests/scene/test_path_follow_3d.h b/tests/scene/test_path_follow_3d.h index d08af3a70c..6a384bec2b 100644 --- a/tests/scene/test_path_follow_3d.h +++ b/tests/scene/test_path_follow_3d.h @@ -60,39 +60,30 @@ TEST_CASE("[SceneTree][PathFollow3D] Sampling with progress ratio") { SceneTree::get_singleton()->get_root()->add_child(path); path_follow_3d->set_progress_ratio(0); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.125); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(50, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.25); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.375); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 50, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.5); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.625); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 50), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.75); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 100), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(0.875); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 50, 100), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress_ratio(1); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 100), path_follow_3d->get_transform().get_origin())); memdelete(path); @@ -113,39 +104,30 @@ TEST_CASE("[SceneTree][PathFollow3D] Sampling with progress") { SceneTree::get_singleton()->get_root()->add_child(path); path_follow_3d->set_progress(0); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(50); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(50, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(150); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 50, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(200); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 0), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(250); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 50), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(300); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 100, 100), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(350); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 50, 100), path_follow_3d->get_transform().get_origin())); path_follow_3d->set_progress(400); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 100), path_follow_3d->get_transform().get_origin())); memdelete(path); @@ -163,13 +145,11 @@ TEST_CASE("[SceneTree][PathFollow3D] Removal of a point in curve") { SceneTree::get_singleton()->get_root()->add_child(path); path_follow_3d->set_progress_ratio(0.5); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(100, 0, 0), path_follow_3d->get_transform().get_origin())); curve->remove_point(1); path_follow_3d->set_progress_ratio(0.5); - path_follow_3d->update_transform(true); CHECK_MESSAGE( is_equal_approx(Vector3(50, 50, 0), path_follow_3d->get_transform().get_origin()), "Path follow's position should be updated after removing a point from the curve"); @@ -270,47 +250,36 @@ TEST_CASE("[SceneTree][PathFollow3D] Calculate forward vector") { path_follow_3d->set_rotation_mode(PathFollow3D::RotationMode::ROTATION_ORIENTED); path_follow_3d->set_progress(-50); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(0); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(50); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(100 + dist_cube_100 / 2); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-0.577348, -0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(100 + dist_cube_100 - 0.01); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(-0.577348, -0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(250 + dist_cube_100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0, 0, -1), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(400 + dist_cube_100 - 0.01); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0, 0, -1), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(400 + 1.5 * dist_cube_100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0.577348, 0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(400 + 2 * dist_cube_100 - 0.01); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(0.577348, 0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2))); path_follow_3d->set_progress(500 + 2 * dist_cube_100); - path_follow_3d->update_transform(true); CHECK(is_equal_approx(Vector3(1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2))); memdelete(path); |