summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/extension/gdextension.cpp10
-rw-r--r--core/extension/gdextension.h1
-rw-r--r--core/extension/gdextension_compat_hashes.cpp73
-rw-r--r--core/extension/gdextension_interface.h1
-rw-r--r--core/math/expression.cpp10
-rw-r--r--core/object/object.cpp1
-rw-r--r--core/object/object.h4
-rw-r--r--core/object/script_language.cpp17
-rw-r--r--core/object/script_language.h2
-rw-r--r--core/object/worker_thread_pool.cpp20
-rw-r--r--core/object/worker_thread_pool.h2
-rw-r--r--core/register_core_types.cpp8
-rw-r--r--core/string/string_name.cpp5
-rw-r--r--core/string/string_name.h3
-rw-r--r--core/string/translation.compat.inc41
-rw-r--r--core/string/translation.cpp1
-rw-r--r--core/string/translation.h4
-rw-r--r--core/string/translation_server.compat.inc38
-rw-r--r--core/string/translation_server.cpp1
-rw-r--r--core/string/translation_server.h4
-rw-r--r--doc/classes/EditorContextMenuPlugin.xml42
-rw-r--r--doc/classes/EditorPlugin.xml23
-rw-r--r--doc/classes/LineEdit.xml21
-rw-r--r--drivers/metal/metal_objects.mm6
-rw-r--r--drivers/windows/file_access_windows.cpp50
-rw-r--r--editor/connections_dialog.cpp7
-rw-r--r--editor/editor_data.cpp132
-rw-r--r--editor/editor_data.h29
-rw-r--r--editor/editor_help.cpp5
-rw-r--r--editor/editor_help_search.cpp3
-rw-r--r--editor/editor_inspector.cpp5
-rw-r--r--editor/editor_interface.cpp5
-rw-r--r--editor/editor_main_screen.cpp291
-rw-r--r--editor/editor_main_screen.h (renamed from core/object/object.compat.inc)67
-rw-r--r--editor/editor_node.cpp340
-rw-r--r--editor/editor_node.h51
-rw-r--r--editor/editor_properties.cpp1
-rw-r--r--editor/export/editor_export_platform.cpp2
-rw-r--r--editor/filesystem_dock.cpp38
-rw-r--r--editor/inspector_dock.cpp3
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp3
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp3
-rw-r--r--editor/plugins/cpu_particles_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/editor_context_menu_plugin.cpp134
-rw-r--r--editor/plugins/editor_context_menu_plugin.h55
-rw-r--r--editor/plugins/editor_plugin.cpp18
-rw-r--r--editor/plugins/editor_plugin.h14
-rw-r--r--editor/plugins/editor_preview_plugins.cpp42
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/mesh_instance_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp2
-rw-r--r--editor/plugins/multimesh_editor_plugin.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/script_editor_plugin.cpp24
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp2
-rw-r--r--editor/project_manager/project_dialog.cpp12
-rw-r--r--editor/scene_tree_dock.cpp32
-rw-r--r--editor/scene_tree_dock.h1
-rw-r--r--editor/themes/editor_theme_manager.cpp15
-rw-r--r--main/main.cpp18
-rw-r--r--modules/basis_universal/image_compress_basisu.cpp89
-rw-r--r--modules/basis_universal/image_compress_basisu.h1
-rw-r--r--modules/csg/editor/csg_gizmos.cpp2
-rw-r--r--modules/gdscript/gdscript_editor.cpp4
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_unique.cfg9
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_unique.gd5
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp3
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp3
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp2
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp2
-rw-r--r--scene/3d/path_3d.cpp52
-rw-r--r--scene/3d/path_3d.h3
-rw-r--r--scene/animation/animation_player.compat.inc10
-rw-r--r--scene/gui/control.compat.inc48
-rw-r--r--scene/gui/control.cpp1
-rw-r--r--scene/gui/control.h4
-rw-r--r--scene/gui/line_edit.cpp539
-rw-r--r--scene/gui/line_edit.h6
-rw-r--r--scene/gui/spin_box.cpp51
-rw-r--r--scene/gui/spin_box.h3
-rw-r--r--scene/main/window.compat.inc48
-rw-r--r--scene/main/window.cpp1
-rw-r--r--scene/main/window.h4
-rw-r--r--tests/core/math/test_expression.h53
-rw-r--r--tests/scene/test_path_follow_3d.h31
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);