summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/object/class_db.cpp61
-rw-r--r--core/object/class_db.h23
-rw-r--r--core/object/method_bind.h42
-rw-r--r--core/object/object.compat.inc40
-rw-r--r--core/object/object.cpp6
-rw-r--r--core/object/object.h8
-rw-r--r--core/string/translation.compat.inc46
-rw-r--r--core/string/translation.cpp15
-rw-r--r--core/string/translation.h8
-rw-r--r--doc/classes/AnimationPlayer.xml4
-rw-r--r--doc/classes/Button.xml3
-rw-r--r--doc/classes/CodeEdit.xml2
-rw-r--r--doc/classes/Control.xml24
-rw-r--r--doc/classes/Font.xml2
-rw-r--r--doc/classes/Object.xml4
-rw-r--r--doc/classes/PopupMenu.xml32
-rw-r--r--doc/classes/TextServer.xml3
-rw-r--r--doc/classes/Translation.xml10
-rw-r--r--doc/classes/TranslationServer.xml4
-rw-r--r--doc/classes/Window.xml24
-rw-r--r--drivers/gles3/storage/utilities.cpp46
-rw-r--r--drivers/gles3/storage/utilities.h19
-rw-r--r--editor/editor_node.cpp30
-rw-r--r--editor/filesystem_dock.cpp8
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp5
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp5
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp5
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp18
-rw-r--r--editor/plugins/font_config_plugin.cpp3
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp18
-rw-r--r--editor/plugins/script_editor_plugin.cpp12
-rw-r--r--editor/plugins/script_text_editor.cpp29
-rw-r--r--editor/plugins/text_editor.cpp12
-rw-r--r--editor/plugins/text_shader_editor.cpp4
-rw-r--r--editor/plugins/theme_editor_preview.cpp4
-rw-r--r--editor/plugins/version_control_editor_plugin.cpp8
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp5
-rw-r--r--editor/scene_tree_dock.cpp7
-rw-r--r--misc/extension_api_validation/4.2-stable.expected44
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Compat.cs2
-rw-r--r--modules/openxr/openxr_api.cpp8
-rw-r--r--modules/text_server_adv/text_server_adv.cpp25
-rw-r--r--modules/text_server_fb/text_server_fb.cpp19
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.cpp10
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.h2
-rw-r--r--scene/animation/animation_player.compat.inc10
-rw-r--r--scene/animation/animation_player.cpp4
-rw-r--r--scene/animation/animation_player.h2
-rw-r--r--scene/gui/button.cpp46
-rw-r--r--scene/gui/button.h4
-rw-r--r--scene/gui/code_edit.compat.inc5
-rw-r--r--scene/gui/code_edit.cpp2
-rw-r--r--scene/gui/code_edit.h3
-rw-r--r--scene/gui/control.compat.inc48
-rw-r--r--scene/gui/control.cpp25
-rw-r--r--scene/gui/control.h4
-rw-r--r--scene/gui/line_edit.cpp8
-rw-r--r--scene/gui/popup_menu.cpp162
-rw-r--r--scene/gui/popup_menu.h6
-rw-r--r--scene/gui/text_edit.cpp8
-rw-r--r--scene/main/window.compat.inc48
-rw-r--r--scene/main/window.cpp25
-rw-r--r--scene/main/window.h4
-rw-r--r--servers/rendering/dummy/storage/material_storage.cpp1
-rw-r--r--servers/text_server.cpp37
-rw-r--r--servers/text_server.h1
66 files changed, 806 insertions, 356 deletions
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 7671b7972e..63adc5b502 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -178,6 +178,7 @@ public:
while (native_parent->gdextension) {
native_parent = native_parent->inherits_ptr;
}
+ ERR_FAIL_NULL_V(native_parent->creation_func, nullptr);
// Construct a placeholder.
Object *obj = native_parent->creation_func();
@@ -501,7 +502,7 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require
if (ti->gdextension && ti->gdextension->create_instance) {
ObjectGDExtension *extension = ti->gdextension;
#ifdef TOOLS_ENABLED
- if (!p_require_real_class && ti->gdextension->is_runtime && Engine::get_singleton()->is_editor_hint()) {
+ if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) {
extension = get_placeholder_extension(ti->name);
}
#endif
@@ -514,6 +515,17 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require
#endif
return obj;
} else {
+#ifdef TOOLS_ENABLED
+ if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) {
+ if (!ti->inherits_ptr || !ti->inherits_ptr->creation_func) {
+ ERR_PRINT_ONCE(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name));
+ } else {
+ ObjectGDExtension *extension = get_placeholder_extension(ti->name);
+ return (Object *)extension->create_instance(extension->class_userdata);
+ }
+ }
+#endif
+
return ti->creation_func();
}
}
@@ -544,26 +556,42 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class)
}
ERR_FAIL_NULL_V_MSG(ti, nullptr, "Cannot get class '" + String(p_class) + "'.");
ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled.");
- ERR_FAIL_NULL_V_MSG(ti->gdextension, nullptr, "Class '" + String(p_class) + "' has no native extension.");
}
+ // Make a "fake" extension to act as a placeholder.
placeholder_extensions[p_class] = ObjectGDExtension();
placeholder_extension = placeholder_extensions.getptr(p_class);
- // Make a "fake" extension to act as a placeholder.
- placeholder_extension->library = ti->gdextension->library;
- placeholder_extension->parent = ti->gdextension->parent;
- placeholder_extension->children = ti->gdextension->children;
- placeholder_extension->parent_class_name = ti->gdextension->parent_class_name;
- placeholder_extension->class_name = ti->gdextension->class_name;
- placeholder_extension->editor_class = ti->gdextension->editor_class;
- placeholder_extension->reloadable = ti->gdextension->reloadable;
- placeholder_extension->is_virtual = ti->gdextension->is_virtual;
- placeholder_extension->is_abstract = ti->gdextension->is_abstract;
- placeholder_extension->is_exposed = ti->gdextension->is_exposed;
placeholder_extension->is_runtime = true;
placeholder_extension->is_placeholder = true;
+ if (ti->gdextension) {
+ placeholder_extension->library = ti->gdextension->library;
+ placeholder_extension->parent = ti->gdextension->parent;
+ placeholder_extension->children = ti->gdextension->children;
+ placeholder_extension->parent_class_name = ti->gdextension->parent_class_name;
+ placeholder_extension->class_name = ti->gdextension->class_name;
+ placeholder_extension->editor_class = ti->gdextension->editor_class;
+ placeholder_extension->reloadable = ti->gdextension->reloadable;
+ placeholder_extension->is_virtual = ti->gdextension->is_virtual;
+ placeholder_extension->is_abstract = ti->gdextension->is_abstract;
+ placeholder_extension->is_exposed = ti->gdextension->is_exposed;
+
+ placeholder_extension->tracking_userdata = ti->gdextension->tracking_userdata;
+ placeholder_extension->track_instance = ti->gdextension->track_instance;
+ placeholder_extension->untrack_instance = ti->gdextension->untrack_instance;
+ } else {
+ placeholder_extension->library = nullptr;
+ placeholder_extension->parent = nullptr;
+ placeholder_extension->parent_class_name = ti->inherits;
+ placeholder_extension->class_name = ti->name;
+ placeholder_extension->editor_class = ti->api == API_EDITOR;
+ placeholder_extension->reloadable = false;
+ placeholder_extension->is_virtual = ti->is_virtual;
+ placeholder_extension->is_abstract = false;
+ placeholder_extension->is_exposed = ti->exposed;
+ }
+
placeholder_extension->set = &PlaceholderExtensionInstance::placeholder_instance_set;
placeholder_extension->get = &PlaceholderExtensionInstance::placeholder_instance_get;
placeholder_extension->get_property_list = &PlaceholderExtensionInstance::placeholder_instance_get_property_list;
@@ -586,10 +614,6 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class)
placeholder_extension->call_virtual_with_data = nullptr;
placeholder_extension->recreate_instance = &PlaceholderExtensionInstance::placeholder_class_recreate_instance;
- placeholder_extension->tracking_userdata = ti->gdextension->tracking_userdata;
- placeholder_extension->track_instance = ti->gdextension->track_instance;
- placeholder_extension->untrack_instance = ti->gdextension->untrack_instance;
-
return placeholder_extension;
}
#endif
@@ -2028,6 +2052,9 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) {
}
}
c.reloadable = p_extension->reloadable;
+#ifdef TOOLS_ENABLED
+ c.is_runtime = p_extension->is_runtime;
+#endif
classes[p_extension->class_name] = c;
}
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 5b4f218845..7f117b4a9b 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -133,6 +133,7 @@ public:
bool exposed = false;
bool reloadable = false;
bool is_virtual = false;
+ bool is_runtime = false;
Object *(*creation_func)() = nullptr;
ClassInfo() {}
@@ -234,6 +235,23 @@ public:
T::register_custom_data_to_otdb();
}
+ template <class T>
+ static void register_runtime_class() {
+ GLOBAL_LOCK_FUNCTION;
+ static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS.");
+ T::initialize_class();
+ ClassInfo *t = classes.getptr(T::get_class_static());
+ ERR_FAIL_NULL(t);
+ ERR_FAIL_COND_MSG(t->inherits_ptr && !t->inherits_ptr->creation_func, vformat("Cannot register runtime class '%s' that descends from an abstract parent class.", T::get_class_static()));
+ t->creation_func = &creator<T>;
+ t->exposed = true;
+ t->is_virtual = false;
+ t->is_runtime = true;
+ t->class_ptr = T::get_class_ptr_static();
+ t->api = current_api;
+ T::register_custom_data_to_otdb();
+ }
+
static void register_extension_class(ObjectGDExtension *p_extension);
static void unregister_extension_class(const StringName &p_class, bool p_free_method_binds = true);
@@ -518,6 +536,11 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) {
::ClassDB::register_internal_class<m_class>(); \
}
+#define GDREGISTER_RUNTIME_CLASS(m_class) \
+ if (m_class::_class_is_enabled) { \
+ ::ClassDB::register_runtime_class<m_class>(); \
+ }
+
#define GDREGISTER_NATIVE_STRUCT(m_class, m_code) ClassDB::register_native_struct(#m_class, m_code, sizeof(m_class))
#include "core/disabled_classes.gen.h"
diff --git a/core/object/method_bind.h b/core/object/method_bind.h
index d67fd003c8..a1723adb9a 100644
--- a/core/object/method_bind.h
+++ b/core/object/method_bind.h
@@ -225,6 +225,9 @@ class MethodBindVarArgT : public MethodBindVarArgBase<MethodBindVarArgT<T>, T, v
public:
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder(), Variant(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
(static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>::method)(p_args, p_arg_count, r_error);
return {};
}
@@ -261,6 +264,9 @@ public:
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder(), Variant(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
return (static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>::method)(p_args, p_arg_count, r_error);
}
@@ -329,6 +335,9 @@ public:
#endif
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder(), Variant(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_variant_args_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments());
#else
@@ -338,6 +347,9 @@ public:
}
virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_validated_object_instance_args(static_cast<T *>(p_object), method, p_args);
#else
@@ -346,6 +358,9 @@ public:
}
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_ptr_args<T, P...>(static_cast<T *>(p_object), method, p_args);
#else
@@ -404,6 +419,9 @@ public:
#endif
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder(), Variant(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_variant_argsc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments());
#else
@@ -413,6 +431,9 @@ public:
}
virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_validated_object_instance_argsc(static_cast<T *>(p_object), method, p_args);
#else
@@ -421,6 +442,9 @@ public:
}
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_ptr_argsc<T, P...>(static_cast<T *>(p_object), method, p_args);
#else
@@ -490,6 +514,9 @@ public:
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
Variant ret;
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder(), ret, vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_variant_args_ret_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments());
#else
@@ -499,6 +526,9 @@ public:
}
virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_validated_object_instance_args_ret(static_cast<T *>(p_object), method, p_args, r_ret);
#else
@@ -507,6 +537,9 @@ public:
}
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_ptr_args_ret<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret);
#else
@@ -577,6 +610,9 @@ public:
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
Variant ret;
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder(), ret, vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_variant_args_retc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments());
#else
@@ -586,6 +622,9 @@ public:
}
virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_validated_object_instance_args_retc(static_cast<T *>(p_object), method, p_args, r_ret);
#else
@@ -594,6 +633,9 @@ public:
}
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name()));
+#endif
#ifdef TYPED_METHOD_BIND
call_with_ptr_args_retc<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret);
#else
diff --git a/core/object/object.compat.inc b/core/object/object.compat.inc
new file mode 100644
index 0000000000..bf1e99fc9b
--- /dev/null
+++ b/core/object/object.compat.inc
@@ -0,0 +1,40 @@
+/**************************************************************************/
+/* object.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
+
+#include "core/object/class_db.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(""));
+}
+
+#endif
diff --git a/core/object/object.cpp b/core/object/object.cpp
index ee2810f808..5db1d2534f 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "object.h"
+#include "object.compat.inc"
#include "core/core_string_names.h"
#include "core/extension/gdextension_manager.h"
@@ -1472,6 +1473,7 @@ void Object::initialize_class() {
}
ClassDB::_add_class<Object>();
_bind_methods();
+ _bind_compatibility_methods();
initialized = true;
}
@@ -1647,8 +1649,8 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_message_translation", "enable"), &Object::set_message_translation);
ClassDB::bind_method(D_METHOD("can_translate_messages"), &Object::can_translate_messages);
- ClassDB::bind_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion);
ClassDB::bind_method(D_METHOD("cancel_free"), &Object::cancel_free);
diff --git a/core/object/object.h b/core/object/object.h
index 3974ef7295..a062b8aa62 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -358,8 +358,8 @@ struct ObjectGDExtension {
#ifdef TOOLS_ENABLED
void *tracking_userdata = nullptr;
- void (*track_instance)(void *p_userdata, void *p_instance);
- void (*untrack_instance)(void *p_userdata, void *p_instance);
+ void (*track_instance)(void *p_userdata, void *p_instance) = nullptr;
+ void (*untrack_instance)(void *p_userdata, void *p_instance) = nullptr;
#endif
};
@@ -702,7 +702,11 @@ protected:
virtual void _notificationv(int p_notification, bool p_reversed) {}
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ static void _bind_compatibility_methods();
+#else
static void _bind_compatibility_methods() {}
+#endif
bool _set(const StringName &p_name, const Variant &p_property) { return false; };
bool _get(const StringName &p_name, Variant &r_property) const { return false; };
void _get_property_list(List<PropertyInfo> *p_list) const {};
diff --git a/core/string/translation.compat.inc b/core/string/translation.compat.inc
new file mode 100644
index 0000000000..d792d4a6fc
--- /dev/null
+++ b/core/string/translation.compat.inc
@@ -0,0 +1,46 @@
+/**************************************************************************/
+/* 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(""));
+}
+
+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.cpp b/core/string/translation.cpp
index 829c9bf777..db0c3f6006 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "translation.h"
+#include "translation.compat.inc"
#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
@@ -155,11 +156,11 @@ int Translation::get_message_count() const {
void Translation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_locale", "locale"), &Translation::set_locale);
ClassDB::bind_method(D_METHOD("get_locale"), &Translation::get_locale);
- ClassDB::bind_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("get_message_list"), &Translation::_get_message_list);
ClassDB::bind_method(D_METHOD("get_translated_message_list"), &Translation::get_translated_message_list);
ClassDB::bind_method(D_METHOD("get_message_count"), &Translation::get_message_count);
@@ -1002,8 +1003,8 @@ void TranslationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name);
- ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation);
ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
diff --git a/core/string/translation.h b/core/string/translation.h
index 3f9dbcc476..4eca37f6be 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -51,6 +51,10 @@ 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);
@@ -111,6 +115,10 @@ 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/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index e023f2d41a..9d2d93b0f0 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -90,7 +90,7 @@
</method>
<method name="play">
<return type="void" />
- <param index="0" name="name" type="StringName" default="&quot;&quot;" />
+ <param index="0" name="name" type="StringName" default="&amp;&quot;&quot;" />
<param index="1" name="custom_blend" type="float" default="-1" />
<param index="2" name="custom_speed" type="float" default="1.0" />
<param index="3" name="from_end" type="bool" default="false" />
@@ -103,7 +103,7 @@
</method>
<method name="play_backwards">
<return type="void" />
- <param index="0" name="name" type="StringName" default="&quot;&quot;" />
+ <param index="0" name="name" type="StringName" default="&amp;&quot;&quot;" />
<param index="1" name="custom_blend" type="float" default="-1" />
<description>
Plays the animation with key [param name] in reverse.
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index 739a824655..7ba328c8b8 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -43,6 +43,9 @@
<member name="alignment" type="int" setter="set_text_alignment" getter="get_text_alignment" enum="HorizontalAlignment" default="1">
Text alignment policy for the button's text, use one of the [enum HorizontalAlignment] constants.
</member>
+ <member name="autowrap_mode" type="int" setter="set_autowrap_mode" getter="get_autowrap_mode" enum="TextServer.AutowrapMode" default="0">
+ If set to something other than [constant TextServer.AUTOWRAP_OFF], the text gets wrapped inside the node's bounding rectangle.
+ </member>
<member name="clip_text" type="bool" setter="set_clip_text" getter="get_clip_text" default="false">
When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text.
</member>
diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml
index 13f39d4fae..bc80b86811 100644
--- a/doc/classes/CodeEdit.xml
+++ b/doc/classes/CodeEdit.xml
@@ -48,7 +48,7 @@
<param index="2" name="insert_text" type="String" />
<param index="3" name="text_color" type="Color" default="Color(1, 1, 1, 1)" />
<param index="4" name="icon" type="Resource" default="null" />
- <param index="5" name="value" type="Variant" default="0" />
+ <param index="5" name="value" type="Variant" default="null" />
<param index="6" name="location" type="int" default="1024" />
<description>
Submits an item to the queue of potential candidates for the autocomplete menu. Call [method update_code_completion_options] to update the list.
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index c7ddd5b12e..43c3f5c1be 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -457,7 +457,7 @@
<method name="get_theme_color" qualifiers="const">
<return type="Color" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a [Color] from the first matching [Theme] in the tree if that [Theme] has a color item with the specified [param name] and [param theme_type]. If [param theme_type] is omitted the class name of the current control is used as the type, or [member theme_type_variation] if it is defined. If the type is a class name its parent classes are also checked, in order of inheritance. If the type is a variation its base types are checked, in order of dependency, then the control's class name and its parent classes are checked.
For the current control its local overrides are considered first (see [method add_theme_color_override]), then its assigned [member theme]. After the current control, each parent control and its assigned [member theme] are considered; controls without a [member theme] assigned are skipped. If no matching [Theme] is found in the tree, the custom project [Theme] (see [member ProjectSettings.gui/theme/custom]) and the default [Theme] are used (see [ThemeDB]).
@@ -484,7 +484,7 @@
<method name="get_theme_constant" qualifiers="const">
<return type="int" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a constant from the first matching [Theme] in the tree if that [Theme] has a constant item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -514,7 +514,7 @@
<method name="get_theme_font" qualifiers="const">
<return type="Font" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a [Font] from the first matching [Theme] in the tree if that [Theme] has a font item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -523,7 +523,7 @@
<method name="get_theme_font_size" qualifiers="const">
<return type="int" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a font size from the first matching [Theme] in the tree if that [Theme] has a font size item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -532,7 +532,7 @@
<method name="get_theme_icon" qualifiers="const">
<return type="Texture2D" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns an icon from the first matching [Theme] in the tree if that [Theme] has an icon item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -541,7 +541,7 @@
<method name="get_theme_stylebox" qualifiers="const">
<return type="StyleBox" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a [StyleBox] from the first matching [Theme] in the tree if that [Theme] has a stylebox item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -590,7 +590,7 @@
<method name="has_theme_color" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a color item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -607,7 +607,7 @@
<method name="has_theme_constant" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a constant item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -624,7 +624,7 @@
<method name="has_theme_font" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a font item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -641,7 +641,7 @@
<method name="has_theme_font_size" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a font size item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -658,7 +658,7 @@
<method name="has_theme_icon" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has an icon item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
@@ -675,7 +675,7 @@
<method name="has_theme_stylebox" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a stylebox item with the specified [param name] and [param theme_type].
See [method get_theme_color] for details.
diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml
index f2bbb747b1..e979a0b875 100644
--- a/doc/classes/Font.xml
+++ b/doc/classes/Font.xml
@@ -136,7 +136,7 @@
<param index="0" name="char" type="int" />
<param index="1" name="font_size" type="int" />
<description>
- Returns the size of a character, optionally taking kerning into account if the next character is provided.
+ Returns the size of a character. Does not take kerning into account.
[b]Note:[/b] Do not use this function to calculate width of the string character by character, use [method get_string_size] or [TextLine] instead. The height returned is the font height (see also [method get_height]) and has no relation to the glyph height.
</description>
</method>
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index 0f8a458bae..307eaa5e00 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -1014,7 +1014,7 @@
<method name="tr" qualifiers="const">
<return type="String" />
<param index="0" name="message" type="StringName" />
- <param index="1" name="context" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="context" type="StringName" default="&amp;&quot;&quot;" />
<description>
Translates a [param message], using the translation catalogs configured in the Project Settings. Further [param context] can be specified to help with the translation.
If [method can_translate_messages] is [code]false[/code], or no translation is available, this method returns the [param message] without changes. See [method set_message_translation].
@@ -1026,7 +1026,7 @@
<param index="0" name="message" type="StringName" />
<param index="1" name="plural_message" type="StringName" />
<param index="2" name="n" type="int" />
- <param index="3" name="context" type="StringName" default="&quot;&quot;" />
+ <param index="3" name="context" type="StringName" default="&amp;&quot;&quot;" />
<description>
Translates a [param message] or [param plural_message], using the translation catalogs configured in the Project Settings. Further [param context] can be specified to help with the translation.
If [method can_translate_messages] is [code]false[/code], or no translation is available, this method returns [param message] or [param plural_message], without changes. See [method set_message_translation].
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 8f6507eabf..d884c7806f 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -197,7 +197,7 @@
If [param allow_echo] is [code]true[/code], the shortcut can be activated with echo events.
</description>
</method>
- <method name="add_submenu_item">
+ <method name="add_submenu_item" deprecated="Prefer using [method add_submenu_node_item] instead.">
<return type="void" />
<param index="0" name="label" type="String" />
<param index="1" name="submenu" type="String" />
@@ -207,6 +207,17 @@
An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index.
</description>
</method>
+ <method name="add_submenu_node_item">
+ <return type="void" />
+ <param index="0" name="label" type="String" />
+ <param index="1" name="submenu" type="PopupMenu" />
+ <param index="2" name="id" type="int" default="-1" />
+ <description>
+ Adds an item that will act as a submenu of the parent [PopupMenu] node when clicked. This submenu will be shown when the item is clicked, hovered for long enough, or activated using the [code]ui_select[/code] or [code]ui_right[/code] input actions.
+ [param submenu] must be either child of this [PopupMenu] or has no parent node (in which case it will be automatically added as a child). If the [param submenu] popup has another parent, this method will fail.
+ An [param id] can optionally be provided. If no [param id] is provided, one will be created from the index.
+ </description>
+ </method>
<method name="clear">
<return type="void" />
<param index="0" name="free_submenus" type="bool" default="false" />
@@ -304,13 +315,20 @@
Returns the [Shortcut] associated with the item at the given [param index].
</description>
</method>
- <method name="get_item_submenu" qualifiers="const">
+ <method name="get_item_submenu" qualifiers="const" deprecated="Prefer using [method get_item_submenu_node] instead.">
<return type="String" />
<param index="0" name="index" type="int" />
<description>
Returns the submenu name of the item at the given [param index]. See [method add_submenu_item] for more info on how to add a submenu.
</description>
</method>
+ <method name="get_item_submenu_node" qualifiers="const">
+ <return type="PopupMenu" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the submenu of the item at the given [param index], or [code]null[/code] if no submenu was added. See [method add_submenu_node_item] for more info on how to add a submenu.
+ </description>
+ </method>
<method name="get_item_text" qualifiers="const">
<return type="String" />
<param index="0" name="index" type="int" />
@@ -545,7 +563,7 @@
Disables the [Shortcut] of the item at the given [param index].
</description>
</method>
- <method name="set_item_submenu">
+ <method name="set_item_submenu" deprecated="Prefer using [method set_item_submenu_node] instead.">
<return type="void" />
<param index="0" name="index" type="int" />
<param index="1" name="submenu" type="String" />
@@ -553,6 +571,14 @@
Sets the submenu of the item at the given [param index]. The submenu is the name of a child [PopupMenu] node that would be shown when the item is clicked.
</description>
</method>
+ <method name="set_item_submenu_node">
+ <return type="void" />
+ <param index="0" name="index" type="int" />
+ <param index="1" name="submenu" type="PopupMenu" />
+ <description>
+ Sets the submenu of the item at the given [param index]. The submenu is a [PopupMenu] node that would be shown when the item is clicked. It must either be a child of this [PopupMenu] or has no parent (in which case it will be automatically added as a child). If the [param submenu] popup has another parent, this method will fail.
+ </description>
+ </method>
<method name="set_item_text">
<return type="void" />
<param index="0" name="index" type="int" />
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 244ed08b9e..d4b341b700 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -1924,6 +1924,9 @@
<constant name="GRAPHEME_IS_EMBEDDED_OBJECT" value="4096" enum="GraphemeFlag" is_bitfield="true">
Grapheme is an object replacement character for the embedded object.
</constant>
+ <constant name="GRAPHEME_IS_SOFT_HYPHEN" value="8192" enum="GraphemeFlag" is_bitfield="true">
+ Grapheme is a soft hyphen.
+ </constant>
<constant name="HINTING_NONE" value="0" enum="Hinting">
Disables font hinting (smoother but less crisp).
</constant>
diff --git a/doc/classes/Translation.xml b/doc/classes/Translation.xml
index 194b3be854..cc63247266 100644
--- a/doc/classes/Translation.xml
+++ b/doc/classes/Translation.xml
@@ -33,7 +33,7 @@
<return type="void" />
<param index="0" name="src_message" type="StringName" />
<param index="1" name="xlated_message" type="StringName" />
- <param index="2" name="context" type="StringName" default="&quot;&quot;" />
+ <param index="2" name="context" type="StringName" default="&amp;&quot;&quot;" />
<description>
Adds a message if nonexistent, followed by its translation.
An additional context could be used to specify the translation context or differentiate polysemic words.
@@ -43,7 +43,7 @@
<return type="void" />
<param index="0" name="src_message" type="StringName" />
<param index="1" name="xlated_messages" type="PackedStringArray" />
- <param index="2" name="context" type="StringName" default="&quot;&quot;" />
+ <param index="2" name="context" type="StringName" default="&amp;&quot;&quot;" />
<description>
Adds a message involving plural translation if nonexistent, followed by its translation.
An additional context could be used to specify the translation context or differentiate polysemic words.
@@ -52,7 +52,7 @@
<method name="erase_message">
<return type="void" />
<param index="0" name="src_message" type="StringName" />
- <param index="1" name="context" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="context" type="StringName" default="&amp;&quot;&quot;" />
<description>
Erases a message.
</description>
@@ -60,7 +60,7 @@
<method name="get_message" qualifiers="const">
<return type="StringName" />
<param index="0" name="src_message" type="StringName" />
- <param index="1" name="context" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="context" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a message's translation.
</description>
@@ -82,7 +82,7 @@
<param index="0" name="src_message" type="StringName" />
<param index="1" name="src_plural_message" type="StringName" />
<param index="2" name="n" type="int" />
- <param index="3" name="context" type="StringName" default="&quot;&quot;" />
+ <param index="3" name="context" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a message's translation involving plurals.
The number [param n] is the number or quantity of the plural object. It will be used to guide the translation system to fetch the correct plural form for the selected language.
diff --git a/doc/classes/TranslationServer.xml b/doc/classes/TranslationServer.xml
index 3a4cd06013..db1a65278c 100644
--- a/doc/classes/TranslationServer.xml
+++ b/doc/classes/TranslationServer.xml
@@ -144,7 +144,7 @@
<method name="translate" qualifiers="const">
<return type="StringName" />
<param index="0" name="message" type="StringName" />
- <param index="1" name="context" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="context" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns the current locale's translation for the given message (key) and context.
</description>
@@ -154,7 +154,7 @@
<param index="0" name="message" type="StringName" />
<param index="1" name="plural_message" type="StringName" />
<param index="2" name="n" type="int" />
- <param index="3" name="context" type="StringName" default="&quot;&quot;" />
+ <param index="3" name="context" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns the current locale's translation for the given message (key), plural message and context.
The number [param n] is the number or quantity of the plural object. It will be used to guide the translation system to fetch the correct plural form for the selected language.
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 97c7ffdd3c..7a60c23e58 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -129,7 +129,7 @@
<method name="get_theme_color" qualifiers="const">
<return type="Color" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a [Color] from the first matching [Theme] in the tree if that [Theme] has a color item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for more details.
@@ -138,7 +138,7 @@
<method name="get_theme_constant" qualifiers="const">
<return type="int" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a constant from the first matching [Theme] in the tree if that [Theme] has a constant item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for more details.
@@ -168,7 +168,7 @@
<method name="get_theme_font" qualifiers="const">
<return type="Font" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a [Font] from the first matching [Theme] in the tree if that [Theme] has a font item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
@@ -177,7 +177,7 @@
<method name="get_theme_font_size" qualifiers="const">
<return type="int" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a font size from the first matching [Theme] in the tree if that [Theme] has a font size item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
@@ -186,7 +186,7 @@
<method name="get_theme_icon" qualifiers="const">
<return type="Texture2D" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns an icon from the first matching [Theme] in the tree if that [Theme] has an icon item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
@@ -195,7 +195,7 @@
<method name="get_theme_stylebox" qualifiers="const">
<return type="StyleBox" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns a [StyleBox] from the first matching [Theme] in the tree if that [Theme] has a stylebox item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
@@ -222,7 +222,7 @@
<method name="has_theme_color" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a color item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
@@ -239,7 +239,7 @@
<method name="has_theme_constant" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a constant item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
@@ -256,7 +256,7 @@
<method name="has_theme_font" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a font item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
@@ -273,7 +273,7 @@
<method name="has_theme_font_size" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a font size item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
@@ -290,7 +290,7 @@
<method name="has_theme_icon" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has an icon item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
@@ -307,7 +307,7 @@
<method name="has_theme_stylebox" qualifiers="const">
<return type="bool" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="theme_type" type="StringName" default="&quot;&quot;" />
+ <param index="1" name="theme_type" type="StringName" default="&amp;&quot;&quot;" />
<description>
Returns [code]true[/code] if there is a matching [Theme] in the tree that has a stylebox item with the specified [param name] and [param theme_type].
See [method Control.get_theme_color] for details.
diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp
index 793b3f64f0..c4fbe098cd 100644
--- a/drivers/gles3/storage/utilities.cpp
+++ b/drivers/gles3/storage/utilities.cpp
@@ -160,6 +160,8 @@ RS::InstanceType Utilities::get_base_type(RID p_rid) const {
return RS::INSTANCE_PARTICLES;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) {
return RS::INSTANCE_PARTICLES_COLLISION;
+ } else if (owns_visibility_notifier(p_rid)) {
+ return RS::INSTANCE_VISIBLITY_NOTIFIER;
}
return RS::INSTANCE_NONE;
}
@@ -207,6 +209,9 @@ bool Utilities::free(RID p_rid) {
} else if (GLES3::MeshStorage::get_singleton()->owns_skeleton(p_rid)) {
GLES3::MeshStorage::get_singleton()->skeleton_free(p_rid);
return true;
+ } else if (owns_visibility_notifier(p_rid)) {
+ visibility_notifier_free(p_rid);
+ return true;
} else {
return false;
}
@@ -233,32 +238,69 @@ void Utilities::base_update_dependency(RID p_base, DependencyTracker *p_instance
} else if (ParticlesStorage::get_singleton()->owns_particles_collision(p_base)) {
Dependency *dependency = ParticlesStorage::get_singleton()->particles_collision_get_dependency(p_base);
p_instance->update_dependency(dependency);
+ } else if (owns_visibility_notifier(p_base)) {
+ VisibilityNotifier *vn = get_visibility_notifier(p_base);
+ p_instance->update_dependency(&vn->dependency);
}
}
/* VISIBILITY NOTIFIER */
RID Utilities::visibility_notifier_allocate() {
- return RID();
+ return visibility_notifier_owner.allocate_rid();
}
void Utilities::visibility_notifier_initialize(RID p_notifier) {
+ visibility_notifier_owner.initialize_rid(p_notifier, VisibilityNotifier());
}
void Utilities::visibility_notifier_free(RID p_notifier) {
+ VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
+ vn->dependency.deleted_notify(p_notifier);
+ visibility_notifier_owner.free(p_notifier);
}
void Utilities::visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) {
+ VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
+ ERR_FAIL_NULL(vn);
+ vn->aabb = p_aabb;
+ vn->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
void Utilities::visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) {
+ VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
+ ERR_FAIL_NULL(vn);
+ vn->enter_callback = p_enter_callbable;
+ vn->exit_callback = p_exit_callable;
}
AABB Utilities::visibility_notifier_get_aabb(RID p_notifier) const {
- return AABB();
+ const VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
+ ERR_FAIL_NULL_V(vn, AABB());
+ return vn->aabb;
}
void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) {
+ VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
+ ERR_FAIL_NULL(vn);
+
+ if (p_enter) {
+ if (!vn->enter_callback.is_null()) {
+ if (p_deferred) {
+ vn->enter_callback.call_deferred();
+ } else {
+ vn->enter_callback.call();
+ }
+ }
+ } else {
+ if (!vn->exit_callback.is_null()) {
+ if (p_deferred) {
+ vn->exit_callback.call_deferred();
+ } else {
+ vn->exit_callback.call();
+ }
+ }
+ }
}
/* TIMING */
diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h
index b9603b972e..7c3b08717e 100644
--- a/drivers/gles3/storage/utilities.h
+++ b/drivers/gles3/storage/utilities.h
@@ -39,10 +39,25 @@
namespace GLES3 {
+/* VISIBILITY NOTIFIER */
+
+struct VisibilityNotifier {
+ AABB aabb;
+ Callable enter_callback;
+ Callable exit_callback;
+ Dependency dependency;
+};
+
class Utilities : public RendererUtilities {
private:
static Utilities *singleton;
+ /* VISIBILITY NOTIFIER */
+
+ mutable RID_Owner<VisibilityNotifier> visibility_notifier_owner;
+
+ /* MISC */
+
struct ResourceAllocation {
#ifdef DEV_ENABLED
String name;
@@ -149,6 +164,10 @@ public:
virtual void base_update_dependency(RID p_base, DependencyTracker *p_instance) override;
/* VISIBILITY NOTIFIER */
+
+ VisibilityNotifier *get_visibility_notifier(RID p_rid) { return visibility_notifier_owner.get_or_null(p_rid); };
+ bool owns_visibility_notifier(RID p_rid) const { return visibility_notifier_owner.owns(p_rid); };
+
virtual RID visibility_notifier_allocate() override;
virtual void visibility_notifier_initialize(RID p_notifier) override;
virtual void visibility_notifier_free(RID p_notifier) override;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index cc0e954087..fbde412834 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -5535,9 +5535,7 @@ void EditorNode::add_tool_menu_item(const String &p_name, const Callable &p_call
void EditorNode::add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu) {
ERR_FAIL_NULL(p_submenu);
ERR_FAIL_COND(p_submenu->get_parent() != nullptr);
-
- tool_menu->add_child(p_submenu);
- tool_menu->add_submenu_item(p_name, p_submenu->get_name(), TOOLS_CUSTOM);
+ tool_menu->add_submenu_node_item(p_name, p_submenu, TOOLS_CUSTOM);
}
void EditorNode::remove_tool_menu_item(const String &p_name) {
@@ -6841,7 +6839,10 @@ EditorNode::EditorNode() {
file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/new_inherited_scene", TTR("New Inherited Scene..."), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::SHIFT + Key::N), FILE_NEW_INHERITED_SCENE);
file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/open_scene", TTR("Open Scene..."), KeyModifierMask::CMD_OR_CTRL + Key::O), FILE_OPEN_SCENE);
file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reopen_closed_scene", TTR("Reopen Closed Scene"), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::SHIFT + Key::T), FILE_OPEN_PREV);
- file_menu->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT);
+
+ recent_scenes = memnew(PopupMenu);
+ file_menu->add_submenu_node_item(TTR("Open Recent"), recent_scenes, FILE_OPEN_RECENT);
+ recent_scenes->connect("id_pressed", callable_mp(this, &EditorNode::_open_recent_scene));
file_menu->add_separator();
file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_scene", TTR("Save Scene"), KeyModifierMask::CMD_OR_CTRL + Key::S), FILE_SAVE_SCENE);
@@ -6857,9 +6858,7 @@ EditorNode::EditorNode() {
file_menu->add_separator();
export_as_menu = memnew(PopupMenu);
- export_as_menu->set_name("Export");
- file_menu->add_child(export_as_menu);
- file_menu->add_submenu_item(TTR("Export As..."), "Export");
+ file_menu->add_submenu_node_item(TTR("Export As..."), export_as_menu);
export_as_menu->add_shortcut(ED_SHORTCUT("editor/export_as_mesh_library", TTR("MeshLibrary...")), FILE_EXPORT_MESH_LIBRARY);
export_as_menu->connect("index_pressed", callable_mp(this, &EditorNode::_export_as_menu_option));
@@ -6871,11 +6870,6 @@ EditorNode::EditorNode() {
file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE);
file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/close_scene", TTR("Close Scene"), KeyModifierMask::CMD_OR_CTRL + KeyModifierMask::SHIFT + Key::W), FILE_CLOSE);
- recent_scenes = memnew(PopupMenu);
- recent_scenes->set_name("RecentScenes");
- file_menu->add_child(recent_scenes);
- recent_scenes->connect("id_pressed", callable_mp(this, &EditorNode::_open_recent_scene));
-
if (!global_menu || !OS::get_singleton()->has_feature("macos")) {
// On macOS "Quit" and "About" options are in the "app" menu.
file_menu->add_separator();
@@ -6918,10 +6912,8 @@ EditorNode::EditorNode() {
project_menu->add_separator();
tool_menu = memnew(PopupMenu);
- tool_menu->set_name("Tools");
tool_menu->connect("index_pressed", callable_mp(this, &EditorNode::_tool_menu_option));
- project_menu->add_child(tool_menu);
- project_menu->add_submenu_item(TTR("Tools"), "Tools");
+ project_menu->add_submenu_node_item(TTR("Tools"), tool_menu);
tool_menu->add_item(TTR("Orphan Resource Explorer..."), TOOLS_ORPHAN_RESOURCES);
tool_menu->add_item(TTR("Upgrade Mesh Surfaces..."), TOOLS_SURFACE_UPGRADE);
@@ -6972,11 +6964,9 @@ EditorNode::EditorNode() {
settings_menu->add_separator();
editor_layouts = memnew(PopupMenu);
- editor_layouts->set_name("Layouts");
editor_layouts->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
- settings_menu->add_child(editor_layouts);
+ settings_menu->add_submenu_node_item(TTR("Editor Layout"), editor_layouts);
editor_layouts->connect("id_pressed", callable_mp(this, &EditorNode::_layout_menu_option));
- settings_menu->add_submenu_item(TTR("Editor Layout"), "Layouts");
settings_menu->add_separator();
ED_SHORTCUT_AND_COMMAND("editor/take_screenshot", TTR("Take Screenshot"), KeyModifierMask::CTRL | Key::F12);
@@ -7404,12 +7394,10 @@ EditorNode::EditorNode() {
add_editor_plugin(VersionControlEditorPlugin::get_singleton());
vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel();
- vcs_actions_menu->set_name("Version Control");
vcs_actions_menu->connect("index_pressed", callable_mp(this, &EditorNode::_version_control_menu_option));
vcs_actions_menu->add_item(TTR("Create/Override Version Control Metadata..."), RUN_VCS_METADATA);
vcs_actions_menu->add_item(TTR("Version Control Settings..."), RUN_VCS_SETTINGS);
- project_menu->add_child(vcs_actions_menu);
- project_menu->set_item_submenu(project_menu->get_item_index(VCS_MENU), "Version Control");
+ project_menu->set_item_submenu_node(project_menu->get_item_index(VCS_MENU), vcs_actions_menu);
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index c4022bd9a5..74bb152e7c 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -3052,11 +3052,9 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
if (p_paths.size() == 1 && p_display_path_dependent_options) {
PopupMenu *new_menu = memnew(PopupMenu);
- new_menu->set_name("New");
new_menu->connect("id_pressed", callable_mp(this, &FileSystemDock::_tree_rmb_option));
- p_popup->add_child(new_menu);
- p_popup->add_submenu_item(TTR("Create New"), "New", FILE_NEW);
+ p_popup->add_submenu_node_item(TTR("Create New"), new_menu, FILE_NEW);
p_popup->set_item_icon(p_popup->get_item_index(FILE_NEW), get_editor_theme_icon(SNAME("Add")));
new_menu->add_icon_item(get_editor_theme_icon(SNAME("Folder")), TTR("Folder..."), FILE_NEW_FOLDER);
@@ -3079,11 +3077,9 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
if (p_paths[0] != "res://") {
PopupMenu *folder_colors_menu = memnew(PopupMenu);
- folder_colors_menu->set_name("FolderColor");
folder_colors_menu->connect("id_pressed", callable_mp(this, &FileSystemDock::_folder_color_index_pressed).bind(folder_colors_menu));
- p_popup->add_child(folder_colors_menu);
- p_popup->add_submenu_item(TTR("Set Folder Color..."), "FolderColor");
+ p_popup->add_submenu_node_item(TTR("Set Folder Color..."), folder_colors_menu);
p_popup->set_item_icon(-1, get_editor_theme_icon(SNAME("Paint")));
folder_colors_menu->add_icon_item(get_editor_theme_icon(SNAME("Folder")), TTR("Default (Reset)"));
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
index d52a2057af..8c2b738549 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -80,7 +80,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
classes.sort_custom<StringName::AlphCompare>();
- menu->add_submenu_item(TTR("Add Animation"), "AddAnimations");
+ menu->add_submenu_node_item(TTR("Add Animation"), animations_menu);
List<StringName> names;
tree->get_animation_list(&names);
@@ -802,9 +802,8 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
menu->connect("id_pressed", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_add_menu_type));
animations_menu = memnew(PopupMenu);
- menu->add_child(animations_menu);
- animations_menu->set_name("AddAnimations");
animations_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+ menu->add_child(animations_menu);
animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_add_animation_type));
open_file = memnew(EditorFileDialog);
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index 30104f0ab4..ec67fb7254 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -125,7 +125,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
classes.sort_custom<StringName::AlphCompare>();
ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
- menu->add_submenu_item(TTR("Add Animation"), "AddAnimations");
+ menu->add_submenu_node_item(TTR("Add Animation"), animations_menu);
List<StringName> names;
tree->get_animation_list(&names);
@@ -1081,9 +1081,8 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
menu->connect("id_pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_add_menu_type));
animations_menu = memnew(PopupMenu);
- menu->add_child(animations_menu);
- animations_menu->set_name("AddAnimations");
animations_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+ menu->add_child(animations_menu);
animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_add_animation_type));
open_file = memnew(EditorFileDialog);
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 08c52f6d12..6b8943ee84 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -568,7 +568,7 @@ void AnimationNodeStateMachineEditor::_open_menu(const Vector2 &p_position) {
List<StringName> animation_names;
tree->get_animation_list(&animation_names);
- menu->add_submenu_item(TTR("Add Animation"), "AddAnimations");
+ menu->add_submenu_node_item(TTR("Add Animation"), animations_menu);
if (animation_names.is_empty()) {
menu->set_item_disabled(menu->get_item_idx_from_text(TTR("Add Animation")), true);
} else {
@@ -1777,9 +1777,8 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
menu->connect("popup_hide", callable_mp(this, &AnimationNodeStateMachineEditor::_stop_connecting));
animations_menu = memnew(PopupMenu);
- menu->add_child(animations_menu);
- animations_menu->set_name("AddAnimations");
animations_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+ menu->add_child(animations_menu);
animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_add_animation_type));
connect_menu = memnew(PopupMenu);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 7c766e6195..3bcc316f84 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5319,14 +5319,8 @@ CanvasItemEditor::CanvasItemEditor() {
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_scale_snap", TTR("Use Scale Snap")), SNAP_USE_SCALE);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_relative", TTR("Snap Relative")), SNAP_RELATIVE);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_pixel_snap", TTR("Use Pixel Snap")), SNAP_USE_PIXEL);
- p->add_submenu_item(TTR("Smart Snapping"), "SmartSnapping");
-
- p->add_separator();
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/configure_snap", TTR("Configure Snap...")), SNAP_CONFIGURE);
smartsnap_config_popup = memnew(PopupMenu);
- p->add_child(smartsnap_config_popup);
- smartsnap_config_popup->set_name("SmartSnapping");
smartsnap_config_popup->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback));
smartsnap_config_popup->set_hide_on_checkable_item_selection(false);
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_parent", TTR("Snap to Parent")), SNAP_USE_NODE_PARENT);
@@ -5335,6 +5329,10 @@ CanvasItemEditor::CanvasItemEditor() {
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_center", TTR("Snap to Node Center")), SNAP_USE_NODE_CENTER);
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_other_nodes", TTR("Snap to Other Nodes")), SNAP_USE_OTHER_NODES);
smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_guides", TTR("Snap to Guides")), SNAP_USE_GUIDES);
+ p->add_submenu_node_item(TTR("Smart Snapping"), smartsnap_config_popup);
+
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/configure_snap", TTR("Configure Snap...")), SNAP_CONFIGURE);
main_menu_hbox->add_child(memnew(VSeparator));
@@ -5416,14 +5414,12 @@ CanvasItemEditor::CanvasItemEditor() {
grid_menu = memnew(PopupMenu);
grid_menu->connect("about_to_popup", callable_mp(this, &CanvasItemEditor::_prepare_grid_menu));
grid_menu->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_on_grid_menu_id_pressed));
- grid_menu->set_name("GridMenu");
grid_menu->add_radio_check_item(TTR("Show"), GRID_VISIBILITY_SHOW);
grid_menu->add_radio_check_item(TTR("Show When Snapping"), GRID_VISIBILITY_SHOW_WHEN_SNAPPING);
grid_menu->add_radio_check_item(TTR("Hide"), GRID_VISIBILITY_HIDE);
grid_menu->add_separator();
grid_menu->add_shortcut(ED_SHORTCUT("canvas_item_editor/toggle_grid", TTR("Toggle Grid"), KeyModifierMask::CMD_OR_CTRL | Key::APOSTROPHE));
- p->add_child(grid_menu);
- p->add_submenu_item(TTR("Grid"), "GridMenu");
+ p->add_submenu_node_item(TTR("Grid"), grid_menu);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), Key::H), SHOW_HELPERS);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers")), SHOW_RULERS);
@@ -5452,12 +5448,10 @@ CanvasItemEditor::CanvasItemEditor() {
theme_menu = memnew(PopupMenu);
theme_menu->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_switch_theme_preview));
- theme_menu->set_name("ThemeMenu");
theme_menu->add_radio_check_item(TTR("Project theme"), THEME_PREVIEW_PROJECT);
theme_menu->add_radio_check_item(TTR("Editor theme"), THEME_PREVIEW_EDITOR);
theme_menu->add_radio_check_item(TTR("Default theme"), THEME_PREVIEW_DEFAULT);
- p->add_child(theme_menu);
- p->add_submenu_item(TTR("Preview Theme"), "ThemeMenu");
+ p->add_submenu_node_item(TTR("Preview Theme"), theme_menu);
theme_preview = (ThemePreviewMode)(int)EditorSettings::get_singleton()->get_project_metadata("2d_editor", "theme_preview", THEME_PREVIEW_PROJECT);
for (int i = 0; i < THEME_PREVIEW_MAX; i++) {
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index d719850204..b71a11e166 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -723,7 +723,7 @@ void EditorPropertyOTFeatures::update_property() {
}
for (int i = 0; i < FGRP_MAX; i++) {
if (have_sub[i]) {
- menu->add_submenu_item(RTR(group_names[i]), "FTRMenu_" + itos(i));
+ menu->add_submenu_node_item(RTR(group_names[i]), menu_sub[i]);
}
}
@@ -851,7 +851,6 @@ EditorPropertyOTFeatures::EditorPropertyOTFeatures() {
for (int i = 0; i < FGRP_MAX; i++) {
menu_sub[i] = memnew(PopupMenu);
- menu_sub[i]->set_name("FTRMenu_" + itos(i));
menu->add_child(menu_sub[i]);
menu_sub[i]->connect("id_pressed", callable_mp(this, &EditorPropertyOTFeatures::_add_feature));
}
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 15a9cfad22..78825c99e6 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -5077,9 +5077,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
view_menu->set_shortcut_context(this);
vbox->add_child(view_menu);
- display_submenu = memnew(PopupMenu);
view_menu->get_popup()->set_hide_on_checkable_item_selection(false);
- view_menu->get_popup()->add_child(display_submenu);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM);
@@ -5104,6 +5102,8 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_lighting", TTR("Display Lighting")), VIEW_DISPLAY_LIGHTING);
view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_UNSHADED);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true);
+
+ display_submenu = memnew(PopupMenu);
display_submenu->set_hide_on_checkable_item_selection(false);
display_submenu->add_radio_check_item(TTR("Directional Shadow Splits"), VIEW_DISPLAY_DEBUG_PSSM_SPLITS);
display_submenu->add_separator();
@@ -5137,9 +5137,8 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
display_submenu->add_radio_check_item(TTR("Occlusion Culling Buffer"), VIEW_DISPLAY_DEBUG_OCCLUDERS);
display_submenu->add_radio_check_item(TTR("Motion Vectors"), VIEW_DISPLAY_MOTION_VECTORS);
display_submenu->add_radio_check_item(TTR("Internal Buffer"), VIEW_DISPLAY_INTERNAL_BUFFER);
+ view_menu->get_popup()->add_submenu_node_item(TTR("Display Advanced..."), display_submenu, VIEW_DISPLAY_ADVANCED);
- display_submenu->set_name("DisplayAdvanced");
- view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "DisplayAdvanced", VIEW_DISPLAY_ADVANCED);
view_menu->get_popup()->add_separator();
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT);
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_gizmos", TTR("View Gizmos")), VIEW_GIZMOS);
@@ -8518,7 +8517,10 @@ Node3DEditor::Node3DEditor() {
p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KeyModifierMask::CMD_OR_CTRL + Key::KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
p->add_separator();
- p->add_submenu_item(TTR("Gizmos"), "GizmosMenu");
+ gizmos_menu = memnew(PopupMenu);
+ gizmos_menu->set_hide_on_checkable_item_selection(false);
+ p->add_submenu_node_item(TTR("Gizmos"), gizmos_menu);
+ gizmos_menu->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_gizmo_toggled));
p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
@@ -8532,12 +8534,6 @@ Node3DEditor::Node3DEditor() {
p->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed));
- gizmos_menu = memnew(PopupMenu);
- p->add_child(gizmos_menu);
- gizmos_menu->set_name("GizmosMenu");
- gizmos_menu->set_hide_on_checkable_item_selection(false);
- gizmos_menu->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_gizmo_toggled));
-
/* REST OF MENU */
left_panel_split = memnew(HSplitContainer);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 32dae74ba4..d7d5381ed3 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -4004,12 +4004,11 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New Text File..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::N), FILE_NEW_TEXTFILE);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open...")), FILE_OPEN);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::T), FILE_REOPEN_CLOSED);
- file_menu->get_popup()->add_submenu_item(TTR("Open Recent"), "RecentScripts", FILE_OPEN_RECENT);
recent_scripts = memnew(PopupMenu);
- recent_scripts->set_name("RecentScripts");
- file_menu->get_popup()->add_child(recent_scripts);
+ file_menu->get_popup()->add_submenu_node_item(TTR("Open Recent"), recent_scripts, FILE_OPEN_RECENT);
recent_scripts->connect("id_pressed", callable_mp(this, &ScriptEditor::_open_recent_script));
+
_update_recent_scripts();
file_menu->get_popup()->add_separator();
@@ -4027,14 +4026,11 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KeyModifierMask::ALT | Key::RIGHT), WINDOW_NEXT);
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_submenu_item(TTR("Theme"), "Theme", FILE_THEME);
-
theme_submenu = memnew(PopupMenu);
- theme_submenu->set_name("Theme");
- file_menu->get_popup()->add_child(theme_submenu);
- theme_submenu->connect("id_pressed", callable_mp(this, &ScriptEditor::_theme_option));
theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/import_theme", TTR("Import Theme...")), THEME_IMPORT);
theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/reload_theme", TTR("Reload Theme")), THEME_RELOAD);
+ file_menu->get_popup()->add_submenu_node_item(TTR("Theme"), theme_submenu, FILE_THEME);
+ theme_submenu->connect("id_pressed", callable_mp(this, &ScriptEditor::_theme_option));
theme_submenu->add_separator();
theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme", TTR("Save Theme")), THEME_SAVE);
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index ce5535432c..ee347538a4 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -2255,7 +2255,6 @@ void ScriptTextEditor::_enable_code_editor() {
edit_menu->get_popup()->add_separator();
{
PopupMenu *sub_menu = memnew(PopupMenu);
- sub_menu->set_name("LineMenu");
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP);
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN);
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT);
@@ -2263,47 +2262,39 @@ void ScriptTextEditor::_enable_code_editor() {
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE);
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
- edit_menu->get_popup()->add_child(sub_menu);
- edit_menu->get_popup()->add_submenu_item(TTR("Line"), "LineMenu");
+ edit_menu->get_popup()->add_submenu_node_item(TTR("Line"), sub_menu);
}
{
PopupMenu *sub_menu = memnew(PopupMenu);
- sub_menu->set_name("FoldingMenu");
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE);
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES);
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES);
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/create_code_region"), EDIT_CREATE_CODE_REGION);
sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
- edit_menu->get_popup()->add_child(sub_menu);
- edit_menu->get_popup()->add_submenu_item(TTR("Folding"), "FoldingMenu");
+ edit_menu->get_popup()->add_submenu_node_item(TTR("Folding"), sub_menu);
}
edit_menu->get_popup()->add_separator();
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE);
{
PopupMenu *sub_menu = memnew(PopupMenu);
- sub_menu->set_name("IndentMenu");
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES);
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_tabs"), EDIT_CONVERT_INDENT_TO_TABS);
sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/auto_indent"), EDIT_AUTO_INDENT);
sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
- edit_menu->get_popup()->add_child(sub_menu);
- edit_menu->get_popup()->add_submenu_item(TTR("Indentation"), "IndentMenu");
+ edit_menu->get_popup()->add_submenu_node_item(TTR("Indentation"), sub_menu);
}
edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
edit_menu->get_popup()->add_separator();
{
PopupMenu *sub_menu = memnew(PopupMenu);
- sub_menu->set_name("ConvertCase");
sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4), EDIT_TO_UPPERCASE);
sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5), EDIT_TO_LOWERCASE);
sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6), EDIT_CAPITALIZE);
sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
- edit_menu->get_popup()->add_child(sub_menu);
- edit_menu->get_popup()->add_submenu_item(TTR("Convert Case"), "ConvertCase");
+ edit_menu->get_popup()->add_submenu_node_item(TTR("Convert Case"), sub_menu);
}
- edit_menu->get_popup()->add_child(highlighter_menu);
- edit_menu->get_popup()->add_submenu_item(TTR("Syntax Highlighter"), "HighlighterMenu");
+ edit_menu->get_popup()->add_submenu_node_item(TTR("Syntax Highlighter"), highlighter_menu);
highlighter_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_change_syntax_highlighter));
edit_hb->add_child(search_menu);
@@ -2325,14 +2316,12 @@ void ScriptTextEditor::_enable_code_editor() {
goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
goto_menu->get_popup()->add_separator();
- goto_menu->get_popup()->add_child(bookmarks_menu);
- goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "BookmarksMenu");
+ goto_menu->get_popup()->add_submenu_node_item(TTR("Bookmarks"), bookmarks_menu);
_update_bookmark_list();
bookmarks_menu->connect("about_to_popup", callable_mp(this, &ScriptTextEditor::_update_bookmark_list));
bookmarks_menu->connect("index_pressed", callable_mp(this, &ScriptTextEditor::_bookmark_item_pressed));
- goto_menu->get_popup()->add_child(breakpoints_menu);
- goto_menu->get_popup()->add_submenu_item(TTR("Breakpoints"), "BreakpointsMenu");
+ goto_menu->get_popup()->add_submenu_node_item(TTR("Breakpoints"), breakpoints_menu);
_update_breakpoint_list();
breakpoints_menu->connect("about_to_popup", callable_mp(this, &ScriptTextEditor::_update_breakpoint_list));
breakpoints_menu->connect("index_pressed", callable_mp(this, &ScriptTextEditor::_breakpoint_item_pressed));
@@ -2393,7 +2382,6 @@ ScriptTextEditor::ScriptTextEditor() {
edit_menu->set_shortcut_context(this);
highlighter_menu = memnew(PopupMenu);
- highlighter_menu->set_name("HighlighterMenu");
Ref<EditorPlainTextSyntaxHighlighter> plain_highlighter;
plain_highlighter.instantiate();
@@ -2415,10 +2403,7 @@ ScriptTextEditor::ScriptTextEditor() {
goto_menu->set_shortcut_context(this);
bookmarks_menu = memnew(PopupMenu);
- bookmarks_menu->set_name("BookmarksMenu");
-
breakpoints_menu = memnew(PopupMenu);
- breakpoints_menu->set_name("BreakpointsMenu");
connection_info_dialog = memnew(ConnectionInfoDialog);
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 465785a3b7..2eb914e976 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -654,18 +654,14 @@ TextEditor::TextEditor() {
edit_menu->get_popup()->add_separator();
PopupMenu *convert_case = memnew(PopupMenu);
- convert_case->set_name("ConvertCase");
- edit_menu->get_popup()->add_child(convert_case);
- edit_menu->get_popup()->add_submenu_item(TTR("Convert Case"), "ConvertCase");
+ edit_menu->get_popup()->add_submenu_node_item(TTR("Convert Case"), convert_case);
convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase")), EDIT_TO_UPPERCASE);
convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase")), EDIT_TO_LOWERCASE);
convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize")), EDIT_CAPITALIZE);
convert_case->connect("id_pressed", callable_mp(this, &TextEditor::_edit_option));
highlighter_menu = memnew(PopupMenu);
- highlighter_menu->set_name("HighlighterMenu");
- edit_menu->get_popup()->add_child(highlighter_menu);
- edit_menu->get_popup()->add_submenu_item(TTR("Syntax Highlighter"), "HighlighterMenu");
+ edit_menu->get_popup()->add_submenu_node_item(TTR("Syntax Highlighter"), highlighter_menu);
highlighter_menu->connect("id_pressed", callable_mp(this, &TextEditor::_change_syntax_highlighter));
Ref<EditorPlainTextSyntaxHighlighter> plain_highlighter;
@@ -703,9 +699,7 @@ TextEditor::TextEditor() {
goto_menu->get_popup()->add_separator();
bookmarks_menu = memnew(PopupMenu);
- bookmarks_menu->set_name("BookmarksMenu");
- goto_menu->get_popup()->add_child(bookmarks_menu);
- goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "BookmarksMenu");
+ goto_menu->get_popup()->add_submenu_node_item(TTR("Bookmarks"), bookmarks_menu);
_update_bookmark_list();
bookmarks_menu->connect("about_to_popup", callable_mp(this, &TextEditor::_update_bookmark_list));
bookmarks_menu->connect("index_pressed", callable_mp(this, &TextEditor::_bookmark_item_pressed));
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index ad64e1d1d7..ad8674207b 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -1161,9 +1161,7 @@ TextShaderEditor::TextShaderEditor() {
goto_menu->get_popup()->add_separator();
bookmarks_menu = memnew(PopupMenu);
- bookmarks_menu->set_name("BookmarksMenu");
- goto_menu->get_popup()->add_child(bookmarks_menu);
- goto_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "BookmarksMenu");
+ goto_menu->get_popup()->add_submenu_node_item(TTR("Bookmarks"), bookmarks_menu);
_update_bookmark_list();
bookmarks_menu->connect("about_to_popup", callable_mp(this, &TextShaderEditor::_update_bookmark_list));
bookmarks_menu->connect("index_pressed", callable_mp(this, &TextShaderEditor::_bookmark_item_pressed));
diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
index 205a5e8a20..61069604e5 100644
--- a/editor/plugins/theme_editor_preview.cpp
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -353,9 +353,7 @@ DefaultThemeEditorPreview::DefaultThemeEditorPreview() {
test_menu_button->get_popup()->add_separator(TTR("Named Separator"));
PopupMenu *test_submenu = memnew(PopupMenu);
- test_menu_button->get_popup()->add_child(test_submenu);
- test_submenu->set_name("SubMenu");
- test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "SubMenu");
+ test_menu_button->get_popup()->add_submenu_node_item(TTR("Submenu"), test_submenu);
test_submenu->add_item(TTR("Subitem 1"));
test_submenu->add_item(TTR("Subitem 2"));
first_vb->add_child(test_menu_button);
diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp
index d8d70e5b7d..19acc706f2 100644
--- a/editor/plugins/version_control_editor_plugin.cpp
+++ b/editor/plugins/version_control_editor_plugin.cpp
@@ -1444,18 +1444,14 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
extra_options_remove_branch_list = memnew(PopupMenu);
extra_options_remove_branch_list->connect(SNAME("id_pressed"), callable_mp(this, &VersionControlEditorPlugin::_popup_branch_remove_confirm));
- extra_options_remove_branch_list->set_name("RemoveBranch");
- extra_options->get_popup()->add_child(extra_options_remove_branch_list);
- extra_options->get_popup()->add_submenu_item(TTR("Remove Branch"), "RemoveBranch");
+ extra_options->get_popup()->add_submenu_node_item(TTR("Remove Branch"), extra_options_remove_branch_list);
extra_options->get_popup()->add_separator();
extra_options->get_popup()->add_item(TTR("Create New Remote"), EXTRA_OPTION_CREATE_REMOTE);
extra_options_remove_remote_list = memnew(PopupMenu);
extra_options_remove_remote_list->connect(SNAME("id_pressed"), callable_mp(this, &VersionControlEditorPlugin::_popup_remote_remove_confirm));
- extra_options_remove_remote_list->set_name("RemoveRemote");
- extra_options->get_popup()->add_child(extra_options_remove_remote_list);
- extra_options->get_popup()->add_submenu_item(TTR("Remove Remote"), "RemoveRemote");
+ extra_options->get_popup()->add_submenu_node_item(TTR("Remove Remote"), extra_options_remove_remote_list);
change_type_to_strings[EditorVCSInterface::CHANGE_TYPE_NEW] = TTR("New");
change_type_to_strings[EditorVCSInterface::CHANGE_TYPE_MODIFIED] = TTR("Modified");
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index acca5810a9..4261f6f32e 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -4209,18 +4209,15 @@ void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
popup_menu->add_separator("", NodeMenuOptions::SEPARATOR2);
if (selected_float_constant != -1) {
- popup_menu->add_submenu_item(TTR("Float Constants"), "FloatConstants", int(NodeMenuOptions::FLOAT_CONSTANTS));
-
if (!constants_submenu) {
constants_submenu = memnew(PopupMenu);
- constants_submenu->set_name("FloatConstants");
for (int i = 0; i < MAX_FLOAT_CONST_DEFS; i++) {
constants_submenu->add_item(float_constant_defs[i].name, i);
}
- popup_menu->add_child(constants_submenu);
constants_submenu->connect("index_pressed", callable_mp(this, &VisualShaderEditor::_float_constant_selected));
}
+ popup_menu->add_submenu_node_item(TTR("Float Constants"), constants_submenu, int(NodeMenuOptions::FLOAT_CONSTANTS));
}
if (selected_constants.size() > 0) {
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 023d9e9f00..f8e0628072 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -3263,7 +3263,7 @@ void SceneTreeDock::_add_children_to_popup(Object *p_obj, int p_depth) {
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(obj);
if (menu->get_item_count() == 0) {
- menu->add_submenu_item(TTR("Sub-Resources"), "SubResources");
+ menu->add_submenu_node_item(TTR("Sub-Resources"), menu_subresources);
}
menu_subresources->add_icon_item(icon, E.name.capitalize(), EDIT_SUBRESOURCE_BASE + subresources.size());
menu_subresources->set_item_indent(-1, p_depth);
@@ -3504,11 +3504,9 @@ void SceneTreeDock::_update_tree_menu() {
tree_menu->set_item_tooltip(tree_menu->get_item_index(TOOL_CENTER_PARENT), TTR("If enabled, Reparent to New Node will create the new node in the center of the selected nodes, if possible."));
PopupMenu *resource_list = memnew(PopupMenu);
- resource_list->set_name("AllResources");
resource_list->connect("about_to_popup", callable_mp(this, &SceneTreeDock::_list_all_subresources).bind(resource_list));
resource_list->connect("index_pressed", callable_mp(this, &SceneTreeDock::_edit_subresource).bind(resource_list));
- tree_menu->add_child(resource_list);
- tree_menu->add_submenu_item(TTR("All Scene Sub-Resources"), "AllResources");
+ tree_menu->add_submenu_node_item(TTR("All Scene Sub-Resources"), resource_list);
}
void SceneTreeDock::_filter_changed(const String &p_filter) {
@@ -4365,7 +4363,6 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
menu->connect("id_pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(false));
menu_subresources = memnew(PopupMenu);
- menu_subresources->set_name("SubResources");
menu_subresources->connect("id_pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(false));
menu->add_child(menu_subresources);
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index 1bb1bdcd70..260e36ac3d 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -160,3 +160,47 @@ Validate extension JSON: JSON file: Field was added in a way that breaks compati
Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/RenderSceneBuffersRD/methods/get_velocity_texture': arguments
MSAA flag was added, compatibility functions exist for these.
+
+
+GH-84906
+--------
+Validate extension JSON: Error: Field 'classes/AnimationPlayer/methods/play/arguments/0': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/AnimationPlayer/methods/play_backwards/arguments/0': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/CodeEdit/methods/add_code_completion_option/arguments/5': default_value changed value in new API, from "0" to "null".
+Validate extension JSON: Error: Field 'classes/Control/methods/get_theme_color/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/get_theme_constant/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/get_theme_font/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/get_theme_font_size/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/get_theme_icon/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/get_theme_stylebox/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/has_theme_color/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/has_theme_constant/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/has_theme_font/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/has_theme_font_size/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/has_theme_icon/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Control/methods/has_theme_stylebox/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Object/methods/tr/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Object/methods/tr_n/arguments/3': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Translation/methods/add_message/arguments/2': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Translation/methods/add_plural_message/arguments/2': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Translation/methods/erase_message/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Translation/methods/get_message/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Translation/methods/get_plural_message/arguments/3': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/TranslationServer/methods/translate/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/TranslationServer/methods/translate_plural/arguments/3': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/get_theme_color/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/get_theme_constant/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/get_theme_font/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/get_theme_font_size/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/get_theme_icon/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/get_theme_stylebox/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/has_theme_color/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/has_theme_constant/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/has_theme_font/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/has_theme_font_size/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/has_theme_icon/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+Validate extension JSON: Error: Field 'classes/Window/methods/has_theme_stylebox/arguments/1': default_value changed value in new API, from "\"\"" to "&\"\"".
+
+Fix the default parameter value for StringName and Variant.
+The changes to StringName parameters should be equivalent to the previous default values.
+The change to the Variant parameter in 'add_code_completion_option' breaks behavior compatibility.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Compat.cs b/modules/mono/glue/GodotSharp/GodotSharp/Compat.cs
index 9126495a27..daaf4730fd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Compat.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Compat.cs
@@ -66,7 +66,7 @@ partial class AnimationTree
partial class CodeEdit
{
- /// <inheritdoc cref="AddCodeCompletionOption(CodeCompletionKind, string, string, Nullable{Color}, Resource, Nullable{Variant}, int)"/>
+ /// <inheritdoc cref="AddCodeCompletionOption(CodeCompletionKind, string, string, Nullable{Color}, Resource, Variant, int)"/>
[EditorBrowsable(EditorBrowsableState.Never)]
public void AddCodeCompletionOption(CodeCompletionKind type, string displayText, string insertText, Nullable<Color> textColor, Resource icon, Nullable<Variant> value)
{
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index ccf97662e2..e978c012b5 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -1532,7 +1532,13 @@ void OpenXRAPI::register_extension_metadata() {
void OpenXRAPI::cleanup_extension_wrappers() {
for (OpenXRExtensionWrapper *extension_wrapper : registered_extension_wrappers) {
- memdelete(extension_wrapper);
+ // Fix crash when the extension wrapper comes from GDExtension.
+ OpenXRExtensionWrapperExtension *gdextension_extension_wrapper = dynamic_cast<OpenXRExtensionWrapperExtension *>(extension_wrapper);
+ if (gdextension_extension_wrapper) {
+ memdelete(gdextension_extension_wrapper);
+ } else {
+ memdelete(extension_wrapper);
+ }
}
registered_extension_wrappers.clear();
}
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index c06202ed19..ea88278a17 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -4588,6 +4588,12 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end)) {
// Copy glyphs.
Glyph gl = sd_glyphs[j];
+ if (gl.end == p_start + p_length && ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN)) {
+ uint32_t index = font_get_glyph_index(gl.font_rid, gl.font_size, 0x00ad, 0);
+ float w = font_get_glyph_advance(gl.font_rid, gl.font_size, index)[(p_new_sd->orientation == ORIENTATION_HORIZONTAL) ? 0 : 1];
+ gl.index = index;
+ gl.advance = w;
+ }
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
@@ -4708,22 +4714,22 @@ double TextServerAdvanced::_shaped_text_fit_to_width(const RID &p_shaped, double
if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) {
// Trim spaces.
- while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
sd->glyphs.write[start_pos].advance = 0;
start_pos += sd->glyphs[start_pos].count;
}
- while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
justification_width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;
sd->glyphs.write[end_pos].advance = 0;
end_pos -= sd->glyphs[end_pos].count;
}
} else {
// Skip breaks, but do not reset size.
- while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
start_pos += sd->glyphs[start_pos].count;
}
- while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= sd->glyphs[end_pos].count;
}
}
@@ -4739,7 +4745,7 @@ double TextServerAdvanced::_shaped_text_fit_to_width(const RID &p_shaped, double
elongation_count++;
}
}
- if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
+ if ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN && (gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
space_count++;
}
}
@@ -4772,7 +4778,7 @@ double TextServerAdvanced::_shaped_text_fit_to_width(const RID &p_shaped, double
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
if (gl.count > 0) {
- if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
+ if ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN && (gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
double old_adv = gl.advance;
double new_advance;
if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
@@ -5420,6 +5426,9 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
if (c == 0x0009 || c == 0x000b) {
sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_TAB;
}
+ if (c == 0x00ad) {
+ sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_SOFT_HYPHEN;
+ }
if (is_whitespace(c)) {
sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_SPACE;
}
@@ -5441,7 +5450,7 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
if (sd->breaks.has(sd_glyphs[i].end)) {
if (sd->breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_BREAK_HARD;
- } else if (is_whitespace(c)) {
+ } else if (is_whitespace(c) || c == 0x00ad) {
sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_BREAK_SOFT;
} else {
int count = sd_glyphs[i].count;
@@ -5660,7 +5669,7 @@ bool TextServerAdvanced::_shaped_text_update_justification_ops(const RID &p_shap
sd_glyphs[i].flags |= GRAPHEME_IS_ELONGATION;
}
if (sd->jstops.has(sd_glyphs[i].start)) {
- if (c == 0xfffc) {
+ if (c == 0xfffc || c == 0x00ad) {
continue;
}
if (sd->jstops[sd_glyphs[i].start]) {
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 06bc7b1cf8..aa37df6752 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -3367,6 +3367,10 @@ RID TextServerFallback::_shaped_text_substr(const RID &p_shaped, int64_t p_start
for (int i = 0; i < sd_size; i++) {
if ((sd_glyphs[i].start >= new_sd->start) && (sd_glyphs[i].end <= new_sd->end)) {
Glyph gl = sd_glyphs[i];
+ if (gl.end == p_start + p_length && ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN)) {
+ gl.index = 0x00ad;
+ gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, 0x00ad).x;
+ }
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
@@ -3480,22 +3484,22 @@ double TextServerFallback::_shaped_text_fit_to_width(const RID &p_shaped, double
if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) {
// Trim spaces.
- while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
sd->glyphs.write[start_pos].advance = 0;
start_pos += sd->glyphs[start_pos].count;
}
- while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
justification_width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;
sd->glyphs.write[end_pos].advance = 0;
end_pos -= sd->glyphs[end_pos].count;
}
} else {
// Skip breaks, but do not reset size.
- while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD)) {
+ while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
start_pos += sd->glyphs[start_pos].count;
}
- while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD)) {
+ while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= sd->glyphs[end_pos].count;
}
}
@@ -3504,7 +3508,7 @@ double TextServerFallback::_shaped_text_fit_to_width(const RID &p_shaped, double
for (int i = start_pos; i <= end_pos; i++) {
const Glyph &gl = sd->glyphs[i];
if (gl.count > 0) {
- if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
+ if ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN && (gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
space_count++;
}
}
@@ -3515,7 +3519,7 @@ double TextServerFallback::_shaped_text_fit_to_width(const RID &p_shaped, double
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
if (gl.count > 0) {
- if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
+ if ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN && (gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE && (gl.flags & GRAPHEME_IS_PUNCTUATION) != GRAPHEME_IS_PUNCTUATION) {
double old_adv = gl.advance;
gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size));
justification_width += (gl.advance - old_adv);
@@ -3641,6 +3645,9 @@ bool TextServerFallback::_shaped_text_update_breaks(const RID &p_shaped) {
if (c == 0x0009 || c == 0x000b) {
sd_glyphs[i].flags |= GRAPHEME_IS_TAB;
}
+ if (c == 0x00ad) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_SOFT_HYPHEN;
+ }
i += (sd_glyphs[i].count - 1);
}
diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp
index be86872a59..272852e8fa 100644
--- a/scene/3d/visible_on_screen_notifier_3d.cpp
+++ b/scene/3d/visible_on_screen_notifier_3d.cpp
@@ -79,16 +79,6 @@ void VisibleOnScreenNotifier3D::_notification(int p_what) {
}
}
-PackedStringArray VisibleOnScreenNotifier3D::get_configuration_warnings() const {
- PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
-
- if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
- warnings.push_back(RTR("VisibleOnScreenNotifier3D nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release."));
- }
-
- return warnings;
-}
-
void VisibleOnScreenNotifier3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_aabb", "rect"), &VisibleOnScreenNotifier3D::set_aabb);
ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibleOnScreenNotifier3D::is_on_screen);
diff --git a/scene/3d/visible_on_screen_notifier_3d.h b/scene/3d/visible_on_screen_notifier_3d.h
index 85156c256e..7115de536f 100644
--- a/scene/3d/visible_on_screen_notifier_3d.h
+++ b/scene/3d/visible_on_screen_notifier_3d.h
@@ -57,8 +57,6 @@ public:
virtual AABB get_aabb() const override;
bool is_on_screen() const;
- virtual PackedStringArray get_configuration_warnings() const override;
-
VisibleOnScreenNotifier3D();
~VisibleOnScreenNotifier3D();
};
diff --git a/scene/animation/animation_player.compat.inc b/scene/animation/animation_player.compat.inc
index 974eb2a7d8..39efacc4ca 100644
--- a/scene/animation/animation_player.compat.inc
+++ b/scene/animation/animation_player.compat.inc
@@ -58,6 +58,14 @@ 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);
@@ -66,6 +74,8 @@ 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/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index a57ce39336..d6b7be7020 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -813,8 +813,8 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_blend_time", "sec"), &AnimationPlayer::set_default_blend_time);
ClassDB::bind_method(D_METHOD("get_default_blend_time"), &AnimationPlayer::get_default_blend_time);
- ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(""), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(""), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(StringName()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("play_with_capture", "name", "duration", "custom_blend", "custom_speed", "from_end", "trans_type", "ease_type"), &AnimationPlayer::play_with_capture, DEFVAL(-1.0), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false), DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);
ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false));
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 24a5351fb7..f7c05b23d7 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -142,6 +142,8 @@ protected:
void _set_root_bind_compat_80813(const NodePath &p_root);
NodePath _get_root_bind_compat_80813() const;
void _seek_bind_compat_80813(double p_time, bool p_update = false);
+ void _play_compat_84906(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
+ void _play_backwards_compat_84906(const StringName &p_name = StringName(), double p_custom_blend = -1);
static void _bind_compatibility_methods();
#endif // DISABLE_DEPRECATED
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index a4eddb832d..978fb56f78 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -128,6 +128,15 @@ void Button::_notification(int p_what) {
queue_redraw();
} break;
+ case NOTIFICATION_RESIZED: {
+ if (autowrap_mode != TextServer::AUTOWRAP_OFF) {
+ _shape();
+
+ update_minimum_size();
+ queue_redraw();
+ }
+ } break;
+
case NOTIFICATION_DRAW: {
const RID ci = get_canvas_item();
const Size2 size = get_size();
@@ -261,7 +270,7 @@ void Button::_notification(int p_what) {
} break;
}
- const bool is_clipped = clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING;
+ const bool is_clipped = clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING || autowrap_mode != TextServer::AUTOWRAP_OFF;
const Size2 custom_element_size = drawable_size_remained;
// Draw the icon.
@@ -415,7 +424,7 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
}
Size2 minsize = paragraph->get_size();
- if (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
+ if (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING || autowrap_mode != TextServer::AUTOWRAP_OFF) {
minsize.width = 0;
}
@@ -468,6 +477,23 @@ void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) {
return;
}
+ BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY;
+ switch (autowrap_mode) {
+ case TextServer::AUTOWRAP_WORD_SMART:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY;
+ break;
+ case TextServer::AUTOWRAP_WORD:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case TextServer::AUTOWRAP_ARBITRARY:
+ autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case TextServer::AUTOWRAP_OFF:
+ break;
+ }
+ autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
+ p_paragraph->set_break_flags(autowrap_flags);
+
if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
} else {
@@ -510,6 +536,19 @@ String Button::get_text() const {
return text;
}
+void Button::set_autowrap_mode(TextServer::AutowrapMode p_mode) {
+ if (autowrap_mode != p_mode) {
+ autowrap_mode = p_mode;
+ _shape();
+ queue_redraw();
+ update_minimum_size();
+ }
+}
+
+TextServer::AutowrapMode Button::get_autowrap_mode() const {
+ return autowrap_mode;
+}
+
void Button::set_text_direction(Control::TextDirection p_text_direction) {
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (text_direction != p_text_direction) {
@@ -649,6 +688,8 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text);
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &Button::set_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &Button::get_text_overrun_behavior);
+ ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Button::set_autowrap_mode);
+ ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Button::get_autowrap_mode);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Button::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction"), &Button::get_text_direction);
ClassDB::bind_method(D_METHOD("set_language", "language"), &Button::set_language);
@@ -675,6 +716,7 @@ void Button::_bind_methods() {
ADD_GROUP("Text Behavior", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_alignment", "get_text_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
ADD_GROUP("Icon Behavior", "");
diff --git a/scene/gui/button.h b/scene/gui/button.h
index d0243a9eeb..6efbafe0ad 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -45,6 +45,7 @@ private:
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
+ TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
Ref<Texture2D> icon;
@@ -120,6 +121,9 @@ public:
void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior);
TextServer::OverrunBehavior get_text_overrun_behavior() const;
+ void set_autowrap_mode(TextServer::AutowrapMode p_mode);
+ TextServer::AutowrapMode get_autowrap_mode() const;
+
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
diff --git a/scene/gui/code_edit.compat.inc b/scene/gui/code_edit.compat.inc
index 9107d6523f..8dd7c00aa4 100644
--- a/scene/gui/code_edit.compat.inc
+++ b/scene/gui/code_edit.compat.inc
@@ -34,8 +34,13 @@ String CodeEdit::_get_text_for_symbol_lookup_bind_compat_73196() {
return get_text_for_symbol_lookup();
}
+void CodeEdit::_add_code_completion_option_compat_84906(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color, const Ref<Resource> &p_icon, const Variant &p_value, int p_location) {
+ add_code_completion_option(p_type, p_display_text, p_insert_text, p_text_color, p_icon, p_value, p_location);
+}
+
void CodeEdit::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("get_text_for_symbol_lookup"), &CodeEdit::_get_text_for_symbol_lookup_bind_compat_73196);
+ ClassDB::bind_compatibility_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value", "location"), &CodeEdit::_add_code_completion_option_compat_84906, DEFVAL(Color(1, 1, 1)), DEFVAL(Ref<Resource>()), DEFVAL(Variant::NIL), DEFVAL(LOCATION_OTHER));
}
#endif
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index f50ac4a0ee..73cbfceea4 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -2636,7 +2636,7 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text_for_code_completion"), &CodeEdit::get_text_for_code_completion);
ClassDB::bind_method(D_METHOD("request_code_completion", "force"), &CodeEdit::request_code_completion, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value", "location"), &CodeEdit::add_code_completion_option, DEFVAL(Color(1, 1, 1)), DEFVAL(Ref<Resource>()), DEFVAL(Variant::NIL), DEFVAL(LOCATION_OTHER));
+ ClassDB::bind_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value", "location"), &CodeEdit::add_code_completion_option, DEFVAL(Color(1, 1, 1)), DEFVAL(Ref<Resource>()), DEFVAL(Variant()), DEFVAL(LOCATION_OTHER));
ClassDB::bind_method(D_METHOD("update_code_completion_options", "force"), &CodeEdit::update_code_completion_options);
ClassDB::bind_method(D_METHOD("get_code_completion_options"), &CodeEdit::get_code_completion_options);
ClassDB::bind_method(D_METHOD("get_code_completion_option", "index"), &CodeEdit::get_code_completion_option);
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index 4b0629d29d..1770d4f4d8 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -305,6 +305,7 @@ protected:
#ifndef DISABLE_DEPRECATED
String _get_text_for_symbol_lookup_bind_compat_73196();
+ void _add_code_completion_option_compat_84906(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color = Color(1, 1, 1), const Ref<Resource> &p_icon = Ref<Resource>(), const Variant &p_value = Variant::NIL, int p_location = LOCATION_OTHER);
static void _bind_compatibility_methods();
#endif
@@ -462,7 +463,7 @@ public:
void request_code_completion(bool p_force = false);
- void add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color = Color(1, 1, 1), const Ref<Resource> &p_icon = Ref<Resource>(), const Variant &p_value = Variant::NIL, int p_location = LOCATION_OTHER);
+ void add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color = Color(1, 1, 1), const Ref<Resource> &p_icon = Ref<Resource>(), const Variant &p_value = Variant(), int p_location = LOCATION_OTHER);
void update_code_completion_options(bool p_forced = false);
TypedArray<Dictionary> get_code_completion_options() const;
diff --git a/scene/gui/control.compat.inc b/scene/gui/control.compat.inc
new file mode 100644
index 0000000000..96ee720d90
--- /dev/null
+++ b/scene/gui/control.compat.inc
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* 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 b351d1ee4f..235895ce47 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "control.h"
+#include "control.compat.inc"
#include "container.h"
#include "core/config/project_settings.h"
@@ -3418,12 +3419,12 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_theme_color_override", "name"), &Control::remove_theme_color_override);
ClassDB::bind_method(D_METHOD("remove_theme_constant_override", "name"), &Control::remove_theme_constant_override);
- ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Control::get_theme_icon, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Control::get_theme_stylebox, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_font", "name", "theme_type"), &Control::get_theme_font, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Control::get_theme_font_size, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_color", "name", "theme_type"), &Control::get_theme_color, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Control::get_theme_constant, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Control::get_theme_icon, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Control::get_theme_stylebox, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_font", "name", "theme_type"), &Control::get_theme_font, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Control::get_theme_font_size, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_color", "name", "theme_type"), &Control::get_theme_color, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Control::get_theme_constant, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("has_theme_icon_override", "name"), &Control::has_theme_icon_override);
ClassDB::bind_method(D_METHOD("has_theme_stylebox_override", "name"), &Control::has_theme_stylebox_override);
@@ -3432,12 +3433,12 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_theme_color_override", "name"), &Control::has_theme_color_override);
ClassDB::bind_method(D_METHOD("has_theme_constant_override", "name"), &Control::has_theme_constant_override);
- ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Control::has_theme_icon, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Control::has_theme_stylebox, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_font", "name", "theme_type"), &Control::has_theme_font, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Control::has_theme_font_size, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Control::has_theme_color, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Control::has_theme_constant, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Control::has_theme_icon, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Control::has_theme_stylebox, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_font", "name", "theme_type"), &Control::has_theme_font, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Control::has_theme_font_size, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Control::has_theme_color, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Control::has_theme_constant, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("get_theme_default_base_scale"), &Control::get_theme_default_base_scale);
ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Control::get_theme_default_font);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index a900a593dd..aa5147b345 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -344,6 +344,10 @@ 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 d707a98e14..75474c060e 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -2406,15 +2406,12 @@ void LineEdit::_generate_context_menu() {
add_child(menu, false, INTERNAL_MODE_FRONT);
menu_dir = memnew(PopupMenu);
- menu_dir->set_name("DirMenu");
menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED);
menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO);
menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR);
menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL);
- menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT);
menu_ctl = memnew(PopupMenu);
- menu_ctl->set_name("CTLMenu");
menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM);
menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM);
menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE);
@@ -2433,7 +2430,6 @@ void LineEdit::_generate_context_menu() {
menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ);
menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
- menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT);
menu->add_item(RTR("Cut"), MENU_CUT);
menu->add_item(RTR("Copy"), MENU_COPY);
@@ -2445,10 +2441,10 @@ void LineEdit::_generate_context_menu() {
menu->add_item(RTR("Undo"), MENU_UNDO);
menu->add_item(RTR("Redo"), MENU_REDO);
menu->add_separator();
- menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu", MENU_SUBMENU_TEXT_DIR);
+ menu->add_submenu_node_item(RTR("Text Writing Direction"), menu_dir, MENU_SUBMENU_TEXT_DIR);
menu->add_separator();
menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC);
- menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu", MENU_SUBMENU_INSERT_UCC);
+ menu->add_submenu_node_item(RTR("Insert Control Character"), menu_ctl, MENU_SUBMENU_INSERT_UCC);
menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option));
menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option));
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index fda909af79..e69f713798 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -111,13 +111,10 @@ String PopupMenu::bind_global_menu() {
ds->global_menu_add_separator(global_menu_name);
} else {
int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), item.shortcut_is_global ? callable_mp(this, &PopupMenu::activate_item) : Callable(), i);
- if (!item.submenu.is_empty()) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu));
- if (pm) {
- String submenu_name = pm->bind_global_menu();
- ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name);
- item.submenu_bound = true;
- }
+ if (item.submenu) {
+ String submenu_name = item.submenu->bind_global_menu();
+ ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name);
+ item.submenu_bound = true;
}
if (item.checkable_type == Item::CHECKABLE_TYPE_CHECK_BOX) {
ds->global_menu_set_item_checkable(global_menu_name, index, true);
@@ -158,11 +155,8 @@ void PopupMenu::unbind_global_menu() {
for (int i = 0; i < items.size(); i++) {
Item &item = items.write[i];
- if (!item.submenu.is_empty()) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu));
- if (pm) {
- pm->unbind_global_menu();
- }
+ if (item.submenu) {
+ item.submenu->unbind_global_menu();
item.submenu_bound = false;
}
}
@@ -251,7 +245,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
accel_max_w = MAX(accel_w, accel_max_w);
}
- if (!items[i].submenu.is_empty()) {
+ if (items[i].submenu) {
item_size.width += theme_cache.submenu->get_width();
}
@@ -335,10 +329,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
}
void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
- Node *n = get_node_or_null(items[p_over].submenu);
- ERR_FAIL_NULL_MSG(n, "Item subnode does not exist: '" + items[p_over].submenu + "'.");
- Popup *submenu_popup = Object::cast_to<Popup>(n);
- ERR_FAIL_NULL_MSG(submenu_popup, "Item subnode is not a Popup: '" + items[p_over].submenu + "'.");
+ Popup *submenu_popup = items[p_over].submenu;
if (submenu_popup->is_visible()) {
return; // Already visible.
}
@@ -551,7 +542,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
}
}
} else if (p_event->is_action("ui_right", true) && p_event->is_pressed()) {
- if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) {
+ if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && items[mouse_over].submenu && submenu_over != mouse_over) {
_activate_submenu(mouse_over, true);
set_input_as_handled();
} else {
@@ -564,7 +555,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
}
} else if (p_event->is_action("ui_accept", true) && p_event->is_pressed()) {
if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) {
- if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) {
+ if (items[mouse_over].submenu && submenu_over != mouse_over) {
_activate_submenu(mouse_over, true);
} else {
activate_item(mouse_over);
@@ -620,7 +611,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
return;
}
- if (!items[over].submenu.is_empty()) {
+ if (items[over].submenu) {
_activate_submenu(over);
return;
}
@@ -657,7 +648,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
return;
}
- if (!items[over].submenu.is_empty() && submenu_over != over) {
+ if (items[over].submenu && submenu_over != over) {
submenu_over = over;
submenu_timer->start();
}
@@ -858,7 +849,7 @@ void PopupMenu::_draw_items() {
}
// Submenu arrow on right hand side.
- if (!items[i].submenu.is_empty()) {
+ if (items[i].submenu) {
if (rtl) {
submenu->draw(ci, Point2(scroll_width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
} else {
@@ -974,11 +965,13 @@ void PopupMenu::_menu_changed() {
void PopupMenu::add_child_notify(Node *p_child) {
Window::add_child_notify(p_child);
- if (Object::cast_to<PopupMenu>(p_child) && !global_menu_name.is_empty()) {
- String node_name = p_child->get_name();
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(node_name));
+ PopupMenu *pm = Object::cast_to<PopupMenu>(p_child);
+ if (!pm) {
+ return;
+ }
+ if (!global_menu_name.is_empty()) {
for (int i = 0; i < items.size(); i++) {
- if (items[i].submenu == node_name) {
+ if (items[i].submenu == p_child) {
String submenu_name = pm->bind_global_menu();
DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, i, submenu_name);
items.write[i].submenu_bound = true;
@@ -996,9 +989,8 @@ void PopupMenu::remove_child_notify(Node *p_child) {
return;
}
if (Object::cast_to<PopupMenu>(p_child) && !global_menu_name.is_empty()) {
- String node_name = p_child->get_name();
for (int i = 0; i < items.size(); i++) {
- if (items[i].submenu == node_name) {
+ if (items[i].submenu == p_child) {
DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, i, String());
items.write[i].submenu_bound = false;
}
@@ -1056,7 +1048,7 @@ void PopupMenu::_notification(int p_what) {
} break;
case NOTIFICATION_WM_MOUSE_EXIT: {
- if (mouse_over >= 0 && (items[mouse_over].submenu.is_empty() || submenu_over != -1)) {
+ if (mouse_over >= 0 && (!items[mouse_over].submenu || submenu_over != -1)) {
mouse_over = -1;
control->queue_redraw();
}
@@ -1166,21 +1158,10 @@ void PopupMenu::_notification(int p_what) {
}
for (int i = 0; i < items.size(); i++) {
- if (items[i].submenu.is_empty()) {
+ if (!items[i].submenu) {
continue;
}
-
- Node *n = get_node(items[i].submenu);
- if (!n) {
- continue;
- }
-
- PopupMenu *pm = Object::cast_to<PopupMenu>(n);
- if (!pm || !pm->is_visible()) {
- continue;
- }
-
- pm->hide();
+ items[i].submenu->hide();
}
set_process_internal(false);
@@ -1563,9 +1544,18 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons
}
void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) {
- String submenu_name_safe = p_submenu.replace("@", "_"); // Allow special characters for auto-generated names.
- if (submenu_name_safe.validate_node_name() != submenu_name_safe) {
- ERR_FAIL_MSG(vformat("Invalid node name '%s' for a submenu, the following characters are not allowed:\n%s", p_submenu, String::get_invalid_node_name_characters(true)));
+ PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(p_submenu));
+ ERR_FAIL_NULL_MSG(pm, vformat("Child PopupMenu \"%s\" does not exist.", p_submenu));
+
+ add_submenu_node_item(p_label, pm, p_id);
+}
+
+void PopupMenu::add_submenu_node_item(const String &p_label, PopupMenu *p_submenu, int p_id) {
+ ERR_FAIL_NULL(p_submenu);
+
+ if (p_submenu->get_parent() != this) {
+ ERR_FAIL_COND_MSG(p_submenu->get_parent() != nullptr, vformat("The submenu \"%s\" already has a different parent.", p_submenu->get_name()));
+ add_child(p_submenu);
}
Item item;
@@ -1573,17 +1563,15 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
item.xl_text = atr(p_label);
item.id = p_id == -1 ? items.size() : p_id;
item.submenu = p_submenu;
+ item.submenu_name = p_submenu->get_name();
items.push_back(item);
if (!global_menu_name.is_empty()) {
DisplayServer *ds = DisplayServer::get_singleton();
int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1);
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu)); // Find first menu with this name.
- if (pm) {
- String submenu_name = pm->bind_global_menu();
- ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name);
- items.write[index].submenu_bound = true;
- }
+ String submenu_name = p_submenu->bind_global_menu();
+ ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name);
+ items.write[index].submenu_bound = true;
}
_shape_item(items.size() - 1);
@@ -1804,18 +1792,31 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
}
ERR_FAIL_INDEX(p_idx, items.size());
- if (items[p_idx].submenu == p_submenu) {
+ if (items[p_idx].submenu_name == p_submenu) {
return;
}
- String submenu_name_safe = p_submenu.replace("@", "_"); // Allow special characters for auto-generated names.
- if (submenu_name_safe.validate_node_name() != submenu_name_safe) {
- ERR_FAIL_MSG(vformat("Invalid node name '%s' for a submenu, the following characters are not allowed:\n%s", p_submenu, String::get_invalid_node_name_characters(true)));
+ PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(p_submenu));
+ ERR_FAIL_NULL_MSG(pm, vformat("Child PopupMenu \"%s\" does not exist.", p_submenu));
+
+ set_item_submenu_node(p_idx, pm);
+}
+
+void PopupMenu::set_item_submenu_node(int p_idx, PopupMenu *p_submenu) {
+ ERR_FAIL_NULL(p_submenu);
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
+ ERR_FAIL_INDEX(p_idx, items.size());
+
+ if (p_submenu->get_parent() != this) {
+ ERR_FAIL_COND_MSG(p_submenu->get_parent() != nullptr, vformat("The submenu \"%s\" already has a different parent.", p_submenu->get_name()));
+ add_child(p_submenu);
}
if (!global_menu_name.is_empty()) {
if (items[p_idx].submenu_bound) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(items[p_idx].submenu));
+ PopupMenu *pm = items[p_idx].submenu;
if (pm) {
DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, String());
pm->unbind_global_menu();
@@ -1827,13 +1828,10 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
items.write[p_idx].submenu = p_submenu;
if (!global_menu_name.is_empty()) {
- if (!items[p_idx].submenu.is_empty()) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(items[p_idx].submenu));
- if (pm) {
- String submenu_name = pm->bind_global_menu();
- DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, submenu_name);
- items.write[p_idx].submenu_bound = true;
- }
+ if (items[p_idx].submenu) {
+ String submenu_name = p_submenu->bind_global_menu();
+ DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, submenu_name);
+ items.write[p_idx].submenu_bound = true;
}
}
@@ -1937,6 +1935,11 @@ int PopupMenu::get_item_index(int p_id) const {
String PopupMenu::get_item_submenu(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), "");
+ return items[p_idx].submenu_name;
+}
+
+PopupMenu *PopupMenu::get_item_submenu_node(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), nullptr);
return items[p_idx].submenu;
}
@@ -2333,18 +2336,8 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo
return true;
}
- if (!items[i].submenu.is_empty()) {
- Node *n = get_node(items[i].submenu);
- if (!n) {
- continue;
- }
-
- PopupMenu *pm = Object::cast_to<PopupMenu>(n);
- if (!pm) {
- continue;
- }
-
- if (pm->activate_item_by_event(p_event, p_for_global_only)) {
+ if (items[i].submenu) {
+ if (items[i].submenu->activate_item_by_event(p_event, p_for_global_only)) {
return true;
}
}
@@ -2458,12 +2451,9 @@ void PopupMenu::clear(bool p_free_submenus) {
_unref_shortcut(I.shortcut);
}
- if (p_free_submenus && !I.submenu.is_empty()) {
- Node *submenu = get_node_or_null(I.submenu);
- if (submenu) {
- remove_child(submenu);
- submenu->queue_free();
- }
+ if (p_free_submenus && I.submenu) {
+ remove_child(I.submenu);
+ I.submenu->queue_free();
}
}
@@ -2471,11 +2461,8 @@ void PopupMenu::clear(bool p_free_submenus) {
DisplayServer *ds = DisplayServer::get_singleton();
for (int i = items.size() - 1; i >= 0; i--) {
Item &item = items.write[i];
- if (!item.submenu.is_empty()) {
- PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu));
- if (pm) {
- pm->unbind_global_menu();
- }
+ if (item.submenu) {
+ item.submenu->unbind_global_menu();
item.submenu_bound = false;
}
ds->global_menu_remove_item(global_menu_name, i);
@@ -2654,6 +2641,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_icon_radio_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_radio_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("add_submenu_node_item", "label", "submenu", "id"), &PopupMenu::add_submenu_node_item, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("set_item_text", "index", "text"), &PopupMenu::set_item_text);
ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction);
@@ -2667,6 +2655,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_metadata", "index", "metadata"), &PopupMenu::set_item_metadata);
ClassDB::bind_method(D_METHOD("set_item_disabled", "index", "disabled"), &PopupMenu::set_item_disabled);
ClassDB::bind_method(D_METHOD("set_item_submenu", "index", "submenu"), &PopupMenu::set_item_submenu);
+ ClassDB::bind_method(D_METHOD("set_item_submenu_node", "index", "submenu"), &PopupMenu::set_item_submenu_node);
ClassDB::bind_method(D_METHOD("set_item_as_separator", "index", "enable"), &PopupMenu::set_item_as_separator);
ClassDB::bind_method(D_METHOD("set_item_as_checkable", "index", "enable"), &PopupMenu::set_item_as_checkable);
ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "index", "enable"), &PopupMenu::set_item_as_radio_checkable);
@@ -2693,6 +2682,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_metadata", "index"), &PopupMenu::get_item_metadata);
ClassDB::bind_method(D_METHOD("is_item_disabled", "index"), &PopupMenu::is_item_disabled);
ClassDB::bind_method(D_METHOD("get_item_submenu", "index"), &PopupMenu::get_item_submenu);
+ ClassDB::bind_method(D_METHOD("get_item_submenu_node", "index"), &PopupMenu::get_item_submenu_node);
ClassDB::bind_method(D_METHOD("is_item_separator", "index"), &PopupMenu::is_item_separator);
ClassDB::bind_method(D_METHOD("is_item_checkable", "index"), &PopupMenu::is_item_checkable);
ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "index"), &PopupMenu::is_item_radio_checkable);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index d068418059..9f4d28e95d 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -68,7 +68,8 @@ class PopupMenu : public Popup {
bool dirty = true;
int id = 0;
Variant metadata;
- String submenu;
+ String submenu_name; // Compatibility.
+ PopupMenu *submenu = nullptr;
String tooltip;
Key accel = Key::NONE;
int _ofs_cache = 0;
@@ -254,6 +255,7 @@ public:
void add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
void add_submenu_item(const String &p_label, const String &p_submenu, int p_id = -1);
+ void add_submenu_node_item(const String &p_label, PopupMenu *p_submenu, int p_id = -1);
void set_item_text(int p_idx, const String &p_text);
@@ -268,6 +270,7 @@ public:
void set_item_metadata(int p_idx, const Variant &p_meta);
void set_item_disabled(int p_idx, bool p_disabled);
void set_item_submenu(int p_idx, const String &p_submenu);
+ void set_item_submenu_node(int p_idx, PopupMenu *p_submenu);
void set_item_as_separator(int p_idx, bool p_separator);
void set_item_as_checkable(int p_idx, bool p_checkable);
void set_item_as_radio_checkable(int p_idx, bool p_radio_checkable);
@@ -296,6 +299,7 @@ public:
Variant get_item_metadata(int p_idx) const;
bool is_item_disabled(int p_idx) const;
String get_item_submenu(int p_idx) const;
+ PopupMenu *get_item_submenu_node(int p_idx) const;
bool is_item_separator(int p_idx) const;
bool is_item_checkable(int p_idx) const;
bool is_item_radio_checkable(int p_idx) const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index f8bd65ae06..59be64b6dc 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -6873,15 +6873,12 @@ void TextEdit::_generate_context_menu() {
add_child(menu, false, INTERNAL_MODE_FRONT);
menu_dir = memnew(PopupMenu);
- menu_dir->set_name("DirMenu");
menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED);
menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO);
menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR);
menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL);
- menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT);
menu_ctl = memnew(PopupMenu);
- menu_ctl->set_name("CTLMenu");
menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM);
menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM);
menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE);
@@ -6900,7 +6897,6 @@ void TextEdit::_generate_context_menu() {
menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ);
menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
- menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT);
menu->add_item(RTR("Cut"), MENU_CUT);
menu->add_item(RTR("Copy"), MENU_COPY);
@@ -6912,10 +6908,10 @@ void TextEdit::_generate_context_menu() {
menu->add_item(RTR("Undo"), MENU_UNDO);
menu->add_item(RTR("Redo"), MENU_REDO);
menu->add_separator();
- menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu", MENU_SUBMENU_TEXT_DIR);
+ menu->add_submenu_node_item(RTR("Text Writing Direction"), menu_dir, MENU_SUBMENU_TEXT_DIR);
menu->add_separator();
menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC);
- menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu", MENU_SUBMENU_INSERT_UCC);
+ menu->add_submenu_node_item(RTR("Insert Control Character"), menu_ctl, MENU_SUBMENU_INSERT_UCC);
menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
diff --git a/scene/main/window.compat.inc b/scene/main/window.compat.inc
new file mode 100644
index 0000000000..0bba01bb1b
--- /dev/null
+++ b/scene/main/window.compat.inc
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* 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 3dbd96a53f..9c2509404c 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "window.h"
+#include "window.compat.inc"
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
@@ -2852,12 +2853,12 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_theme_color_override", "name"), &Window::remove_theme_color_override);
ClassDB::bind_method(D_METHOD("remove_theme_constant_override", "name"), &Window::remove_theme_constant_override);
- ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Window::get_theme_icon, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Window::get_theme_stylebox, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_font", "name", "theme_type"), &Window::get_theme_font, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Window::get_theme_font_size, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_color", "name", "theme_type"), &Window::get_theme_color, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Window::get_theme_constant, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Window::get_theme_icon, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Window::get_theme_stylebox, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_font", "name", "theme_type"), &Window::get_theme_font, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Window::get_theme_font_size, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_color", "name", "theme_type"), &Window::get_theme_color, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Window::get_theme_constant, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("has_theme_icon_override", "name"), &Window::has_theme_icon_override);
ClassDB::bind_method(D_METHOD("has_theme_stylebox_override", "name"), &Window::has_theme_stylebox_override);
@@ -2866,12 +2867,12 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_theme_color_override", "name"), &Window::has_theme_color_override);
ClassDB::bind_method(D_METHOD("has_theme_constant_override", "name"), &Window::has_theme_constant_override);
- ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Window::has_theme_icon, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Window::has_theme_stylebox, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_font", "name", "theme_type"), &Window::has_theme_font, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Window::has_theme_font_size, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL(""));
- ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Window::has_theme_icon, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Window::has_theme_stylebox, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_font", "name", "theme_type"), &Window::has_theme_font, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Window::has_theme_font_size, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("get_theme_default_base_scale"), &Window::get_theme_default_base_scale);
ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Window::get_theme_default_font);
diff --git a/scene/main/window.h b/scene/main/window.h
index 768bd407bb..e37a98bd2d 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -244,6 +244,10 @@ 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/servers/rendering/dummy/storage/material_storage.cpp b/servers/rendering/dummy/storage/material_storage.cpp
index 5a2c135ff5..64f6b55172 100644
--- a/servers/rendering/dummy/storage/material_storage.cpp
+++ b/servers/rendering/dummy/storage/material_storage.cpp
@@ -81,6 +81,7 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) {
new_mode = RS::SHADER_FOG;
} else {
new_mode = RS::SHADER_MAX;
+ ERR_FAIL_MSG("Shader type " + mode_string + " not supported in Dummy renderer.");
}
ShaderCompiler::IdentifierActions actions;
actions.uniforms = &shader->uniforms;
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index cd06027f9f..b67a698615 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -572,6 +572,7 @@ void TextServer::_bind_methods() {
BIND_BITFIELD_FLAG(GRAPHEME_IS_CONNECTED);
BIND_BITFIELD_FLAG(GRAPHEME_IS_SAFE_TO_INSERT_TATWEEL);
BIND_BITFIELD_FLAG(GRAPHEME_IS_EMBEDDED_OBJECT);
+ BIND_BITFIELD_FLAG(GRAPHEME_IS_SOFT_HYPHEN);
/* Hinting */
BIND_ENUM_CONSTANT(HINTING_NONE);
@@ -733,6 +734,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
ERR_FAIL_COND_V(p_width.is_empty(), lines);
+ TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped);
const Vector2i &range = shaped_text_get_range(p_shaped);
@@ -758,10 +760,10 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
int start_pos = prev_safe_break;
int end_pos = last_safe_break;
- while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
start_pos += l_gl[start_pos].count;
}
- while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= l_gl[end_pos].count;
}
if (last_end <= l_gl[start_pos].start) {
@@ -829,8 +831,17 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
}
if (p_break_flags.has_flag(BREAK_WORD_BOUND)) {
if ((l_gl[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
- last_safe_break = i;
- word_count++;
+ if ((l_gl[i].flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN) {
+ uint32_t gl = font_get_glyph_index(l_gl[i].font_rid, l_gl[i].font_size, 0x00ad, 0);
+ float w = font_get_glyph_advance(l_gl[i].font_rid, l_gl[i].font_size, gl)[(orientation == ORIENTATION_HORIZONTAL) ? 0 : 1];
+ if (width + l_gl[i].advance + w <= p_width[chunk]) {
+ last_safe_break = i;
+ word_count++;
+ }
+ } else {
+ last_safe_break = i;
+ word_count++;
+ }
}
}
if (p_break_flags.has_flag(BREAK_GRAPHEME_BOUND) && word_count == 0) {
@@ -876,6 +887,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
int word_count = 0;
bool trim_next = false;
+ TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
int l_size = shaped_text_get_glyph_count(p_shaped);
const Glyph *l_gl = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped);
@@ -889,10 +901,10 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
int start_pos = prev_safe_break;
int end_pos = last_safe_break;
- while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
start_pos += l_gl[start_pos].count;
}
- while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= l_gl[end_pos].count;
}
if (last_end <= l_gl[start_pos].start) {
@@ -949,8 +961,17 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
}
if (p_break_flags.has_flag(BREAK_WORD_BOUND)) {
if ((l_gl[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
- last_safe_break = i;
- word_count++;
+ if ((l_gl[i].flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN) {
+ uint32_t gl = font_get_glyph_index(l_gl[i].font_rid, l_gl[i].font_size, 0x00AD, 0);
+ float w = font_get_glyph_advance(l_gl[i].font_rid, l_gl[i].font_size, gl)[(orientation == ORIENTATION_HORIZONTAL) ? 0 : 1];
+ if (width + l_gl[i].advance + w <= p_width) {
+ last_safe_break = i;
+ word_count++;
+ }
+ } else {
+ last_safe_break = i;
+ word_count++;
+ }
}
if (p_break_flags.has_flag(BREAK_ADAPTIVE) && word_count == 0) {
last_safe_break = i;
diff --git a/servers/text_server.h b/servers/text_server.h
index e48b1bf890..c2cc444646 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -141,6 +141,7 @@ public:
GRAPHEME_IS_CONNECTED = 1 << 10, // Connected to previous grapheme.
GRAPHEME_IS_SAFE_TO_INSERT_TATWEEL = 1 << 11, // It is safe to insert a U+0640 before this grapheme for elongation.
GRAPHEME_IS_EMBEDDED_OBJECT = 1 << 12, // Grapheme is an object replacement character for the embedded object.
+ GRAPHEME_IS_SOFT_HYPHEN = 1 << 13, // Grapheme is a soft hyphen.
};
enum Hinting {