diff options
86 files changed, 1489 insertions, 1240 deletions
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index d4b50facb2..7cba5cb161 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -262,7 +262,6 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func; - p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; p_extension_funcs->class_userdata, // void *class_userdata; }; @@ -270,6 +269,7 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func; p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; + p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy); } @@ -297,7 +297,6 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func; - p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; p_extension_funcs->class_userdata, // void *class_userdata; }; @@ -305,6 +304,7 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar nullptr, // GDExtensionClassNotification notification_func; p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; + p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy); } @@ -332,7 +332,6 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func; - p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; p_extension_funcs->class_userdata, // void *class_userdata; }; @@ -340,6 +339,7 @@ void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_librar nullptr, // GDExtensionClassNotification notification_func; nullptr, // GDExtensionClassFreePropertyList free_property_list_func; p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance2 create_instance_func; + p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; }; _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info4, &legacy); } @@ -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; @@ -427,6 +427,7 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr extension->gdextension.notification = p_deprecated_funcs->notification_func; extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func; extension->gdextension.create_instance = p_deprecated_funcs->create_instance_func; + extension->gdextension.get_rid = p_deprecated_funcs->get_rid_func; } #endif // DISABLE_DEPRECATED extension->gdextension.notification2 = p_extension_funcs->notification_func; @@ -440,7 +441,6 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr extension->gdextension.get_virtual = p_extension_funcs->get_virtual_func; extension->gdextension.get_virtual_call_data = p_extension_funcs->get_virtual_call_data_func; extension->gdextension.call_virtual_with_data = p_extension_funcs->call_virtual_with_data_func; - extension->gdextension.get_rid = p_extension_funcs->get_rid_func; extension->gdextension.reloadable = self->reloadable; #ifdef TOOLS_ENABLED diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 7bb4294909..706bc7e189 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -71,6 +71,7 @@ class GDExtension : public Resource { GDExtensionClassNotification notification_func = nullptr; GDExtensionClassFreePropertyList free_property_list_func = nullptr; GDExtensionClassCreateInstance create_instance_func = nullptr; + GDExtensionClassGetRID get_rid_func = nullptr; #endif // DISABLE_DEPRECATED }; 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/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index d3132baf1b..8eb8a2ed33 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -392,7 +392,6 @@ typedef struct { GDExtensionClassGetVirtualCallData get_virtual_call_data_func; // Used to call virtual functions when `get_virtual_call_data_func` is not null. GDExtensionClassCallVirtualWithData call_virtual_with_data_func; - GDExtensionClassGetRID get_rid_func; void *class_userdata; // Per-class user data, later accessible in instance bindings. } GDExtensionClassCreationInfo4; 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/object/script_language.cpp b/core/object/script_language.cpp index 57e5195137..d5b7bc768d 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -41,6 +41,7 @@ ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES]; int ScriptServer::_language_count = 0; bool ScriptServer::languages_ready = false; Mutex ScriptServer::languages_mutex; +thread_local bool ScriptServer::thread_entered = false; bool ScriptServer::scripting_enabled = true; bool ScriptServer::reload_scripts_on_save = false; @@ -326,6 +327,10 @@ bool ScriptServer::are_languages_initialized() { return languages_ready; } +bool ScriptServer::thread_is_entered() { + return thread_entered; +} + void ScriptServer::set_reload_scripts_on_save(bool p_enable) { reload_scripts_on_save = p_enable; } @@ -335,6 +340,10 @@ bool ScriptServer::is_reload_scripts_on_save_enabled() { } void ScriptServer::thread_enter() { + if (thread_entered) { + return; + } + MutexLock lock(languages_mutex); if (!languages_ready) { return; @@ -342,9 +351,15 @@ void ScriptServer::thread_enter() { for (int i = 0; i < _language_count; i++) { _languages[i]->thread_enter(); } + + thread_entered = true; } void ScriptServer::thread_exit() { + if (!thread_entered) { + return; + } + MutexLock lock(languages_mutex); if (!languages_ready) { return; @@ -352,6 +367,8 @@ void ScriptServer::thread_exit() { for (int i = 0; i < _language_count; i++) { _languages[i]->thread_exit(); } + + thread_entered = false; } HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes; diff --git a/core/object/script_language.h b/core/object/script_language.h index e38c344ae5..d9e2ab1d3c 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -54,6 +54,7 @@ class ScriptServer { static int _language_count; static bool languages_ready; static Mutex languages_mutex; + static thread_local bool thread_entered; static bool scripting_enabled; static bool reload_scripts_on_save; @@ -101,6 +102,7 @@ public: static void init_languages(); static void finish_languages(); static bool are_languages_initialized(); + static bool thread_is_entered(); }; class PlaceHolderScriptInstance; diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp index fe7bbd474c..cf396c2676 100644 --- a/core/object/worker_thread_pool.cpp +++ b/core/object/worker_thread_pool.cpp @@ -63,17 +63,14 @@ void WorkerThreadPool::_process_task(Task *p_task) { // Tasks must start with these at default values. They are free to set-and-forget otherwise. set_current_thread_safe_for_nodes(false); MessageQueue::set_thread_singleton_override(nullptr); + // Since the WorkerThreadPool is started before the script server, // its pre-created threads can't have ScriptServer::thread_enter() called on them early. // Therefore, we do it late at the first opportunity, so in case the task // about to be run uses scripting, guarantees are held. + ScriptServer::thread_enter(); + task_mutex.lock(); - if (!curr_thread.ready_for_scripting && ScriptServer::are_languages_initialized()) { - task_mutex.unlock(); - ScriptServer::thread_enter(); - task_mutex.lock(); - curr_thread.ready_for_scripting = true; - } p_task->pool_thread_index = pool_thread_index; prev_task = curr_thread.current_task; curr_thread.current_task = p_task; @@ -326,6 +323,8 @@ WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void * } WorkerThreadPool::TaskID WorkerThreadPool::_add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description) { + ERR_FAIL_COND_V_MSG(threads.is_empty(), INVALID_TASK_ID, "Can't add a task because the WorkerThreadPool is either not initialized yet or already terminated."); + task_mutex.lock(); // Get a free task Task *task = task_allocator.alloc(); @@ -514,6 +513,12 @@ void WorkerThreadPool::yield() { int th_index = get_thread_index(); ERR_FAIL_COND_MSG(th_index == -1, "This function can only be called from a worker thread."); _wait_collaboratively(&threads[th_index], ThreadData::YIELDING); + + // If this long-lived task started before the scripting server was initialized, + // now is a good time to have scripting languages ready for the current thread. + // Otherwise, such a piece of setup won't happen unless another task has been + // run during the collaborative wait. + ScriptServer::thread_enter(); } void WorkerThreadPool::notify_yield_over(TaskID p_task_id) { @@ -538,6 +543,7 @@ void WorkerThreadPool::notify_yield_over(TaskID p_task_id) { } WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { + ERR_FAIL_COND_V_MSG(threads.is_empty(), INVALID_TASK_ID, "Can't add a group task because the WorkerThreadPool is either not initialized yet or already terminated."); ERR_FAIL_COND_V(p_elements < 0, INVALID_TASK_ID); if (p_tasks < 0) { p_tasks = MAX(1u, threads.size()); @@ -749,5 +755,5 @@ WorkerThreadPool::WorkerThreadPool() { } WorkerThreadPool::~WorkerThreadPool() { - finish(); + DEV_ASSERT(threads.size() == 0 && "finish() hasn't been called!"); } diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h index 5be4f20927..6374dbe8c7 100644 --- a/core/object/worker_thread_pool.h +++ b/core/object/worker_thread_pool.h @@ -112,7 +112,6 @@ private: uint32_t index = 0; Thread thread; - bool ready_for_scripting : 1; bool signaled : 1; bool yield_is_over : 1; Task *current_task = nullptr; @@ -120,7 +119,6 @@ private: ConditionVariable cond_var; ThreadData() : - ready_for_scripting(false), signaled(false), yield_is_over(false) {} }; diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index c866ff0415..220ed9da31 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -107,8 +107,6 @@ static Time *_time = nullptr; static core_bind::Geometry2D *_geometry_2d = nullptr; static core_bind::Geometry3D *_geometry_3d = nullptr; -static WorkerThreadPool *worker_thread_pool = nullptr; - extern Mutex _global_mutex; static GDExtensionManager *gdextension_manager = nullptr; @@ -297,8 +295,6 @@ void register_core_types() { GDREGISTER_NATIVE_STRUCT(AudioFrame, "float left;float right"); GDREGISTER_NATIVE_STRUCT(ScriptLanguageExtensionProfilingInfo, "StringName signature;uint64_t call_count;uint64_t total_time;uint64_t self_time"); - worker_thread_pool = memnew(WorkerThreadPool); - OS::get_singleton()->benchmark_end_measure("Core", "Register Types"); } @@ -349,7 +345,7 @@ void register_core_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("GDExtensionManager", GDExtensionManager::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", worker_thread_pool)); + Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", WorkerThreadPool::get_singleton())); OS::get_singleton()->benchmark_end_measure("Core", "Register Singletons"); } @@ -382,8 +378,6 @@ void unregister_core_types() { // Destroy singletons in reverse order to ensure dependencies are not broken. - memdelete(worker_thread_pool); - memdelete(_engine_debugger); memdelete(_marshalls); memdelete(_classdb); 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/drivers/metal/metal_objects.mm b/drivers/metal/metal_objects.mm index abdcccf00c..d3c3d2b232 100644 --- a/drivers/metal/metal_objects.mm +++ b/drivers/metal/metal_objects.mm @@ -560,10 +560,10 @@ void MDCommandBuffer::_render_clear_render_area() { } } uint32_t ds_index = subpass.depth_stencil_reference.attachment; - MDAttachment const &attachment = pass.attachments[ds_index]; - bool shouldClearDepth = (ds_index != RDD::AttachmentReference::UNUSED && attachment.shouldClear(subpass, false)); - bool shouldClearStencil = (ds_index != RDD::AttachmentReference::UNUSED && attachment.shouldClear(subpass, true)); + bool shouldClearDepth = (ds_index != RDD::AttachmentReference::UNUSED && pass.attachments[ds_index].shouldClear(subpass, false)); + bool shouldClearStencil = (ds_index != RDD::AttachmentReference::UNUSED && pass.attachments[ds_index].shouldClear(subpass, true)); if (shouldClearDepth || shouldClearStencil) { + MDAttachment const &attachment = pass.attachments[ds_index]; BitField<RDD::TextureAspectBits> bits; if (shouldClearDepth && attachment.type & MDAttachmentType::Depth) { bits.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT); 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 6e375dc1fc..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); } } 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/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 02146c1d07..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" @@ -5737,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 35a8741b19..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" @@ -9372,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/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/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index 3041857d83..17bcbacfc2 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -1415,21 +1415,24 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the p_theme->set_icon("decrement_highlight", "VScrollBar", empty_icon); p_theme->set_icon("decrement_pressed", "VScrollBar", empty_icon); + // Slider + const int background_margin = MAX(2, p_config.base_margin / 2); + // HSlider. p_theme->set_icon("grabber_highlight", "HSlider", p_theme->get_icon(SNAME("GuiSliderGrabberHl"), EditorStringName(EditorIcons))); p_theme->set_icon("grabber", "HSlider", p_theme->get_icon(SNAME("GuiSliderGrabber"), EditorStringName(EditorIcons))); - p_theme->set_stylebox("slider", "HSlider", make_flat_stylebox(p_config.dark_color_3, 0, p_config.base_margin / 2, 0, p_config.base_margin / 2, p_config.corner_radius)); - p_theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(p_config.contrast_color_1, 0, p_config.base_margin / 2, 0, p_config.base_margin / 2, p_config.corner_radius)); - p_theme->set_stylebox("grabber_area_highlight", "HSlider", make_flat_stylebox(p_config.contrast_color_1, 0, p_config.base_margin / 2, 0, p_config.base_margin / 2)); + p_theme->set_stylebox("slider", "HSlider", make_flat_stylebox(p_config.dark_color_3, 0, background_margin, 0, background_margin, p_config.corner_radius)); + p_theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(p_config.contrast_color_1, 0, background_margin, 0, background_margin, p_config.corner_radius)); + p_theme->set_stylebox("grabber_area_highlight", "HSlider", make_flat_stylebox(p_config.contrast_color_1, 0, background_margin, 0, background_margin)); p_theme->set_constant("center_grabber", "HSlider", 0); p_theme->set_constant("grabber_offset", "HSlider", 0); // VSlider. p_theme->set_icon("grabber", "VSlider", p_theme->get_icon(SNAME("GuiSliderGrabber"), EditorStringName(EditorIcons))); p_theme->set_icon("grabber_highlight", "VSlider", p_theme->get_icon(SNAME("GuiSliderGrabberHl"), EditorStringName(EditorIcons))); - p_theme->set_stylebox("slider", "VSlider", make_flat_stylebox(p_config.dark_color_3, p_config.base_margin / 2, 0, p_config.base_margin / 2, 0, p_config.corner_radius)); - p_theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(p_config.contrast_color_1, p_config.base_margin / 2, 0, p_config.base_margin / 2, 0, p_config.corner_radius)); - p_theme->set_stylebox("grabber_area_highlight", "VSlider", make_flat_stylebox(p_config.contrast_color_1, p_config.base_margin / 2, 0, p_config.base_margin / 2, 0)); + p_theme->set_stylebox("slider", "VSlider", make_flat_stylebox(p_config.dark_color_3, background_margin, 0, background_margin, 0, p_config.corner_radius)); + p_theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(p_config.contrast_color_1, background_margin, 0, background_margin, 0, p_config.corner_radius)); + p_theme->set_stylebox("grabber_area_highlight", "VSlider", make_flat_stylebox(p_config.contrast_color_1, background_margin, 0, background_margin, 0)); p_theme->set_constant("center_grabber", "VSlider", 0); p_theme->set_constant("grabber_offset", "VSlider", 0); } diff --git a/main/main.cpp b/main/main.cpp index e59f6d03b0..af0d7b5804 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -140,6 +140,7 @@ static Engine *engine = nullptr; static ProjectSettings *globals = nullptr; static Input *input = nullptr; static InputMap *input_map = nullptr; +static WorkerThreadPool *worker_thread_pool = nullptr; static TranslationServer *translation_server = nullptr; static Performance *performance = nullptr; static PackedData *packed_data = nullptr; @@ -690,6 +691,8 @@ Error Main::test_setup() { register_core_settings(); // Here globals are present. + worker_thread_pool = memnew(WorkerThreadPool); + translation_server = memnew(TranslationServer); tsman = memnew(TextServerManager); @@ -800,6 +803,8 @@ void Main::test_cleanup() { ResourceSaver::remove_custom_savers(); PropertyListHelper::clear_base_helpers(); + WorkerThreadPool::get_singleton()->finish(); + #ifdef TOOLS_ENABLED GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR); uninitialize_modules(MODULE_INITIALIZATION_LEVEL_EDITOR); @@ -841,6 +846,9 @@ void Main::test_cleanup() { if (physics_server_2d_manager) { memdelete(physics_server_2d_manager); } + if (worker_thread_pool) { + memdelete(worker_thread_pool); + } if (globals) { memdelete(globals); } @@ -931,6 +939,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph register_core_settings(); //here globals are present + worker_thread_pool = memnew(WorkerThreadPool); translation_server = memnew(TranslationServer); performance = memnew(Performance); GDREGISTER_CLASS(Performance); @@ -2621,6 +2630,10 @@ error: if (translation_server) { memdelete(translation_server); } + if (worker_thread_pool) { + worker_thread_pool->finish(); + memdelete(worker_thread_pool); + } if (globals) { memdelete(globals); } @@ -4502,6 +4515,8 @@ void Main::cleanup(bool p_force) { ResourceLoader::clear_translation_remaps(); ResourceLoader::clear_path_remaps(); + WorkerThreadPool::get_singleton()->finish(); + ScriptServer::finish_languages(); // Sync pending commands that may have been queued from a different thread during ScriptServer finalization @@ -4592,6 +4607,9 @@ void Main::cleanup(bool p_force) { if (physics_server_2d_manager) { memdelete(physics_server_2d_manager); } + if (worker_thread_pool) { + memdelete(worker_thread_pool); + } if (globals) { memdelete(globals); } 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/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/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/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/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index 66be313ff6..08b20c5b42 100644 --- a/platform/linuxbsd/wayland/wayland_thread.cpp +++ b/platform/linuxbsd/wayland/wayland_thread.cpp @@ -1395,6 +1395,8 @@ void WaylandThread::_wl_pointer_on_leave(void *data, struct wl_pointer *wl_point ss->pointed_surface = nullptr; + ss->pointer_data_buffer.pressed_button_mask.clear(); + Ref<WindowEventMessage> msg; msg.instantiate(); msg->event = DisplayServer::WINDOW_EVENT_MOUSE_EXIT; 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_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/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); |