summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/config/project_settings.cpp4
-rw-r--r--core/core_bind.compat.inc45
-rw-r--r--core/core_bind.cpp10
-rw-r--r--core/core_bind.h7
-rw-r--r--core/extension/gdextension.cpp2
-rw-r--r--core/io/image.cpp27
-rw-r--r--core/io/image.h2
-rw-r--r--core/io/logger.cpp6
-rw-r--r--core/object/object.cpp2
-rw-r--r--core/object/script_language.cpp13
-rw-r--r--core/object/script_language.h5
-rw-r--r--core/string/ustring.cpp24
-rw-r--r--core/string/ustring.h8
-rw-r--r--core/variant/variant_call.cpp2
-rw-r--r--doc/classes/CodeEdit.xml3
-rw-r--r--doc/classes/EditorSettings.xml8
-rw-r--r--doc/classes/EditorSpinSlider.xml8
-rw-r--r--doc/classes/ProjectSettings.xml9
-rw-r--r--doc/classes/Semaphore.xml3
-rw-r--r--doc/classes/Shader.xml4
-rw-r--r--doc/classes/SpinBox.xml94
-rw-r--r--doc/classes/String.xml32
-rw-r--r--doc/classes/StringName.xml32
-rw-r--r--doc/classes/TextEdit.xml5
-rw-r--r--doc/classes/VisualShaderNodeCubemap.xml2
-rw-r--r--doc/classes/VisualShaderNodeTexture2DArray.xml2
-rw-r--r--drivers/gles3/shaders/scene.glsl2
-rw-r--r--drivers/gles3/storage/material_storage.cpp1
-rw-r--r--editor/code_editor.cpp30
-rw-r--r--editor/code_editor.h4
-rw-r--r--editor/editor_autoload_settings.cpp2
-rw-r--r--editor/editor_inspector.cpp2
-rw-r--r--editor/editor_log.cpp4
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/editor_settings.cpp2
-rw-r--r--editor/gui/editor_spin_slider.cpp6
-rw-r--r--editor/gui/editor_spin_slider.h5
-rw-r--r--editor/gui/editor_zoom_widget.cpp17
-rw-r--r--editor/icons/GuiSpinboxDown.svg1
-rw-r--r--editor/icons/GuiSpinboxUp.svg1
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp8
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp20
-rw-r--r--editor/plugins/script_editor_plugin.cpp26
-rw-r--r--editor/plugins/script_editor_plugin.h3
-rw-r--r--editor/plugins/script_text_editor.cpp27
-rw-r--r--editor/plugins/script_text_editor.h4
-rw-r--r--editor/plugins/shader_editor_plugin.cpp123
-rw-r--r--editor/plugins/shader_editor_plugin.h18
-rw-r--r--editor/plugins/text_editor.cpp4
-rw-r--r--editor/plugins/text_editor.h2
-rw-r--r--editor/plugins/text_shader_editor.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp7
-rw-r--r--editor/project_manager.cpp1
-rw-r--r--editor/project_manager/project_list.cpp2
-rw-r--r--editor/project_settings_editor.cpp2
-rw-r--r--editor/scene_tree_dock.cpp11
-rw-r--r--editor/script_create_dialog.cpp2
-rw-r--r--editor/shader_globals_editor.cpp2
-rw-r--r--editor/themes/editor_theme_manager.cpp44
-rw-r--r--main/main.cpp2
-rw-r--r--misc/dist/linux/org.godotengine.Godot.desktop4
-rw-r--r--misc/extension_api_validation/4.3-stable.expected22
-rw-r--r--modules/betsy/image_compress_betsy.cpp27
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp12
-rw-r--r--modules/gdscript/gdscript_editor.cpp6
-rw-r--r--modules/gdscript/gdscript_function.h2
-rw-r--r--modules/gdscript/gdscript_parser.cpp6
-rw-r--r--modules/gdscript/gdscript_vm.cpp85
-rw-r--r--modules/gdscript/tests/scripts/completion/index/array_type.cfg9
-rw-r--r--modules/gdscript/tests/scripts/completion/index/array_type.gd10
-rw-r--r--modules/gdscript/tests/scripts/completion/index/array_value.cfg9
-rw-r--r--modules/gdscript/tests/scripts/completion/index/array_value.gd10
-rw-r--r--modules/gdscript/tests/scripts/completion/index/const_dictionary_keys.cfg11
-rw-r--r--modules/gdscript/tests/scripts/completion/index/const_dictionary_keys.gd13
-rw-r--r--modules/gdscript/tests/scripts/completion/index/dictionary_type.cfg9
-rw-r--r--modules/gdscript/tests/scripts/completion/index/dictionary_type.gd10
-rw-r--r--modules/gdscript/tests/scripts/completion/index/dictionary_value.cfg9
-rw-r--r--modules/gdscript/tests/scripts/completion/index/dictionary_value.gd10
-rw-r--r--modules/gdscript/tests/scripts/completion/index/local_dictionary_keys.cfg11
-rw-r--r--modules/gdscript/tests/scripts/completion/index/local_dictionary_keys.gd13
-rw-r--r--modules/gdscript/tests/scripts/completion/index/property_dictionary_keys.cfg9
-rw-r--r--modules/gdscript/tests/scripts/completion/index/property_dictionary_keys.gd13
-rw-r--r--modules/gdscript/tests/scripts/completion/index/untyped_local.cfg5
-rw-r--r--modules/gdscript/tests/scripts/completion/index/untyped_local.gd10
-rw-r--r--modules/gdscript/tests/scripts/completion/index/untyped_property.cfg5
-rw-r--r--modules/gdscript/tests/scripts/completion/index/untyped_property.gd9
-rw-r--r--modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp18
-rw-r--r--modules/mbedtls/tls_context_mbedtls.cpp2
-rw-r--r--modules/openxr/doc_classes/OpenXRAPIExtension.xml29
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_extension.cpp2
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_extension.h2
-rw-r--r--modules/openxr/extensions/openxr_debug_utils_extension.cpp287
-rw-r--r--modules/openxr/extensions/openxr_debug_utils_extension.h76
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper.h2
-rw-r--r--modules/openxr/openxr_api.cpp70
-rw-r--r--modules/openxr/openxr_api.h5
-rw-r--r--modules/openxr/openxr_api_extension.cpp28
-rw-r--r--modules/openxr/openxr_api_extension.h4
-rw-r--r--modules/openxr/register_types.cpp4
-rw-r--r--platform/windows/display_server_windows.cpp19
-rw-r--r--platform/windows/os_windows.cpp10
-rw-r--r--scene/gui/code_edit.cpp9
-rw-r--r--scene/gui/code_edit.h1
-rw-r--r--scene/gui/spin_box.cpp224
-rw-r--r--scene/gui/spin_box.h67
-rw-r--r--scene/gui/text_edit.cpp55
-rw-r--r--scene/gui/text_edit.h30
-rw-r--r--scene/gui/tree.cpp63
-rw-r--r--scene/gui/tree.h1
-rw-r--r--scene/resources/shader.compat.inc46
-rw-r--r--scene/resources/shader.cpp9
-rw-r--r--scene/resources/shader.h12
-rw-r--r--scene/resources/visual_shader.cpp9
-rw-r--r--scene/resources/visual_shader.h2
-rw-r--r--scene/resources/visual_shader_nodes.compat.inc63
-rw-r--r--scene/resources/visual_shader_nodes.cpp13
-rw-r--r--scene/resources/visual_shader_nodes.h25
-rw-r--r--scene/theme/default_theme.cpp37
-rw-r--r--scene/theme/icons/value_down.svg1
-rw-r--r--scene/theme/icons/value_up.svg1
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp1
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp1
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl2
-rw-r--r--servers/rendering/rendering_device_binds.cpp2
-rw-r--r--servers/rendering/shader_preprocessor.cpp2
-rw-r--r--servers/rendering/shader_types.cpp3
-rw-r--r--servers/text_server.cpp18
-rw-r--r--tests/core/object/test_class_db.h4
-rw-r--r--tests/core/string/test_string.h28
-rw-r--r--tests/scene/test_node_2d.h125
-rw-r--r--tests/scene/test_style_box_texture.h194
-rw-r--r--tests/scene/test_tree.h151
-rw-r--r--tests/test_main.cpp2
134 files changed, 2434 insertions, 414 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 5b04986020..32f36e01f9 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1472,10 +1472,6 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution
GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true);
-#ifdef TOOLS_ENABLED
- GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor_hint", false);
-#endif
-
GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true);
GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true);
diff --git a/core/core_bind.compat.inc b/core/core_bind.compat.inc
new file mode 100644
index 0000000000..83b7b33e38
--- /dev/null
+++ b/core/core_bind.compat.inc
@@ -0,0 +1,45 @@
+/**************************************************************************/
+/* core_bind.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
+
+namespace core_bind {
+
+void Semaphore::_post_bind_compat_93605() {
+ post(1);
+}
+
+void Semaphore::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("post"), &Semaphore::_post_bind_compat_93605);
+}
+
+}; // namespace core_bind
+
+#endif
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 84f66c3479..5137930116 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "core_bind.h"
+#include "core_bind.compat.inc"
#include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h"
@@ -1210,14 +1211,15 @@ bool Semaphore::try_wait() {
return semaphore.try_wait();
}
-void Semaphore::post() {
- semaphore.post();
+void Semaphore::post(int p_count) {
+ ERR_FAIL_COND(p_count <= 0);
+ semaphore.post(p_count);
}
void Semaphore::_bind_methods() {
ClassDB::bind_method(D_METHOD("wait"), &Semaphore::wait);
ClassDB::bind_method(D_METHOD("try_wait"), &Semaphore::try_wait);
- ClassDB::bind_method(D_METHOD("post"), &Semaphore::post);
+ ClassDB::bind_method(D_METHOD("post", "count"), &Semaphore::post, DEFVAL(1));
}
////// Mutex //////
@@ -1768,7 +1770,7 @@ Object *Engine::get_singleton_object(const StringName &p_name) const {
void Engine::register_singleton(const StringName &p_name, Object *p_object) {
ERR_FAIL_COND_MSG(has_singleton(p_name), "Singleton already registered: " + String(p_name));
- ERR_FAIL_COND_MSG(!String(p_name).is_valid_identifier(), "Singleton name is not a valid identifier: " + p_name);
+ ERR_FAIL_COND_MSG(!String(p_name).is_valid_ascii_identifier(), "Singleton name is not a valid identifier: " + p_name);
::Engine::Singleton s;
s.class_name = p_name;
s.name = p_name;
diff --git a/core/core_bind.h b/core/core_bind.h
index 0949ba628f..e953483391 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -390,12 +390,17 @@ class Semaphore : public RefCounted {
GDCLASS(Semaphore, RefCounted);
::Semaphore semaphore;
+protected:
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ void _post_bind_compat_93605();
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
public:
void wait();
bool try_wait();
- void post();
+ void post(int p_count = 1);
};
class Thread : public RefCounted {
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 940a34396f..c9e609cddc 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -355,7 +355,7 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
StringName parent_class_name = *reinterpret_cast<const StringName *>(p_parent_class_name);
- ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
+ ERR_FAIL_COND_MSG(!String(class_name).is_valid_ascii_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier.");
ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered.");
Extension *parent_extension = nullptr;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 003646d095..f6065d984b 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -3824,6 +3824,33 @@ void Image::bump_map_to_normal_map(float bump_scale) {
data = result_image;
}
+bool Image::detect_signed(bool p_include_mips) const {
+ ERR_FAIL_COND_V(is_compressed(), false);
+
+ if (format >= Image::FORMAT_RH && format <= Image::FORMAT_RGBAH) {
+ const uint16_t *img_data = reinterpret_cast<const uint16_t *>(data.ptr());
+ const uint64_t img_size = p_include_mips ? (data.size() / 2) : (width * height * get_format_pixel_size(format) / 2);
+
+ for (uint64_t i = 0; i < img_size; i++) {
+ if ((img_data[i] & 0x8000) != 0 && (img_data[i] & 0x7fff) != 0) {
+ return true;
+ }
+ }
+
+ } else if (format >= Image::FORMAT_RF && format <= Image::FORMAT_RGBAF) {
+ const uint32_t *img_data = reinterpret_cast<const uint32_t *>(data.ptr());
+ const uint64_t img_size = p_include_mips ? (data.size() / 4) : (width * height * get_format_pixel_size(format) / 4);
+
+ for (uint64_t i = 0; i < img_size; i++) {
+ if ((img_data[i] & 0x80000000) != 0 && (img_data[i] & 0x7fffffff) != 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
void Image::srgb_to_linear() {
if (data.size() == 0) {
return;
diff --git a/core/io/image.h b/core/io/image.h
index 8d09a0b40b..4461ae71a6 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -391,6 +391,8 @@ public:
Ref<Image> get_image_from_mipmap(int p_mipmap) const;
void bump_map_to_normal_map(float bump_scale = 1.0);
+ bool detect_signed(bool p_include_mips = true) const;
+
void blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest);
void blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest);
void blend_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest);
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index a24277fe72..26b60f6738 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -84,11 +84,7 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
err_details = p_code;
}
- if (p_editor_notify) {
- logf_error("%s: %s\n", err_type, err_details);
- } else {
- logf_error("USER %s: %s\n", err_type, err_details);
- }
+ logf_error("%s: %s\n", err_type, err_details);
logf_error(" at: %s (%s:%i)\n", p_function, p_file, p_line);
}
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 161b1e3dbe..a2330ecd04 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -997,7 +997,7 @@ void Object::set_meta(const StringName &p_name, const Variant &p_value) {
if (E) {
E->value = p_value;
} else {
- ERR_FAIL_COND_MSG(!p_name.operator String().is_valid_identifier(), "Invalid metadata identifier: '" + p_name + "'.");
+ ERR_FAIL_COND_MSG(!p_name.operator String().is_valid_ascii_identifier(), "Invalid metadata identifier: '" + p_name + "'.");
Variant *V = &metadata.insert(p_name, p_value)->value;
const String &sname = p_name;
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index cdc56e5ec5..57e5195137 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -704,6 +704,19 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const {
return false;
}
+Variant PlaceHolderScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+#if TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return String("Attempt to call a method on a placeholder instance. Check if the script is in tool mode.");
+ } else {
+ return String("Attempt to call a method on a placeholder instance. Probably a bug, please report.");
+ }
+#else
+ return Variant();
+#endif // TOOLS_ENABLED
+}
+
void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values) {
HashSet<StringName> new_values;
for (const PropertyInfo &E : p_properties) {
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 59a43a7b29..e38c344ae5 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -454,10 +454,7 @@ public:
return 0;
}
- virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
virtual void notification(int p_notification, bool p_reversed = false) override {}
virtual Ref<Script> get_script() const override { return script; }
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 303998ab50..2683addd4b 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -4624,7 +4624,7 @@ bool String::is_absolute_path() const {
}
}
-String String::validate_identifier() const {
+String String::validate_ascii_identifier() const {
if (is_empty()) {
return "_"; // Empty string is not a valid identifier;
}
@@ -4647,7 +4647,7 @@ String String::validate_identifier() const {
return result;
}
-bool String::is_valid_identifier() const {
+bool String::is_valid_ascii_identifier() const {
int len = length();
if (len == 0) {
@@ -4669,6 +4669,26 @@ bool String::is_valid_identifier() const {
return true;
}
+bool String::is_valid_unicode_identifier() const {
+ const char32_t *str = ptr();
+ int len = length();
+
+ if (len == 0) {
+ return false; // Empty string.
+ }
+
+ if (!is_unicode_identifier_start(str[0])) {
+ return false;
+ }
+
+ for (int i = 1; i < len; i++) {
+ if (!is_unicode_identifier_continue(str[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool String::is_valid_string() const {
int l = length();
const char32_t *src = get_data();
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 9df2d56e80..11f15031f9 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -459,10 +459,11 @@ public:
// node functions
static String get_invalid_node_name_characters(bool p_allow_internal = false);
String validate_node_name() const;
- String validate_identifier() const;
+ String validate_ascii_identifier() const;
String validate_filename() const;
- bool is_valid_identifier() const;
+ bool is_valid_ascii_identifier() const;
+ bool is_valid_unicode_identifier() const;
bool is_valid_int() const;
bool is_valid_float() const;
bool is_valid_hex_number(bool p_with_prefix) const;
@@ -470,6 +471,9 @@ public:
bool is_valid_ip_address() const;
bool is_valid_filename() const;
+ // Use `is_valid_ascii_identifier()` instead. Kept for compatibility.
+ bool is_valid_identifier() const { return is_valid_ascii_identifier(); }
+
/**
* The constructors must not depend on other overloads
*/
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 695ad23bf5..83f1f981b3 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1724,6 +1724,8 @@ static void _register_variant_builtin_methods_string() {
bind_string_method(validate_node_name, sarray(), varray());
bind_string_method(validate_filename, sarray(), varray());
+ bind_string_method(is_valid_ascii_identifier, sarray(), varray());
+ bind_string_method(is_valid_unicode_identifier, sarray(), varray());
bind_string_method(is_valid_identifier, sarray(), varray());
bind_string_method(is_valid_int, sarray(), varray());
bind_string_method(is_valid_float, sarray(), varray());
diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml
index d455799c29..136b8e5fc5 100644
--- a/doc/classes/CodeEdit.xml
+++ b/doc/classes/CodeEdit.xml
@@ -721,6 +721,9 @@
<theme_item name="can_fold_code_region" data_type="icon" type="Texture2D">
Sets a custom [Texture2D] to draw in the line folding gutter when a code region can be folded.
</theme_item>
+ <theme_item name="completion_color_bg" data_type="icon" type="Texture2D">
+ Background panel for the color preview box in autocompletion (visible when the color is translucent).
+ </theme_item>
<theme_item name="executing_line" data_type="icon" type="Texture2D">
Icon to draw in the executing gutter for executing lines.
</theme_item>
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 4bb7149f2f..ef87fb5edd 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -258,11 +258,14 @@
The color to use when drawing smart snapping lines in the 2D editor. The smart snapping lines will automatically display when moving 2D nodes if smart snapping is enabled in the Snapping Options menu at the top of the 2D editor viewport.
</member>
<member name="editors/2d/use_integer_zoom_by_default" type="bool" setter="" getter="">
- If [code]true[/code], the 2D editor will snap to integer zoom values while not holding the [kbd]Alt[/kbd] key and powers of two while holding it. If [code]false[/code], this behavior is swapped.
+ If [code]true[/code], the 2D editor will snap to integer zoom values when not holding the [kbd]Alt[/kbd] key. If [code]false[/code], this behavior is swapped.
</member>
<member name="editors/2d/viewport_border_color" type="Color" setter="" getter="">
The color of the viewport border in the 2D editor. This border represents the viewport's size at the base resolution defined in the Project Settings. Objects placed outside this border will not be visible unless a [Camera2D] node is used, or unless the window is resized and the stretch mode is set to [code]disabled[/code].
</member>
+ <member name="editors/2d/zoom_speed_factor" type="float" setter="" getter="">
+ The factor to use when zooming in or out in the 2D editor. For example, [code]1.1[/code] will zoom in by 10% with every step. If set to [code]2.0[/code], zooming will only cycle through powers of two.
+ </member>
<member name="editors/3d/default_fov" type="float" setter="" getter="">
The default camera vertical field of view to use in the 3D editor (in degrees). The camera field of view can be adjusted on a per-scene basis using the [b]View[/b] menu at the top of the 3D editor. If a scene had its camera field of view adjusted using the [b]View[/b] menu, this setting is ignored in the scene in question. This setting is also ignored while a [Camera3D] node is being previewed in the editor.
[b]Note:[/b] The editor camera always uses the [b]Keep Height[/b] aspect mode.
@@ -677,6 +680,9 @@
<member name="interface/editor/import_resources_when_unfocused" type="bool" setter="" getter="">
If [code]true[/code], (re)imports resources even if the editor window is unfocused or minimized. If [code]false[/code], resources are only (re)imported when the editor window is focused. This can be set to [code]true[/code] to speed up iteration by starting the import process earlier when saving files in the project folder. This also allows getting visual feedback on changes without having to click the editor window, which is useful with multi-monitor setups. The downside of setting this to [code]true[/code] is that it increases idle CPU usage and may steal CPU time from other applications when importing resources.
</member>
+ <member name="interface/editor/keep_screen_on" type="bool" setter="" getter="">
+ If [code]true[/code], keeps the screen on (even in case of inactivity), so the screensaver does not take over. Works on desktop and mobile platforms.
+ </member>
<member name="interface/editor/localize_settings" type="bool" setter="" getter="">
If [code]true[/code], setting names in the editor are localized when possible.
[b]Note:[/b] This setting affects most [EditorInspector]s in the editor UI, primarily Project Settings and Editor Settings. To control names displayed in the Inspector dock, use [member interface/inspector/default_property_name_style] instead.
diff --git a/doc/classes/EditorSpinSlider.xml b/doc/classes/EditorSpinSlider.xml
index 783f1243e2..83c65b736e 100644
--- a/doc/classes/EditorSpinSlider.xml
+++ b/doc/classes/EditorSpinSlider.xml
@@ -51,4 +51,12 @@
</description>
</signal>
</signals>
+ <theme_items>
+ <theme_item name="updown" data_type="icon" type="Texture2D">
+ Single texture representing both the up and down buttons.
+ </theme_item>
+ <theme_item name="updown_disabled" data_type="icon" type="Texture2D">
+ Single texture representing both the up and down buttons, when the control is readonly or disabled.
+ </theme_item>
+ </theme_items>
</class>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index bf1632965b..30cbbbf799 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -813,9 +813,6 @@
<member name="display/window/energy_saving/keep_screen_on" type="bool" setter="" getter="" default="true">
If [code]true[/code], keeps the screen on (even in case of inactivity), so the screensaver does not take over. Works on desktop and mobile platforms.
</member>
- <member name="display/window/energy_saving/keep_screen_on.editor_hint" type="bool" setter="" getter="" default="false">
- Editor-only override for [member display/window/energy_saving/keep_screen_on]. Does not affect running project.
- </member>
<member name="display/window/handheld/orientation" type="int" setter="" getter="" default="0">
The default screen orientation to use on mobile devices. See [enum DisplayServer.ScreenOrientation] for possible values.
[b]Note:[/b] When set to a portrait orientation, this project setting does not flip the project resolution's width and height automatically. Instead, you have to set [member display/window/size/viewport_width] and [member display/window/size/viewport_height] accordingly.
@@ -2948,6 +2945,12 @@
<member name="xr/openxr/environment_blend_mode" type="int" setter="" getter="" default="&quot;0&quot;">
Specify how OpenXR should blend in the environment. This is specific to certain AR and passthrough devices where camera images are blended in by the XR compositor.
</member>
+ <member name="xr/openxr/extensions/debug_message_types" type="int" setter="" getter="" default="&quot;15&quot;">
+ Specifies the message types for which we request debug messages. Requires [member xr/openxr/extensions/debug_utils] to be set and the extension to be supported by the XR runtime.
+ </member>
+ <member name="xr/openxr/extensions/debug_utils" type="int" setter="" getter="" default="&quot;0&quot;">
+ Enables debug utilities on XR runtimes that supports the debug utils extension. Sets the maximum severity being reported (0 = disabled, 1 = error, 2 = warning, 3 = info, 4 = verbose).
+ </member>
<member name="xr/openxr/extensions/eye_gaze_interaction" type="bool" setter="" getter="" default="false">
Specify whether to enable eye tracking for this project. Depending on the platform, additional export configuration may be needed.
</member>
diff --git a/doc/classes/Semaphore.xml b/doc/classes/Semaphore.xml
index d0db24dfb7..3ecb5c23af 100644
--- a/doc/classes/Semaphore.xml
+++ b/doc/classes/Semaphore.xml
@@ -17,8 +17,9 @@
<methods>
<method name="post">
<return type="void" />
+ <param index="0" name="count" type="int" default="1" />
<description>
- Lowers the [Semaphore], allowing one more thread in.
+ Lowers the [Semaphore], allowing one thread in, or more if [param count] is specified.
</description>
</method>
<method name="try_wait">
diff --git a/doc/classes/Shader.xml b/doc/classes/Shader.xml
index b71f9ca1b0..68176dea14 100644
--- a/doc/classes/Shader.xml
+++ b/doc/classes/Shader.xml
@@ -12,7 +12,7 @@
</tutorials>
<methods>
<method name="get_default_texture_parameter" qualifiers="const">
- <return type="Texture2D" />
+ <return type="Texture" />
<param index="0" name="name" type="StringName" />
<param index="1" name="index" type="int" default="0" />
<description>
@@ -38,7 +38,7 @@
<method name="set_default_texture_parameter">
<return type="void" />
<param index="0" name="name" type="StringName" />
- <param index="1" name="texture" type="Texture2D" />
+ <param index="1" name="texture" type="Texture" />
<param index="2" name="index" type="int" default="0" />
<description>
Sets the default texture to be used with a texture uniform. The default is used if a texture is not set in the [ShaderMaterial].
diff --git a/doc/classes/SpinBox.xml b/doc/classes/SpinBox.xml
index 2fe2a0eaa1..3fb30a81b8 100644
--- a/doc/classes/SpinBox.xml
+++ b/doc/classes/SpinBox.xml
@@ -24,7 +24,7 @@
[/codeblocks]
See [Range] class for more options over the [SpinBox].
[b]Note:[/b] With the [SpinBox]'s context menu disabled, you can right-click the bottom half of the spinbox to set the value to its minimum, while right-clicking the top half sets the value to its maximum.
- [b]Note:[/b] [SpinBox] relies on an underlying [LineEdit] node. To theme a [SpinBox]'s background, add theme items for [LineEdit] and customize them.
+ [b]Note:[/b] [SpinBox] relies on an underlying [LineEdit] node. To theme a [SpinBox]'s background, add theme items for [LineEdit] and customize them. The [LineEdit] has the [code]SpinBoxInnerLineEdit[/code] theme variation, so that you can give it a distinct appearance from regular [LineEdit]s.
[b]Note:[/b] If you want to implement drag and drop for the underlying [LineEdit], you can use [method Control.set_drag_forwarding] on the node returned by [method get_line_edit].
</description>
<tutorials>
@@ -70,8 +70,98 @@
</member>
</members>
<theme_items>
+ <theme_item name="down_disabled_icon_modulate" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
+ Down button icon modulation color, when the button is disabled.
+ </theme_item>
+ <theme_item name="down_hover_icon_modulate" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
+ Down button icon modulation color, when the button is hovered.
+ </theme_item>
+ <theme_item name="down_icon_modulate" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
+ Down button icon modulation color.
+ </theme_item>
+ <theme_item name="down_pressed_icon_modulate" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
+ Down button icon modulation color, when the button is being pressed.
+ </theme_item>
+ <theme_item name="up_disabled_icon_modulate" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 0.5)">
+ Up button icon modulation color, when the button is disabled.
+ </theme_item>
+ <theme_item name="up_hover_icon_modulate" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
+ Up button icon modulation color, when the button is hovered.
+ </theme_item>
+ <theme_item name="up_icon_modulate" data_type="color" type="Color" default="Color(0.875, 0.875, 0.875, 1)">
+ Up button icon modulation color.
+ </theme_item>
+ <theme_item name="up_pressed_icon_modulate" data_type="color" type="Color" default="Color(0.95, 0.95, 0.95, 1)">
+ Up button icon modulation color, when the button is being pressed.
+ </theme_item>
+ <theme_item name="buttons_vertical_separation" data_type="constant" type="int" default="0">
+ Vertical separation between the up and down buttons.
+ </theme_item>
+ <theme_item name="buttons_width" data_type="constant" type="int" default="16">
+ Width of the up and down buttons. If smaller than any icon set on the buttons, the respective icon may overlap neighboring elements, unless [theme_item set_min_buttons_width_from_icons] is different than [code]0[/code].
+ </theme_item>
+ <theme_item name="field_and_buttons_separation" data_type="constant" type="int" default="2">
+ Width of the horizontal separation between the text input field ([LineEdit]) and the buttons.
+ </theme_item>
+ <theme_item name="set_min_buttons_width_from_icons" data_type="constant" type="int" default="1">
+ If not [code]0[/code], the minimum button width corresponds to the widest of all icons set on those buttons, even if [theme_item buttons_width] is smaller.
+ </theme_item>
+ <theme_item name="down" data_type="icon" type="Texture2D">
+ Down button icon, displayed in the middle of the down (value-decreasing) button.
+ </theme_item>
+ <theme_item name="down_disabled" data_type="icon" type="Texture2D">
+ Down button icon when the button is disabled.
+ </theme_item>
+ <theme_item name="down_hover" data_type="icon" type="Texture2D">
+ Down button icon when the button is hovered.
+ </theme_item>
+ <theme_item name="down_pressed" data_type="icon" type="Texture2D">
+ Down button icon when the button is being pressed.
+ </theme_item>
+ <theme_item name="up" data_type="icon" type="Texture2D">
+ Up button icon, displayed in the middle of the up (value-increasing) button.
+ </theme_item>
+ <theme_item name="up_disabled" data_type="icon" type="Texture2D">
+ Up button icon when the button is disabled.
+ </theme_item>
+ <theme_item name="up_hover" data_type="icon" type="Texture2D">
+ Up button icon when the button is hovered.
+ </theme_item>
+ <theme_item name="up_pressed" data_type="icon" type="Texture2D">
+ Up button icon when the button is being pressed.
+ </theme_item>
<theme_item name="updown" data_type="icon" type="Texture2D">
- Sets a custom [Texture2D] for up and down arrows of the [SpinBox].
+ Single texture representing both the up and down buttons icons. It is displayed in the middle of the buttons and does not change upon interaction. It is recommended to use individual [theme_item up] and [theme_item down] graphics for better usability. This can also be used as additional decoration between the two buttons.
+ </theme_item>
+ <theme_item name="down_background" data_type="style" type="StyleBox">
+ Background style of the down button.
+ </theme_item>
+ <theme_item name="down_background_disabled" data_type="style" type="StyleBox">
+ Background style of the down button when disabled.
+ </theme_item>
+ <theme_item name="down_background_hovered" data_type="style" type="StyleBox">
+ Background style of the down button when hovered.
+ </theme_item>
+ <theme_item name="down_background_pressed" data_type="style" type="StyleBox">
+ Background style of the down button when being pressed.
+ </theme_item>
+ <theme_item name="field_and_buttons_separator" data_type="style" type="StyleBox">
+ [StyleBox] drawn in the space occupied by the separation between the input field and the buttons.
+ </theme_item>
+ <theme_item name="up_background" data_type="style" type="StyleBox">
+ Background style of the up button.
+ </theme_item>
+ <theme_item name="up_background_disabled" data_type="style" type="StyleBox">
+ Background style of the up button when disabled.
+ </theme_item>
+ <theme_item name="up_background_hovered" data_type="style" type="StyleBox">
+ Background style of the up button when hovered.
+ </theme_item>
+ <theme_item name="up_background_pressed" data_type="style" type="StyleBox">
+ Background style of the up button when being pressed.
+ </theme_item>
+ <theme_item name="up_down_buttons_separator" data_type="style" type="StyleBox">
+ [StyleBox] drawn in the space occupied by the separation between the up and down buttons.
</theme_item>
</theme_items>
</class>
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index de3d3e7cb9..d99eaa64a6 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -451,6 +451,19 @@
Returns [code]true[/code] if all characters of this string can be found in [param text] in their original order, [b]ignoring case[/b].
</description>
</method>
+ <method name="is_valid_ascii_identifier" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if this string is a valid ASCII identifier. A valid ASCII identifier may contain only letters, digits, and underscores ([code]_[/code]), and the first character may not be a digit.
+ [codeblock]
+ print("node_2d".is_valid_ascii_identifier()) # Prints true
+ print("TYPE_FLOAT".is_valid_ascii_identifier()) # Prints true
+ print("1st_method".is_valid_ascii_identifier()) # Prints false
+ print("MyMethod#2".is_valid_ascii_identifier()) # Prints false
+ [/codeblock]
+ See also [method is_valid_unicode_identifier].
+ </description>
+ </method>
<method name="is_valid_filename" qualifiers="const">
<return type="bool" />
<description>
@@ -490,7 +503,7 @@
Returns [code]true[/code] if this string is a valid color in hexadecimal HTML notation. The string must be a hexadecimal value (see [method is_valid_hex_number]) of either 3, 4, 6 or 8 digits, and may be prefixed by a hash sign ([code]#[/code]). Other HTML notations for colors, such as names or [code]hsl()[/code], are not considered valid. See also [method Color.html].
</description>
</method>
- <method name="is_valid_identifier" qualifiers="const">
+ <method name="is_valid_identifier" qualifiers="const" deprecated="Use [method is_valid_ascii_identifier] instead.">
<return type="bool" />
<description>
Returns [code]true[/code] if this string is a valid identifier. A valid identifier may contain only letters, digits and underscores ([code]_[/code]), and the first character may not be a digit.
@@ -521,6 +534,23 @@
Returns [code]true[/code] if this string represents a well-formatted IPv4 or IPv6 address. This method considers [url=https://en.wikipedia.org/wiki/Reserved_IP_addresses]reserved IP addresses[/url] such as [code]"0.0.0.0"[/code] and [code]"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"[/code] as valid.
</description>
</method>
+ <method name="is_valid_unicode_identifier" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if this string is a valid Unicode identifier.
+ A valid Unicode identifier must begin with a Unicode character of class [code]XID_Start[/code] or [code]"_"[/code], and may contain Unicode characters of class [code]XID_Continue[/code] in the other positions.
+ [codeblock]
+ print("node_2d".is_valid_unicode_identifier()) # Prints true
+ print("1st_method".is_valid_unicode_identifier()) # Prints false
+ print("MyMethod#2".is_valid_unicode_identifier()) # Prints false
+ print("állóképesség".is_valid_unicode_identifier()) # Prints true
+ print("выносливость".is_valid_unicode_identifier()) # Prints true
+ print("体力".is_valid_unicode_identifier()) # Prints true
+ [/codeblock]
+ See also [method is_valid_ascii_identifier].
+ [b]Note:[/b] This method checks identifiers the same way as GDScript. See [method TextServer.is_valid_identifier] for more advanced checks.
+ </description>
+ </method>
<method name="join" qualifiers="const">
<return type="String" />
<param index="0" name="parts" type="PackedStringArray" />
diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml
index 836ca6b4ba..1a891de05f 100644
--- a/doc/classes/StringName.xml
+++ b/doc/classes/StringName.xml
@@ -420,6 +420,19 @@
Returns [code]true[/code] if all characters of this string can be found in [param text] in their original order, [b]ignoring case[/b].
</description>
</method>
+ <method name="is_valid_ascii_identifier" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if this string is a valid ASCII identifier. A valid ASCII identifier may contain only letters, digits, and underscores ([code]_[/code]), and the first character may not be a digit.
+ [codeblock]
+ print("node_2d".is_valid_ascii_identifier()) # Prints true
+ print("TYPE_FLOAT".is_valid_ascii_identifier()) # Prints true
+ print("1st_method".is_valid_ascii_identifier()) # Prints false
+ print("MyMethod#2".is_valid_ascii_identifier()) # Prints false
+ [/codeblock]
+ See also [method is_valid_unicode_identifier].
+ </description>
+ </method>
<method name="is_valid_filename" qualifiers="const">
<return type="bool" />
<description>
@@ -459,7 +472,7 @@
Returns [code]true[/code] if this string is a valid color in hexadecimal HTML notation. The string must be a hexadecimal value (see [method is_valid_hex_number]) of either 3, 4, 6 or 8 digits, and may be prefixed by a hash sign ([code]#[/code]). Other HTML notations for colors, such as names or [code]hsl()[/code], are not considered valid. See also [method Color.html].
</description>
</method>
- <method name="is_valid_identifier" qualifiers="const">
+ <method name="is_valid_identifier" qualifiers="const" deprecated="Use [method is_valid_ascii_identifier] instead.">
<return type="bool" />
<description>
Returns [code]true[/code] if this string is a valid identifier. A valid identifier may contain only letters, digits and underscores ([code]_[/code]), and the first character may not be a digit.
@@ -490,6 +503,23 @@
Returns [code]true[/code] if this string represents a well-formatted IPv4 or IPv6 address. This method considers [url=https://en.wikipedia.org/wiki/Reserved_IP_addresses]reserved IP addresses[/url] such as [code]"0.0.0.0"[/code] and [code]"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"[/code] as valid.
</description>
</method>
+ <method name="is_valid_unicode_identifier" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if this string is a valid Unicode identifier.
+ A valid Unicode identifier must begin with a Unicode character of class [code]XID_Start[/code] or [code]"_"[/code], and may contain Unicode characters of class [code]XID_Continue[/code] in the other positions.
+ [codeblock]
+ print("node_2d".is_valid_unicode_identifier()) # Prints true
+ print("1st_method".is_valid_unicode_identifier()) # Prints false
+ print("MyMethod#2".is_valid_unicode_identifier()) # Prints false
+ print("állóképesség".is_valid_unicode_identifier()) # Prints true
+ print("выносливость".is_valid_unicode_identifier()) # Prints true
+ print("体力".is_valid_unicode_identifier()) # Prints true
+ [/codeblock]
+ See also [method is_valid_ascii_identifier].
+ [b]Note:[/b] This method checks identifiers the same way as GDScript. See [method TextServer.is_valid_identifier] for more advanced checks.
+ </description>
+ </method>
<method name="join" qualifiers="const">
<return type="String" />
<param index="0" name="parts" type="PackedStringArray" />
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index f71601f9ae..6505e48fb9 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -1327,7 +1327,10 @@
Text shown when the [TextEdit] is empty. It is [b]not[/b] the [TextEdit]'s default value (see [member text]).
</member>
<member name="scroll_fit_content_height" type="bool" setter="set_fit_content_height_enabled" getter="is_fit_content_height_enabled" default="false">
- If [code]true[/code], [TextEdit] will disable vertical scroll and fit minimum height to the number of visible lines.
+ If [code]true[/code], [TextEdit] will disable vertical scroll and fit minimum height to the number of visible lines. When both this property and [member scroll_fit_content_width] are [code]true[/code], no scrollbars will be displayed.
+ </member>
+ <member name="scroll_fit_content_width" type="bool" setter="set_fit_content_width_enabled" getter="is_fit_content_width_enabled" default="false">
+ If [code]true[/code], [TextEdit] will disable horizontal scroll and fit minimum width to the widest line in the text. When both this property and [member scroll_fit_content_height] are [code]true[/code], no scrollbars will be displayed.
</member>
<member name="scroll_horizontal" type="int" setter="set_h_scroll" getter="get_h_scroll" default="0">
If there is a horizontal scrollbar, this determines the current horizontal scroll value in pixels.
diff --git a/doc/classes/VisualShaderNodeCubemap.xml b/doc/classes/VisualShaderNodeCubemap.xml
index 4e6cf2120a..6f3df9865a 100644
--- a/doc/classes/VisualShaderNodeCubemap.xml
+++ b/doc/classes/VisualShaderNodeCubemap.xml
@@ -9,7 +9,7 @@
<tutorials>
</tutorials>
<members>
- <member name="cube_map" type="Cubemap" setter="set_cube_map" getter="get_cube_map">
+ <member name="cube_map" type="TextureLayered" setter="set_cube_map" getter="get_cube_map">
The [Cubemap] texture to sample when using [constant SOURCE_TEXTURE] as [member source].
</member>
<member name="source" type="int" setter="set_source" getter="get_source" enum="VisualShaderNodeCubemap.Source" default="0">
diff --git a/doc/classes/VisualShaderNodeTexture2DArray.xml b/doc/classes/VisualShaderNodeTexture2DArray.xml
index 8852cb7cb4..bdf5a42821 100644
--- a/doc/classes/VisualShaderNodeTexture2DArray.xml
+++ b/doc/classes/VisualShaderNodeTexture2DArray.xml
@@ -9,7 +9,7 @@
<tutorials>
</tutorials>
<members>
- <member name="texture_array" type="Texture2DArray" setter="set_texture_array" getter="get_texture_array">
+ <member name="texture_array" type="TextureLayered" setter="set_texture_array" getter="get_texture_array">
A source texture array. Used if [member VisualShaderNodeSample3D.source] is set to [constant VisualShaderNodeSample3D.SOURCE_TEXTURE].
</member>
</members>
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index ce2db7fa85..6143ce2167 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -44,6 +44,7 @@ LIGHTMAP_BICUBIC_FILTER = false
#define M_PI 3.14159265359
#define SHADER_IS_SRGB true
+#define SHADER_SPACE_FAR -1.0
#include "stdlib_inc.glsl"
@@ -583,6 +584,7 @@ void main() {
/* clang-format on */
#define SHADER_IS_SRGB true
+#define SHADER_SPACE_FAR -1.0
#define FLAGS_NON_UNIFORM_SCALE (1 << 4)
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 25af7ff691..a37eba3b15 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1273,6 +1273,7 @@ MaterialStorage::MaterialStorage() {
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
+ actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz";
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 8664c167b5..180c25c34f 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -1299,23 +1299,29 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) {
text_editor->end_complex_operation();
}
-void CodeTextEditor::goto_line(int p_line) {
+void CodeTextEditor::goto_line(int p_line, int p_column) {
text_editor->remove_secondary_carets();
text_editor->deselect();
- text_editor->unfold_line(p_line);
- callable_mp((TextEdit *)text_editor, &TextEdit::set_caret_line).call_deferred(p_line, true, true, 0, 0);
+ text_editor->unfold_line(CLAMP(p_line, 0, text_editor->get_line_count() - 1));
+ text_editor->set_caret_line(p_line, false);
+ text_editor->set_caret_column(p_column, false);
+ // Defer in case the CodeEdit was just created and needs to be resized.
+ callable_mp((TextEdit *)text_editor, &TextEdit::adjust_viewport_to_caret).call_deferred(0);
}
void CodeTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
text_editor->remove_secondary_carets();
- text_editor->unfold_line(p_line);
- callable_mp((TextEdit *)text_editor, &TextEdit::set_caret_line).call_deferred(p_line, true, true, 0, 0);
- callable_mp((TextEdit *)text_editor, &TextEdit::set_caret_column).call_deferred(p_begin, true, 0);
+ text_editor->unfold_line(CLAMP(p_line, 0, text_editor->get_line_count() - 1));
text_editor->select(p_line, p_begin, p_line, p_end);
+ callable_mp((TextEdit *)text_editor, &TextEdit::adjust_viewport_to_caret).call_deferred(0);
}
-void CodeTextEditor::goto_line_centered(int p_line) {
- goto_line(p_line);
+void CodeTextEditor::goto_line_centered(int p_line, int p_column) {
+ text_editor->remove_secondary_carets();
+ text_editor->deselect();
+ text_editor->unfold_line(CLAMP(p_line, 0, text_editor->get_line_count() - 1));
+ text_editor->set_caret_line(p_line, false);
+ text_editor->set_caret_column(p_column, false);
callable_mp((TextEdit *)text_editor, &TextEdit::center_viewport_to_caret).call_deferred(0);
}
@@ -1443,13 +1449,7 @@ void CodeTextEditor::goto_error() {
corrected_column -= tab_count * (indent_size - 1);
}
- if (text_editor->get_line_count() != error_line) {
- text_editor->unfold_line(error_line);
- }
- text_editor->remove_secondary_carets();
- text_editor->set_caret_line(error_line);
- text_editor->set_caret_column(corrected_column);
- text_editor->center_viewport_to_caret();
+ goto_line_centered(error_line, corrected_column);
}
}
diff --git a/editor/code_editor.h b/editor/code_editor.h
index 28f6944b66..d209d5c7a2 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -246,9 +246,9 @@ public:
/// by adding or removing comment delimiter
void toggle_inline_comment(const String &delimiter);
- void goto_line(int p_line);
+ void goto_line(int p_line, int p_column = 0);
void goto_line_selection(int p_line, int p_begin, int p_end);
- void goto_line_centered(int p_line);
+ void goto_line_centered(int p_line, int p_column = 0);
void set_executing_line(int p_line);
void clear_executing_line();
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index 32b2133f23..fb007aee28 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -88,7 +88,7 @@ void EditorAutoloadSettings::_notification(int p_what) {
}
bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, String *r_error) {
- if (!p_name.is_valid_identifier()) {
+ if (!p_name.is_valid_ascii_identifier()) {
if (r_error) {
*r_error = TTR("Invalid name.") + " ";
if (p_name.size() > 0 && p_name.left(1).is_numeric()) {
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 199383c391..1e5acce032 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -4270,7 +4270,7 @@ void EditorInspector::_check_meta_name() {
if (meta_name.is_empty()) {
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name can't be empty."), EditorValidationPanel::MSG_ERROR);
- } else if (!meta_name.is_valid_identifier()) {
+ } else if (!meta_name.is_valid_ascii_identifier()) {
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name must be a valid identifier."), EditorValidationPanel::MSG_ERROR);
} else if (object->has_meta(meta_name)) {
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, vformat(TTR("Metadata with name \"%s\" already exists."), meta_name), EditorValidationPanel::MSG_ERROR);
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index baa44f56c4..2c30cb8265 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -52,10 +52,6 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
err_str = String::utf8(p_file) + ":" + itos(p_line) + " - " + String::utf8(p_error);
}
- if (p_editor_notify) {
- err_str += " (User)";
- }
-
MessageType message_type = p_type == ERR_HANDLER_WARNING ? MSG_TYPE_WARNING : MSG_TYPE_ERROR;
if (!Thread::is_main_thread()) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index d19d4740ee..b2504e1670 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -740,6 +740,7 @@ void EditorNode::_notification(int p_what) {
RenderingServer::get_singleton()->viewport_set_disable_2d(get_scene_root()->get_viewport_rid(), true);
RenderingServer::get_singleton()->viewport_set_environment_mode(get_viewport()->get_viewport_rid(), RenderingServer::VIEWPORT_ENVIRONMENT_DISABLED);
+ DisplayServer::get_singleton()->screen_set_keep_on(EDITOR_GET("interface/editor/keep_screen_on"));
feature_profile_manager->notify_changed();
@@ -838,6 +839,7 @@ void EditorNode::_notification(int p_what) {
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor")) {
_update_update_spinner();
_update_vsync_mode();
+ DisplayServer::get_singleton()->screen_set_keep_on(EDITOR_GET("interface/editor/keep_screen_on"));
}
#if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index a4cb3fbb68..fe2fad0cb6 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -467,6 +467,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/show_update_spinner", 0, "Auto (Disabled),Enabled,Disabled")
#endif
+ _initial_set("interface/editor/keep_screen_on", false);
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "interface/editor/low_processor_mode_sleep_usec", 6900, "1,100000,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
// Default unfocused usec sleep is for 10 FPS. Allow an unfocused FPS limit
// as low as 1 FPS for those who really need low power usage (but don't need
@@ -767,6 +768,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/2d/bone_outline_size", 2.0, "0.01,8,0.01,or_greater")
_initial_set("editors/2d/viewport_border_color", Color(0.4, 0.4, 1.0, 0.4));
_initial_set("editors/2d/use_integer_zoom_by_default", false);
+ EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/2d/zoom_speed_factor", 1.1, "1.01,2,0.01")
// Panning
// Enum should be in sync with ControlScheme in ViewPanner.
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index 9f9bdb37b3..5372d33b4c 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -35,6 +35,7 @@
#include "core/os/keyboard.h"
#include "editor/editor_settings.h"
#include "editor/themes/editor_scale.h"
+#include "scene/theme/theme_db.h"
bool EditorSpinSlider::is_text_field() const {
return true;
@@ -383,7 +384,7 @@ void EditorSpinSlider::_draw_spin_slider() {
if (!hide_slider) {
if (get_step() == 1) {
- Ref<Texture2D> updown2 = get_theme_icon(is_read_only() ? SNAME("updown_disabled") : SNAME("updown"), SNAME("SpinBox"));
+ Ref<Texture2D> updown2 = is_read_only() ? theme_cache.updown_disabled_icon : theme_cache.updown_icon;
int updown_vofs = (size.height - updown2->get_height()) / 2;
if (rtl) {
updown_offset = sb->get_margin(SIDE_LEFT);
@@ -701,6 +702,9 @@ void EditorSpinSlider::_bind_methods() {
ADD_SIGNAL(MethodInfo("ungrabbed"));
ADD_SIGNAL(MethodInfo("value_focus_entered"));
ADD_SIGNAL(MethodInfo("value_focus_exited"));
+
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, EditorSpinSlider, updown_icon, "updown");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, EditorSpinSlider, updown_disabled_icon, "updown_disabled");
}
void EditorSpinSlider::_ensure_input_popup() {
diff --git a/editor/gui/editor_spin_slider.h b/editor/gui/editor_spin_slider.h
index a0c0685629..2476c2f71b 100644
--- a/editor/gui/editor_spin_slider.h
+++ b/editor/gui/editor_spin_slider.h
@@ -87,6 +87,11 @@ class EditorSpinSlider : public Range {
void _ensure_input_popup();
void _draw_spin_slider();
+ struct ThemeCache {
+ Ref<Texture2D> updown_icon;
+ Ref<Texture2D> updown_disabled_icon;
+ } theme_cache;
+
protected:
void _notification(int p_what);
virtual void gui_input(const Ref<InputEvent> &p_event) override;
diff --git a/editor/gui/editor_zoom_widget.cpp b/editor/gui/editor_zoom_widget.cpp
index 341da7bfaf..50a4f020ab 100644
--- a/editor/gui/editor_zoom_widget.cpp
+++ b/editor/gui/editor_zoom_widget.cpp
@@ -141,22 +141,15 @@ void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_inte
}
}
} else {
- // Base increment factor defined as the twelveth root of two.
- // This allows for a smooth geometric evolution of the zoom, with the advantage of
- // visiting all integer power of two scale factors.
- // Note: this is analogous to the 'semitone' interval in the music world
- // In order to avoid numerical imprecisions, we compute and edit a zoom index
- // with the following relation: zoom = 2 ^ (index / 12)
-
if (zoom < CMP_EPSILON || p_increment_count == 0) {
return;
}
- // zoom = 2**(index/12) => log2(zoom) = index/12
- float closest_zoom_index = Math::round(Math::log(zoom_noscale) * 12.f / Math::log(2.f));
-
- float new_zoom_index = closest_zoom_index + p_increment_count;
- float new_zoom = Math::pow(2.f, new_zoom_index / 12.f);
+ // Zoom is calculated as pow(zoom_factor, zoom_step).
+ // This ensures the zoom will always equal 100% when zoom_step is 0.
+ float zoom_factor = EDITOR_GET("editors/2d/zoom_speed_factor");
+ float current_zoom_step = Math::round(Math::log(zoom_noscale) / Math::log(zoom_factor));
+ float new_zoom = Math::pow(zoom_factor, current_zoom_step + p_increment_count);
// Restore Editor scale transformation.
new_zoom *= MAX(1, EDSCALE);
diff --git a/editor/icons/GuiSpinboxDown.svg b/editor/icons/GuiSpinboxDown.svg
new file mode 100644
index 0000000000..f8f473ce1a
--- /dev/null
+++ b/editor/icons/GuiSpinboxDown.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="8"><path fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".8" stroke-width="2" d="m12 2-4 4-4-4"/></svg> \ No newline at end of file
diff --git a/editor/icons/GuiSpinboxUp.svg b/editor/icons/GuiSpinboxUp.svg
new file mode 100644
index 0000000000..28bd0505d4
--- /dev/null
+++ b/editor/icons/GuiSpinboxUp.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="8"><path fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".8" stroke-width="2" d="m4 6 4-4 4 4"/></svg> \ No newline at end of file
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
index 015dfdbca5..32ff478c33 100644
--- a/editor/plugins/bone_map_editor_plugin.cpp
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -1229,9 +1229,11 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
picklist.push_back("face");
int head = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck);
if (head == -1) {
- search_path = skeleton->get_bone_children(neck);
- if (search_path.size() == 1) {
- head = search_path[0]; // Maybe only one child of the Neck is Head.
+ if (neck != -1) {
+ search_path = skeleton->get_bone_children(neck);
+ if (search_path.size() == 1) {
+ head = search_path[0]; // Maybe only one child of the Neck is Head.
+ }
}
}
if (head == -1) {
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index dc99b07067..7c350f1e6d 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -181,7 +181,7 @@ void Polygon2DEditor::_sync_bones() {
}
if (weights.size() == 0) { //create them
- weights.resize(node->get_polygon().size());
+ weights.resize(wc);
float *w = weights.ptrw();
for (int j = 0; j < wc; j++) {
w[j] = 0.0;
@@ -850,8 +850,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (mm.is_valid()) {
if (uv_drag) {
Vector2 uv_drag_to = mm->get_position();
- uv_drag_to = snap_point(uv_drag_to); // FIXME: Only works correctly with 'UV_MODE_EDIT_POINT', it's imprecise with the rest.
- Vector2 drag = mtx.affine_inverse().xform(uv_drag_to) - mtx.affine_inverse().xform(uv_drag_from);
+ uv_drag_to = snap_point(uv_drag_to);
+ Vector2 drag = mtx.affine_inverse().basis_xform(uv_drag_to - uv_drag_from);
switch (uv_move_current) {
case UV_MODE_CREATE: {
@@ -1166,12 +1166,8 @@ void Polygon2DEditor::_uv_draw() {
poly_line_color.a *= 0.25;
}
Color polygon_line_color = Color(0.5, 0.5, 0.9);
- Vector<Color> polygon_fill_color;
- {
- Color pf = polygon_line_color;
- pf.a *= 0.5;
- polygon_fill_color.push_back(pf);
- }
+ Color polygon_fill_color = polygon_line_color;
+ polygon_fill_color.a *= 0.5;
Color prev_color = Color(0.5, 0.5, 0.5);
int uv_draw_max = uvs.size();
@@ -1216,7 +1212,7 @@ void Polygon2DEditor::_uv_draw() {
uv_edit_draw->draw_line(mtx.xform(uvs[idx]), mtx.xform(uvs[idx_next]), polygon_line_color, Math::round(EDSCALE));
}
if (points.size() >= 3) {
- uv_edit_draw->draw_polygon(polypoints, polygon_fill_color);
+ uv_edit_draw->draw_colored_polygon(polypoints, polygon_fill_color);
}
}
@@ -1308,8 +1304,8 @@ void Polygon2DEditor::_bind_methods() {
Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
if (use_snap) {
- p_target.x = Math::snap_scalar(snap_offset.x * uv_draw_zoom - uv_draw_ofs.x, snap_step.x * uv_draw_zoom, p_target.x);
- p_target.y = Math::snap_scalar(snap_offset.y * uv_draw_zoom - uv_draw_ofs.y, snap_step.y * uv_draw_zoom, p_target.y);
+ p_target.x = Math::snap_scalar((snap_offset.x - uv_draw_ofs.x) * uv_draw_zoom, snap_step.x * uv_draw_zoom, p_target.x);
+ p_target.y = Math::snap_scalar((snap_offset.y - uv_draw_ofs.y) * uv_draw_zoom, snap_step.y * uv_draw_zoom, p_target.y);
}
return p_target;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 213af652ca..b27e768999 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -493,7 +493,7 @@ void ScriptEditor::_goto_script_line(Ref<RefCounted> p_script, int p_line) {
if (ScriptTextEditor *script_text_editor = Object::cast_to<ScriptTextEditor>(current)) {
script_text_editor->goto_line_centered(p_line);
} else if (current) {
- current->goto_line(p_line, true);
+ current->goto_line(p_line);
}
_save_history();
@@ -1857,17 +1857,13 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
}
void ScriptEditor::_members_overview_selected(int p_idx) {
- ScriptEditorBase *se = _get_current_editor();
- if (!se) {
- return;
+ int line = members_overview->get_item_metadata(p_idx);
+ ScriptEditorBase *current = _get_current_editor();
+ if (ScriptTextEditor *script_text_editor = Object::cast_to<ScriptTextEditor>(current)) {
+ script_text_editor->goto_line_centered(line);
+ } else if (current) {
+ current->goto_line(line);
}
- // Go to the member's line and reset the cursor column. We can't change scroll_position
- // directly until we have gone to the line first, since code might be folded.
- se->goto_line(members_overview->get_item_metadata(p_idx));
- Dictionary state = se->get_edit_state();
- state["column"] = 0;
- state["scroll_position"] = members_overview->get_item_metadata(p_idx);
- se->set_edit_state(state);
}
void ScriptEditor::_help_overview_selected(int p_idx) {
@@ -2711,9 +2707,11 @@ void ScriptEditor::apply_scripts() const {
}
void ScriptEditor::reload_scripts(bool p_refresh_only) {
- if (external_editor_active) {
- return;
- }
+ // Call deferred to make sure it runs on the main thread.
+ callable_mp(this, &ScriptEditor::_reload_scripts).call_deferred(p_refresh_only);
+}
+
+void ScriptEditor::_reload_scripts(bool p_refresh_only) {
for (int i = 0; i < tab_container->get_tab_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
if (!se) {
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 9db1aff76a..5dbf5d6f38 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -170,7 +170,7 @@ public:
virtual Variant get_edit_state() = 0;
virtual void set_edit_state(const Variant &p_state) = 0;
virtual Variant get_navigation_state() = 0;
- virtual void goto_line(int p_line, bool p_with_error = false) = 0;
+ virtual void goto_line(int p_line, int p_column = 0) = 0;
virtual void set_executing_line(int p_line) = 0;
virtual void clear_executing_line() = 0;
virtual void trim_trailing_whitespace() = 0;
@@ -436,6 +436,7 @@ class ScriptEditor : public PanelContainer {
void _file_removed(const String &p_file);
void _autosave_scripts();
void _update_autosave_timer();
+ void _reload_scripts(bool p_refresh_only = false);
void _update_members_overview_visibility();
void _update_members_overview();
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 070471f3f3..eb39a27d02 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -302,16 +302,14 @@ void ScriptTextEditor::_warning_clicked(const Variant &p_line) {
void ScriptTextEditor::_error_clicked(const Variant &p_line) {
if (p_line.get_type() == Variant::INT) {
- code_editor->get_text_editor()->remove_secondary_carets();
- code_editor->get_text_editor()->set_caret_line(p_line.operator int64_t());
+ goto_line_centered(p_line.operator int64_t());
} else if (p_line.get_type() == Variant::DICTIONARY) {
Dictionary meta = p_line.operator Dictionary();
const String path = meta["path"].operator String();
const int line = meta["line"].operator int64_t();
const int column = meta["column"].operator int64_t();
if (path.is_empty()) {
- code_editor->get_text_editor()->remove_secondary_carets();
- code_editor->get_text_editor()->set_caret_line(line);
+ goto_line_centered(line, column);
} else {
Ref<Resource> scr = ResourceLoader::load(path);
if (!scr.is_valid()) {
@@ -456,16 +454,16 @@ void ScriptTextEditor::tag_saved_version() {
code_editor->get_text_editor()->tag_saved_version();
}
-void ScriptTextEditor::goto_line(int p_line, bool p_with_error) {
- code_editor->goto_line(p_line);
+void ScriptTextEditor::goto_line(int p_line, int p_column) {
+ code_editor->goto_line(p_line, p_column);
}
void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
code_editor->goto_line_selection(p_line, p_begin, p_end);
}
-void ScriptTextEditor::goto_line_centered(int p_line) {
- code_editor->goto_line_centered(p_line);
+void ScriptTextEditor::goto_line_centered(int p_line, int p_column) {
+ code_editor->goto_line_centered(p_line, p_column);
}
void ScriptTextEditor::set_executing_line(int p_line) {
@@ -919,8 +917,7 @@ void ScriptTextEditor::_breakpoint_item_pressed(int p_idx) {
if (p_idx < 4) { // Any item before the separator.
_edit_option(breakpoints_menu->get_item_id(p_idx));
} else {
- code_editor->goto_line(breakpoints_menu->get_item_metadata(p_idx));
- callable_mp((TextEdit *)code_editor->get_text_editor(), &TextEdit::center_viewport_to_caret).call_deferred(0); // Needs to be deferred, because goto uses call_deferred().
+ code_editor->goto_line_centered(breakpoints_menu->get_item_metadata(p_idx));
}
}
@@ -1816,9 +1813,9 @@ static String _get_dropped_resource_line(const Ref<Resource> &p_resource, bool p
}
if (is_script) {
- variable_name = variable_name.to_pascal_case().validate_identifier();
+ variable_name = variable_name.to_pascal_case().validate_ascii_identifier();
} else {
- variable_name = variable_name.to_snake_case().to_upper().validate_identifier();
+ variable_name = variable_name.to_snake_case().to_upper().validate_ascii_identifier();
}
return vformat("const %s = preload(%s)", variable_name, _quote_drop_data(path));
}
@@ -1932,13 +1929,13 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
path = sn->get_path_to(node);
}
for (const String &segment : path.split("/")) {
- if (!segment.is_valid_identifier()) {
+ if (!segment.is_valid_ascii_identifier()) {
path = _quote_drop_data(path);
break;
}
}
- String variable_name = String(node->get_name()).to_snake_case().validate_identifier();
+ String variable_name = String(node->get_name()).to_snake_case().validate_ascii_identifier();
if (use_type) {
StringName class_name = node->get_class_name();
Ref<Script> node_script = node->get_script();
@@ -1975,7 +1972,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
}
for (const String &segment : path.split("/")) {
- if (!segment.is_valid_identifier()) {
+ if (!segment.is_valid_ascii_identifier()) {
path = _quote_drop_data(path);
break;
}
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 8c2ec1561b..7aa0726479 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -234,9 +234,9 @@ public:
virtual void convert_indent() override;
virtual void tag_saved_version() override;
- virtual void goto_line(int p_line, bool p_with_error = false) override;
+ virtual void goto_line(int p_line, int p_column = 0) override;
void goto_line_selection(int p_line, int p_begin, int p_end);
- void goto_line_centered(int p_line);
+ void goto_line_centered(int p_line, int p_column = 0);
virtual void set_executing_line(int p_line) override;
virtual void clear_executing_line() override;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 02cb76ac10..04a392768a 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -45,6 +45,16 @@
#include "scene/gui/item_list.h"
#include "scene/gui/texture_rect.h"
+Ref<Resource> ShaderEditorPlugin::_get_current_shader() {
+ int index = shader_tabs->get_current_tab();
+ ERR_FAIL_INDEX_V(index, shader_tabs->get_tab_count(), Ref<Resource>());
+ if (edited_shaders[index].shader.is_valid()) {
+ return edited_shaders[index].shader;
+ } else {
+ return edited_shaders[index].shader_inc;
+ }
+}
+
void ShaderEditorPlugin::_update_shader_list() {
shader_list->clear();
for (EditedShader &edited_shader : edited_shaders) {
@@ -93,9 +103,7 @@ void ShaderEditorPlugin::_update_shader_list() {
shader_list->select(shader_tabs->get_current_tab());
}
- for (int i = FILE_SAVE; i < FILE_MAX; i++) {
- file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), edited_shaders.is_empty());
- }
+ _set_file_specific_items_disabled(edited_shaders.is_empty());
_update_shader_list_status();
}
@@ -362,6 +370,61 @@ void ShaderEditorPlugin::_shader_list_clicked(int p_item, Vector2 p_local_mouse_
if (p_mouse_button_index == MouseButton::MIDDLE) {
_close_shader(p_item);
}
+ if (p_mouse_button_index == MouseButton::RIGHT) {
+ _make_script_list_context_menu();
+ }
+}
+
+void ShaderEditorPlugin::_setup_popup_menu(PopupMenuType p_type, PopupMenu *p_menu) {
+ if (p_type == FILE) {
+ p_menu->add_shortcut(ED_SHORTCUT("shader_editor/new", TTR("New Shader..."), KeyModifierMask::CMD_OR_CTRL | Key::N), FILE_NEW);
+ p_menu->add_shortcut(ED_SHORTCUT("shader_editor/new_include", TTR("New Shader Include..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::N), FILE_NEW_INCLUDE);
+ p_menu->add_separator();
+ p_menu->add_shortcut(ED_SHORTCUT("shader_editor/open", TTR("Load Shader File..."), KeyModifierMask::CMD_OR_CTRL | Key::O), FILE_OPEN);
+ p_menu->add_shortcut(ED_SHORTCUT("shader_editor/open_include", TTR("Load Shader Include File..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::O), FILE_OPEN_INCLUDE);
+ }
+
+ if (p_type == FILE || p_type == CONTEXT_VALID_ITEM) {
+ p_menu->add_shortcut(ED_SHORTCUT("shader_editor/save", TTR("Save File"), KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL | Key::S), FILE_SAVE);
+ p_menu->add_shortcut(ED_SHORTCUT("shader_editor/save_as", TTR("Save File As...")), FILE_SAVE_AS);
+ }
+
+ if (p_type == FILE) {
+ p_menu->add_separator();
+ p_menu->add_item(TTR("Open File in Inspector"), FILE_INSPECT);
+ p_menu->add_separator();
+ p_menu->add_shortcut(ED_SHORTCUT("shader_editor/close_file", TTR("Close File"), KeyModifierMask::CMD_OR_CTRL | Key::W), FILE_CLOSE);
+ } else {
+ p_menu->add_shortcut(ED_SHORTCUT("shader_editor/close_file", TTR("Close File"), KeyModifierMask::CMD_OR_CTRL | Key::W), FILE_CLOSE);
+ p_menu->add_item(TTR("Close All"), CLOSE_ALL);
+ p_menu->add_item(TTR("Close Other Tabs"), CLOSE_OTHER_TABS);
+ if (p_type == CONTEXT_VALID_ITEM) {
+ p_menu->add_separator();
+ p_menu->add_item(TTR("Copy Script Path"), COPY_PATH);
+ p_menu->add_item(TTR("Show in File System"), SHOW_IN_FILE_SYSTEM);
+ }
+ }
+}
+
+void ShaderEditorPlugin::_make_script_list_context_menu() {
+ context_menu->clear();
+
+ int selected = shader_tabs->get_current_tab();
+ if (selected < 0 || selected >= shader_tabs->get_tab_count()) {
+ return;
+ }
+
+ Control *control = shader_tabs->get_tab_control(selected);
+ bool is_valid_editor_control = Object::cast_to<TextShaderEditor>(control) || Object::cast_to<VisualShaderEditor>(control);
+
+ _setup_popup_menu(is_valid_editor_control ? CONTEXT_VALID_ITEM : CONTEXT, context_menu);
+
+ context_menu->set_item_disabled(context_menu->get_item_index(CLOSE_ALL), shader_tabs->get_tab_count() <= 0);
+ context_menu->set_item_disabled(context_menu->get_item_index(CLOSE_OTHER_TABS), shader_tabs->get_tab_count() <= 1);
+
+ context_menu->set_position(main_split->get_screen_position() + main_split->get_local_mouse_position());
+ context_menu->reset_size();
+ context_menu->popup();
}
void ShaderEditorPlugin::_close_shader(int p_index) {
@@ -486,6 +549,31 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) {
case FILE_CLOSE: {
_close_shader(shader_tabs->get_current_tab());
} break;
+ case CLOSE_ALL: {
+ while (shader_tabs->get_tab_count() > 0) {
+ _close_shader(0);
+ }
+ } break;
+ case CLOSE_OTHER_TABS: {
+ int index = shader_tabs->get_current_tab();
+ for (int i = 0; i < index; i++) {
+ _close_shader(0);
+ }
+ while (shader_tabs->get_tab_count() > 1) {
+ _close_shader(1);
+ }
+ } break;
+ case SHOW_IN_FILE_SYSTEM: {
+ Ref<Resource> shader = _get_current_shader();
+ String path = shader->get_path();
+ if (!path.is_empty()) {
+ FileSystemDock::get_singleton()->navigate_to_path(path);
+ }
+ } break;
+ case COPY_PATH: {
+ Ref<Resource> shader = _get_current_shader();
+ DisplayServer::get_singleton()->clipboard_set(shader->get_path());
+ } break;
}
}
@@ -652,6 +740,14 @@ void ShaderEditorPlugin::_res_saved_callback(const Ref<Resource> &p_res) {
}
}
+void ShaderEditorPlugin::_set_file_specific_items_disabled(bool p_disabled) {
+ PopupMenu *file_popup_menu = file_menu->get_popup();
+ file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_SAVE), p_disabled);
+ file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_SAVE_AS), p_disabled);
+ file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_INSPECT), p_disabled);
+ file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_CLOSE), p_disabled);
+}
+
void ShaderEditorPlugin::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
@@ -679,23 +775,15 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
file_menu = memnew(MenuButton);
file_menu->set_text(TTR("File"));
file_menu->set_shortcut_context(main_split);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/new", TTR("New Shader..."), KeyModifierMask::CMD_OR_CTRL | Key::N), FILE_NEW);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/new_include", TTR("New Shader Include..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::N), FILE_NEW_INCLUDE);
- file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/open", TTR("Load Shader File..."), KeyModifierMask::CMD_OR_CTRL | Key::O), FILE_OPEN);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/open_include", TTR("Load Shader Include File..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::O), FILE_OPEN_INCLUDE);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/save", TTR("Save File"), KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL | Key::S), FILE_SAVE);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/save_as", TTR("Save File As...")), FILE_SAVE_AS);
- file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_item(TTR("Open File in Inspector"), FILE_INSPECT);
- file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/close_file", TTR("Close File"), KeyModifierMask::CMD_OR_CTRL | Key::W), FILE_CLOSE);
+ _setup_popup_menu(FILE, file_menu->get_popup());
file_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed));
menu_hb->add_child(file_menu);
- for (int i = FILE_SAVE; i < FILE_MAX; i++) {
- file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), true);
- }
+ _set_file_specific_items_disabled(true);
+
+ context_menu = memnew(PopupMenu);
+ add_child(context_menu);
+ context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed));
Control *padding = memnew(Control);
padding->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -718,6 +806,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
vb->add_child(shader_list);
shader_list->connect(SceneStringName(item_selected), callable_mp(this, &ShaderEditorPlugin::_shader_selected));
shader_list->connect("item_clicked", callable_mp(this, &ShaderEditorPlugin::_shader_list_clicked));
+ shader_list->set_allow_rmb_select(true);
SET_DRAG_FORWARDING_GCD(shader_list, ShaderEditorPlugin);
main_split->add_child(vb);
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index f9b9405e45..31be64a56c 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -60,8 +60,6 @@ class ShaderEditorPlugin : public EditorPlugin {
LocalVector<EditedShader> edited_shaders;
- // Always valid operations come first in the enum, file-specific ones
- // should go after FILE_SAVE which is used to build the menu accordingly.
enum {
FILE_NEW,
FILE_NEW_INCLUDE,
@@ -71,7 +69,16 @@ class ShaderEditorPlugin : public EditorPlugin {
FILE_SAVE_AS,
FILE_INSPECT,
FILE_CLOSE,
- FILE_MAX
+ CLOSE_ALL,
+ CLOSE_OTHER_TABS,
+ SHOW_IN_FILE_SYSTEM,
+ COPY_PATH,
+ };
+
+ enum PopupMenuType {
+ FILE,
+ CONTEXT,
+ CONTEXT_VALID_ITEM,
};
HSplitContainer *main_split = nullptr;
@@ -80,6 +87,7 @@ class ShaderEditorPlugin : public EditorPlugin {
Button *button = nullptr;
MenuButton *file_menu = nullptr;
+ PopupMenu *context_menu = nullptr;
WindowWrapper *window_wrapper = nullptr;
Button *make_floating = nullptr;
@@ -88,15 +96,19 @@ class ShaderEditorPlugin : public EditorPlugin {
float text_shader_zoom_factor = 1.0f;
+ Ref<Resource> _get_current_shader();
void _update_shader_list();
void _shader_selected(int p_index);
void _shader_list_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index);
+ void _setup_popup_menu(PopupMenuType p_type, PopupMenu *p_menu);
+ void _make_script_list_context_menu();
void _menu_item_pressed(int p_index);
void _resource_saved(Object *obj);
void _close_shader(int p_index);
void _close_builtin_shaders_from_scene(const String &p_scene);
void _file_removed(const String &p_removed_file);
void _res_saved_callback(const Ref<Resource> &p_res);
+ void _set_file_specific_items_disabled(bool p_disabled);
void _shader_created(Ref<Shader> p_shader);
void _shader_include_created(Ref<ShaderInclude> p_shader_inc);
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index ecdc4acf47..cb1f6c1ec6 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -304,8 +304,8 @@ void TextEditor::tag_saved_version() {
code_editor->get_text_editor()->tag_saved_version();
}
-void TextEditor::goto_line(int p_line, bool p_with_error) {
- code_editor->goto_line(p_line);
+void TextEditor::goto_line(int p_line, int p_column) {
+ code_editor->goto_line(p_line, p_column);
}
void TextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index 268e5c32b4..1acec4e959 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -129,7 +129,7 @@ public:
virtual PackedInt32Array get_breakpoints() override;
virtual void set_breakpoint(int p_line, bool p_enabled) override{};
virtual void clear_breakpoints() override{};
- virtual void goto_line(int p_line, bool p_with_error = false) override;
+ virtual void goto_line(int p_line, int p_column = 0) override;
void goto_line_selection(int p_line, int p_begin, int p_end);
virtual void set_executing_line(int p_line) override;
virtual void clear_executing_line() override;
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index 6cd92bdf57..4d8f3298b6 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -779,7 +779,7 @@ void TextShaderEditor::_show_warnings_panel(bool p_show) {
void TextShaderEditor::_warning_clicked(const Variant &p_line) {
if (p_line.get_type() == Variant::INT) {
- code_editor->get_text_editor()->set_caret_line(p_line.operator int64_t());
+ code_editor->goto_line_centered(p_line.operator int64_t());
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index c183aceb13..aaa65a0fc4 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -1590,7 +1590,7 @@ void VisualShaderEditor::clear_custom_types() {
}
void VisualShaderEditor::add_custom_type(const String &p_name, const String &p_type, const Ref<Script> &p_script, const String &p_description, int p_return_icon_type, const String &p_category, bool p_highend) {
- ERR_FAIL_COND(!p_name.is_valid_identifier());
+ ERR_FAIL_COND(!p_name.is_valid_ascii_identifier());
ERR_FAIL_COND(p_type.is_empty() && !p_script.is_valid());
for (int i = 0; i < add_options.size(); i++) {
@@ -5791,7 +5791,7 @@ void VisualShaderEditor::_varying_create() {
}
void VisualShaderEditor::_varying_name_changed(const String &p_name) {
- if (!p_name.is_valid_identifier()) {
+ if (!p_name.is_valid_ascii_identifier()) {
varying_error_label->show();
varying_error_label->set_text(TTR("Invalid name for varying."));
add_varying_dialog->get_ok_button()->set_disabled(true);
@@ -6807,6 +6807,7 @@ VisualShaderEditor::VisualShaderEditor() {
// NODE3D-FOR-ALL
+ add_options.push_back(AddOption("ClipSpaceFar", "Input/All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "clip_space_far", "CLIP_SPACE_FAR"), { "clip_space_far" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Exposure", "Input/All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "exposure", "EXPOSURE"), { "exposure" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InvProjectionMatrix", "Input/All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_projection_matrix", "INV_PROJECTION_MATRIX"), { "inv_projection_matrix" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InvViewMatrix", "Input/All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_view_matrix", "INV_VIEW_MATRIX"), { "inv_view_matrix" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
@@ -7978,7 +7979,7 @@ void VisualShaderNodePortPreview::_shader_changed() {
preview_shader->set_code(shader_code);
for (int i = 0; i < default_textures.size(); i++) {
int j = 0;
- for (List<Ref<Texture2D>>::ConstIterator itr = default_textures[i].params.begin(); itr != default_textures[i].params.end(); ++itr, ++j) {
+ for (List<Ref<Texture>>::ConstIterator itr = default_textures[i].params.begin(); itr != default_textures[i].params.end(); ++itr, ++j) {
preview_shader->set_default_texture_parameter(default_textures[i].name, *itr, j);
}
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 3de9ebcfdd..55c361de4b 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -93,6 +93,7 @@ void ProjectManager::_notification(int p_what) {
} break;
case NOTIFICATION_READY: {
+ DisplayServer::get_singleton()->screen_set_keep_on(EDITOR_GET("interface/editor/keep_screen_on"));
const int default_sorting = (int)EDITOR_GET("project_manager/sorting_order");
filter_option->select(default_sorting);
project_list->set_order_option(default_sorting);
diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp
index 092a6a1a18..541ab01e62 100644
--- a/editor/project_manager/project_list.cpp
+++ b/editor/project_manager/project_list.cpp
@@ -760,7 +760,7 @@ void ProjectList::_create_project_item_control(int p_index) {
hb->set_tags(item.tags, this);
hb->set_unsupported_features(item.unsupported_features.duplicate());
hb->set_project_version(item.project_version);
- hb->set_last_edited_info(Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true));
+ hb->set_last_edited_info(!item.missing ? Time::get_singleton()->get_datetime_string_from_unix_time(item.last_edited, true) : TTR("Missing Date"));
hb->set_is_favorite(item.favorite);
hb->set_is_missing(item.missing);
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index bdf4e41c5f..373895b653 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -221,7 +221,7 @@ void ProjectSettingsEditor::_update_property_box() {
const Vector<String> names = name.split("/");
for (int i = 0; i < names.size(); i++) {
- if (!names[i].is_valid_identifier()) {
+ if (!names[i].is_valid_ascii_identifier()) {
return;
}
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 30050901d9..02c8a03ac6 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1181,7 +1181,16 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
case TOOL_OPEN_DOCUMENTATION: {
List<Node *> selection = editor_selection->get_selected_node_list();
for (const Node *node : selection) {
- ScriptEditor::get_singleton()->goto_help("class_name:" + node->get_class());
+ String class_name;
+ Ref<Script> script_base = node->get_script();
+ if (script_base.is_valid()) {
+ class_name = script_base->get_global_name();
+ }
+ if (class_name.is_empty()) {
+ class_name = node->get_class();
+ }
+
+ ScriptEditor::get_singleton()->goto_help("class_name:" + class_name);
}
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
} break;
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 64c97e1641..f8673ddd44 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -100,7 +100,7 @@ static Vector<String> _get_hierarchy(const String &p_class_name) {
}
if (hierarchy.is_empty()) {
- if (p_class_name.is_valid_identifier()) {
+ if (p_class_name.is_valid_ascii_identifier()) {
hierarchy.push_back(p_class_name);
}
hierarchy.push_back("Object");
diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp
index 0582c598a1..c05f60545d 100644
--- a/editor/shader_globals_editor.cpp
+++ b/editor/shader_globals_editor.cpp
@@ -349,7 +349,7 @@ String ShaderGlobalsEditor::_check_new_variable_name(const String &p_variable_na
return TTR("Name cannot be empty.");
}
- if (!p_variable_name.is_valid_identifier()) {
+ if (!p_variable_name.is_valid_ascii_identifier()) {
return TTR("Name must be a valid identifier.");
}
diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp
index 9237a62a74..f654707630 100644
--- a/editor/themes/editor_theme_manager.cpp
+++ b/editor/themes/editor_theme_manager.cpp
@@ -1471,8 +1471,44 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
}
// SpinBox.
- p_theme->set_icon("updown", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxUpdown"), EditorStringName(EditorIcons)));
- p_theme->set_icon("updown_disabled", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxUpdownDisabled"), EditorStringName(EditorIcons)));
+ {
+ Ref<Texture2D> empty_icon = memnew(ImageTexture);
+ p_theme->set_icon("updown", "SpinBox", empty_icon);
+ p_theme->set_icon("up", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxUp"), EditorStringName(EditorIcons)));
+ p_theme->set_icon("up_hover", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxUp"), EditorStringName(EditorIcons)));
+ p_theme->set_icon("up_pressed", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxUp"), EditorStringName(EditorIcons)));
+ p_theme->set_icon("up_disabled", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxUp"), EditorStringName(EditorIcons)));
+ p_theme->set_icon("down", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxDown"), EditorStringName(EditorIcons)));
+ p_theme->set_icon("down_hover", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxDown"), EditorStringName(EditorIcons)));
+ p_theme->set_icon("down_pressed", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxDown"), EditorStringName(EditorIcons)));
+ p_theme->set_icon("down_disabled", "SpinBox", p_theme->get_icon(SNAME("GuiSpinboxDown"), EditorStringName(EditorIcons)));
+
+ p_theme->set_stylebox("up_background", "SpinBox", make_empty_stylebox());
+ p_theme->set_stylebox("up_background_hovered", "SpinBox", p_config.button_style_hover);
+ p_theme->set_stylebox("up_background_pressed", "SpinBox", p_config.button_style_pressed);
+ p_theme->set_stylebox("up_background_disabled", "SpinBox", make_empty_stylebox());
+ p_theme->set_stylebox("down_background", "SpinBox", make_empty_stylebox());
+ p_theme->set_stylebox("down_background_hovered", "SpinBox", p_config.button_style_hover);
+ p_theme->set_stylebox("down_background_pressed", "SpinBox", p_config.button_style_pressed);
+ p_theme->set_stylebox("down_background_disabled", "SpinBox", make_empty_stylebox());
+
+ p_theme->set_color("up_icon_modulate", "SpinBox", p_config.font_color);
+ p_theme->set_color("up_hover_icon_modulate", "SpinBox", p_config.font_hover_color);
+ p_theme->set_color("up_pressed_icon_modulate", "SpinBox", p_config.font_pressed_color);
+ p_theme->set_color("up_disabled_icon_modulate", "SpinBox", p_config.font_disabled_color);
+ p_theme->set_color("down_icon_modulate", "SpinBox", p_config.font_color);
+ p_theme->set_color("down_hover_icon_modulate", "SpinBox", p_config.font_hover_color);
+ p_theme->set_color("down_pressed_icon_modulate", "SpinBox", p_config.font_pressed_color);
+ p_theme->set_color("down_disabled_icon_modulate", "SpinBox", p_config.font_disabled_color);
+
+ p_theme->set_stylebox("field_and_buttons_separator", "SpinBox", make_empty_stylebox());
+ p_theme->set_stylebox("up_down_buttons_separator", "SpinBox", make_empty_stylebox());
+
+ p_theme->set_constant("buttons_vertical_separation", "SpinBox", 0);
+ p_theme->set_constant("field_and_buttons_separation", "SpinBox", 2);
+ p_theme->set_constant("buttons_width", "SpinBox", 16);
+ p_theme->set_constant("set_min_buttons_width_from_icons", "SpinBox", 1);
+ }
// ProgressBar.
p_theme->set_stylebox("background", "ProgressBar", make_stylebox(p_theme->get_icon(SNAME("GuiProgressBar"), EditorStringName(EditorIcons)), 4, 4, 4, 4, 0, 0, 0, 0));
@@ -1858,6 +1894,10 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme
editor_spin_label_bg->set_border_width_all(0);
p_theme->set_stylebox("label_bg", "EditorSpinSlider", editor_spin_label_bg);
+ // TODO Use separate arrows instead like on SpinBox. Planned for a different PR.
+ p_theme->set_icon("updown", "EditorSpinSlider", p_theme->get_icon(SNAME("GuiSpinboxUpdown"), EditorStringName(EditorIcons)));
+ p_theme->set_icon("updown_disabled", "EditorSpinSlider", p_theme->get_icon(SNAME("GuiSpinboxUpdownDisabled"), EditorStringName(EditorIcons)));
+
// Launch Pad and Play buttons.
Ref<StyleBoxFlat> style_launch_pad = make_flat_stylebox(p_config.dark_color_1, 2 * EDSCALE, 0, 2 * EDSCALE, 0, p_config.corner_radius);
style_launch_pad->set_corner_radius_all(p_config.corner_radius * EDSCALE);
diff --git a/main/main.cpp b/main/main.cpp
index d76ddd5a66..a4f0ae54e9 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2555,6 +2555,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true);
// OpenXR project extensions settings.
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/extensions/debug_utils", PROPERTY_HINT_ENUM, "Disabled,Error,Warning,Info,Verbose"), "0");
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "xr/openxr/extensions/debug_message_types", PROPERTY_HINT_FLAGS, "General,Validation,Performance,Conformance"), "15");
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_unobstructed_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_controller_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT
diff --git a/misc/dist/linux/org.godotengine.Godot.desktop b/misc/dist/linux/org.godotengine.Godot.desktop
index d351839205..28669548d6 100644
--- a/misc/dist/linux/org.godotengine.Godot.desktop
+++ b/misc/dist/linux/org.godotengine.Godot.desktop
@@ -4,11 +4,13 @@ GenericName=Libre game engine
GenericName[el]=Ελεύθερη μηχανή παιχνιδιού
GenericName[fr]=Moteur de jeu libre
GenericName[nl]=Libre game-engine
+GenericName[ru]=Свободный игровой движок
GenericName[zh_CN]=自由的游戏引擎
Comment=Multi-platform 2D and 3D game engine with a feature-rich editor
Comment[el]=2D και 3D μηχανή παιχνιδιού πολλαπλών πλατφορμών με επεξεργαστή πλούσιο σε χαρακτηριστικά
Comment[fr]=Moteur de jeu 2D et 3D multiplateforme avec un éditeur riche en fonctionnalités
-Comment[nl]=Multi-platform 2D- en 3d-game-engine met een veelzijdige editor
+Comment[nl]=Multi-platform 2D- en 3D-game-engine met een veelzijdige editor
+Comment[ru]=Кроссплатформенный движок с многофункциональным редактором для 2D- и 3D-игр
Comment[zh_CN]=多平台 2D 和 3D 游戏引擎,带有功能丰富的编辑器
Exec=godot %f
Icon=godot
diff --git a/misc/extension_api_validation/4.3-stable.expected b/misc/extension_api_validation/4.3-stable.expected
index 733d85c46e..80735f28e7 100644
--- a/misc/extension_api_validation/4.3-stable.expected
+++ b/misc/extension_api_validation/4.3-stable.expected
@@ -26,3 +26,25 @@ Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list
draw_list_begin added a new optional debug argument called breadcrumb.
There used to be an Array argument as arg #9 initially, then changed to typedarray::RID in 4.1, and finally removed in 4.3.
Since we're adding a new one at the same location, we need to silence those warnings for 4.1 and 4.3.
+
+
+GH-95126
+--------
+Validate extension JSON: Error: Field 'classes/Shader/methods/get_default_texture_parameter/return_value': type changed value in new API, from "Texture2D" to "Texture".
+Validate extension JSON: Error: Field 'classes/Shader/methods/set_default_texture_parameter/arguments/1': type changed value in new API, from "Texture2D" to "Texture".
+Validate extension JSON: Error: Field 'classes/VisualShaderNodeCubemap/methods/get_cube_map/return_value': type changed value in new API, from "Cubemap" to "TextureLayered".
+Validate extension JSON: Error: Field 'classes/VisualShaderNodeCubemap/methods/set_cube_map/arguments/0': type changed value in new API, from "Cubemap" to "TextureLayered".
+Validate extension JSON: Error: Field 'classes/VisualShaderNodeCubemap/properties/cube_map': type changed value in new API, from "Cubemap" to "Cubemap,CompressedCubemap,PlaceholderCubemap,TextureCubemapRD".
+Validate extension JSON: Error: Field 'classes/VisualShaderNodeTexture2DArray/methods/get_texture_array/return_value': type changed value in new API, from "Texture2DArray" to "TextureLayered".
+Validate extension JSON: Error: Field 'classes/VisualShaderNodeTexture2DArray/methods/set_texture_array/arguments/0': type changed value in new API, from "Texture2DArray" to "TextureLayered".
+Validate extension JSON: Error: Field 'classes/VisualShaderNodeTexture2DArray/properties/texture_array': type changed value in new API, from "Texture2DArray" to "Texture2DArray,CompressedTexture2DArray,PlaceholderTexture2DArray,Texture2DArrayRD".
+
+Allow setting a cubemap as default parameter to shader.
+Compatibility methods registered.
+
+
+GH-93605
+--------
+Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/Semaphore/methods/post': arguments
+
+Optional arguments added. Compatibility methods registered.
diff --git a/modules/betsy/image_compress_betsy.cpp b/modules/betsy/image_compress_betsy.cpp
index 6a0862e729..7f723826d1 100644
--- a/modules/betsy/image_compress_betsy.cpp
+++ b/modules/betsy/image_compress_betsy.cpp
@@ -49,31 +49,6 @@ static int get_next_multiple(int n, int m) {
return n + (m - (n % m));
}
-static bool is_image_signed(const Image *r_img) {
- if (r_img->get_format() >= Image::FORMAT_RH && r_img->get_format() <= Image::FORMAT_RGBAH) {
- const uint16_t *img_data = reinterpret_cast<const uint16_t *>(r_img->ptr());
- const uint64_t img_size = r_img->get_data_size() / 2;
-
- for (uint64_t i = 0; i < img_size; i++) {
- if ((img_data[i] & 0x8000) != 0 && (img_data[i] & 0x7fff) != 0) {
- return true;
- }
- }
-
- } else if (r_img->get_format() >= Image::FORMAT_RF && r_img->get_format() <= Image::FORMAT_RGBAF) {
- const uint32_t *img_data = reinterpret_cast<const uint32_t *>(r_img->ptr());
- const uint64_t img_size = r_img->get_data_size() / 4;
-
- for (uint64_t i = 0; i < img_size; i++) {
- if ((img_data[i] & 0x80000000) != 0 && (img_data[i] & 0x7fffffff) != 0) {
- return true;
- }
- }
- }
-
- return false;
-}
-
Error _compress_betsy(BetsyFormat p_format, Image *r_img) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
@@ -125,7 +100,7 @@ Error _compress_betsy(BetsyFormat p_format, Image *r_img) {
case BETSY_FORMAT_BC6: {
err = compute_shader->parse_versions_from_text(bc6h_shader_glsl);
- if (is_image_signed(r_img)) {
+ if (r_img->detect_signed(true)) {
dest_format = Image::FORMAT_BPTC_RGBF;
version = "signed";
} else {
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index 6b905c5ea1..2087dde2a1 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -174,17 +174,7 @@ void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) {
p_image->convert(Image::FORMAT_RGBH);
}
- const uint8_t *rb = p_image->get_data().ptr();
-
- const uint16_t *source_data = reinterpret_cast<const uint16_t *>(&rb[0]);
- int pixel_element_count = w * h * 3;
- for (int i = 0; i < pixel_element_count; i++) {
- if ((source_data[i] & 0x8000) != 0 && (source_data[i] & 0x7fff) != 0) {
- is_signed = true;
- break;
- }
- }
-
+ is_signed = p_image->detect_signed();
target_format = is_signed ? Image::FORMAT_BPTC_RGBF : Image::FORMAT_BPTC_RGBFU;
} else {
p_image->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index a6fd3f94da..636339ef1d 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -97,8 +97,8 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri
}
processed_template = processed_template.replace("_BASE_", p_base_class_name)
- .replace("_CLASS_SNAKE_CASE_", p_class_name.to_snake_case().validate_identifier())
- .replace("_CLASS_", p_class_name.to_pascal_case().validate_identifier())
+ .replace("_CLASS_SNAKE_CASE_", p_class_name.to_snake_case().validate_ascii_identifier())
+ .replace("_CLASS_", p_class_name.to_pascal_case().validate_ascii_identifier())
.replace("_TS_", _get_indentation());
scr->set_source_code(processed_template);
return scr;
@@ -3486,7 +3486,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
// is not a valid identifier.
bool path_needs_quote = false;
for (const String &part : opt.split("/")) {
- if (!part.is_valid_identifier()) {
+ if (!part.is_valid_ascii_identifier()) {
path_needs_quote = true;
break;
}
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 759e92d68c..ac4bab6d84 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -509,7 +509,7 @@ private:
} profile;
#endif
- _FORCE_INLINE_ String _get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const;
+ _FORCE_INLINE_ String _get_call_error(const String &p_where, const Variant **p_argptrs, const Variant &p_ret, const Callable::CallError &p_err) const;
Variant _get_default_variant_for_data_type(const GDScriptDataType &p_data_type);
public:
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index ecef852b4b..6e32733faa 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -3122,6 +3122,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *
subscript->base = p_previous_operand;
subscript->index = parse_expression(false);
+#ifdef TOOLS_ENABLED
+ if (subscript->index != nullptr && subscript->index->type == Node::LITERAL) {
+ override_completion_context(subscript->index, COMPLETION_SUBSCRIPT, subscript);
+ }
+#endif
+
if (subscript->index == nullptr) {
push_error(R"(Expected expression after "[".)");
}
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 5eab8a6306..ddb0cf9502 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -134,38 +134,36 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT
return Variant();
}
-String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
- String err_text;
-
- if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
- int errorarg = p_err.argument;
- ERR_FAIL_COND_V_MSG(errorarg < 0 || argptrs[errorarg] == nullptr, "GDScript bug (please report): Invalid CallError argument index or null pointer.", "Invalid CallError argument index or null pointer.");
- // Handle the Object to Object case separately as we don't have further class details.
+String GDScriptFunction::_get_call_error(const String &p_where, const Variant **p_argptrs, const Variant &p_ret, const Callable::CallError &p_err) const {
+ switch (p_err.error) {
+ case Callable::CallError::CALL_OK:
+ return String();
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+ if (p_ret.get_type() == Variant::STRING && !p_ret.operator String().is_empty()) {
+ return "Invalid call " + p_where + ": " + p_ret.operator String();
+ }
+ return "Invalid call. Nonexistent " + p_where + ".";
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ ERR_FAIL_COND_V_MSG(p_err.argument < 0 || p_argptrs[p_err.argument] == nullptr, "Bug: Invalid CallError argument index or null pointer.", "Bug: Invalid CallError argument index or null pointer.");
+ // Handle the Object to Object case separately as we don't have further class details.
#ifdef DEBUG_ENABLED
- if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
- err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
- } else if (p_err.expected == Variant::ARRAY && argptrs[errorarg]->get_type() == p_err.expected) {
- err_text = "Invalid type in " + p_where + ". The array of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") does not have the same element type as the expected typed array argument.";
- } else
+ if (p_err.expected == Variant::OBJECT && p_argptrs[p_err.argument]->get_type() == p_err.expected) {
+ return "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(p_err.argument + 1) + " (" + _get_var_type(p_argptrs[p_err.argument]) + ") is not a subclass of the expected argument class.";
+ }
+ if (p_err.expected == Variant::ARRAY && p_argptrs[p_err.argument]->get_type() == p_err.expected) {
+ return "Invalid type in " + p_where + ". The array of argument " + itos(p_err.argument + 1) + " (" + _get_var_type(p_argptrs[p_err.argument]) + ") does not have the same element type as the expected typed array argument.";
+ }
#endif // DEBUG_ENABLED
- {
- err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
- }
- } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- err_text = "Invalid call. Nonexistent " + p_where + ".";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
- err_text = "Attempt to call " + p_where + " on a null instance.";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
- err_text = "Attempt to call " + p_where + " on a const instance.";
- } else {
- err_text = "Bug, call error: #" + itos(p_err.error);
+ return "Invalid type in " + p_where + ". Cannot convert argument " + itos(p_err.argument + 1) + " from " + Variant::get_type_name(p_argptrs[p_err.argument]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ return "Invalid call to " + p_where + ". Expected " + itos(p_err.expected) + " arguments.";
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ return "Attempt to call " + p_where + " on a null instance.";
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
+ return "Attempt to call " + p_where + " on a const instance.";
}
-
- return err_text;
+ return "Bug: Invalid call error code " + itos(p_err.error) + ".";
}
void (*type_init_function_table[])(Variant *) = {
@@ -1608,7 +1606,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (err.error != Callable::CallError::CALL_OK) {
- err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs);
+ err_text = _get_call_error("'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs, *dst, err);
OPCODE_BREAK;
}
#endif
@@ -1744,10 +1742,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
StringName base_class = base_obj ? base_obj->get_class_name() : StringName();
#endif
+ Variant temp_ret;
Callable::CallError err;
if (call_ret) {
GET_INSTRUCTION_ARG(ret, argc + 1);
- base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err);
+ base->callp(*methodname, (const Variant **)argptrs, argc, temp_ret, err);
+ *ret = temp_ret;
#ifdef DEBUG_ENABLED
if (ret->get_type() == Variant::NIL) {
if (base_type == Variant::OBJECT) {
@@ -1776,8 +1776,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
#endif
} else {
- Variant ret;
- base->callp(*methodname, (const Variant **)argptrs, argc, ret, err);
+ base->callp(*methodname, (const Variant **)argptrs, argc, temp_ret, err);
}
#ifdef DEBUG_ENABLED
@@ -1822,7 +1821,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
}
}
- err_text = _get_call_error(err, "function '" + methodstr + (is_callable ? "" : "' in base '" + basestr) + "'", (const Variant **)argptrs);
+ err_text = _get_call_error("function '" + methodstr + (is_callable ? "" : "' in base '" + basestr) + "'", (const Variant **)argptrs, temp_ret, err);
OPCODE_BREAK;
}
#endif
@@ -1868,12 +1867,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
#endif
+ Variant temp_ret;
Callable::CallError err;
if (call_ret) {
GET_INSTRUCTION_ARG(ret, argc + 1);
- *ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
+ temp_ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
+ *ret = temp_ret;
} else {
- method->call(base_obj, (const Variant **)argptrs, argc, err);
+ temp_ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
}
#ifdef DEBUG_ENABLED
@@ -1906,7 +1907,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
}
}
- err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
+ err_text = _get_call_error("function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs, temp_ret, err);
OPCODE_BREAK;
}
#endif
@@ -1939,7 +1940,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (err.error != Callable::CallError::CALL_OK) {
- err_text = _get_call_error(err, "static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs);
+ err_text = _get_call_error("static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs, *ret, err);
OPCODE_BREAK;
}
#endif
@@ -1983,7 +1984,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif
if (err.error != Callable::CallError::CALL_OK) {
- err_text = _get_call_error(err, "static function '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs);
+ err_text = _get_call_error("static function '" + method->get_name().operator String() + "' in type '" + method->get_instance_class().operator String() + "'", argptrs, *ret, err);
OPCODE_BREAK;
}
@@ -2214,7 +2215,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
// Call provided error string.
err_text = vformat(R"*(Error calling utility function "%s()": %s)*", methodstr, *dst);
} else {
- err_text = _get_call_error(err, vformat(R"*(utility function "%s()")*", methodstr), (const Variant **)argptrs);
+ err_text = _get_call_error(vformat(R"*(utility function "%s()")*", methodstr), (const Variant **)argptrs, *dst, err);
}
OPCODE_BREAK;
}
@@ -2271,7 +2272,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
// Call provided error string.
err_text = vformat(R"*(Error calling GDScript utility function "%s()": %s)*", methodstr, *dst);
} else {
- err_text = _get_call_error(err, vformat(R"*(GDScript utility function "%s()")*", methodstr), (const Variant **)argptrs);
+ err_text = _get_call_error(vformat(R"*(GDScript utility function "%s()")*", methodstr), (const Variant **)argptrs, *dst, err);
}
OPCODE_BREAK;
}
@@ -2338,7 +2339,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (err.error != Callable::CallError::CALL_OK) {
String methodstr = *methodname;
- err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs);
+ err_text = _get_call_error("function '" + methodstr + "'", (const Variant **)argptrs, *dst, err);
OPCODE_BREAK;
}
diff --git a/modules/gdscript/tests/scripts/completion/index/array_type.cfg b/modules/gdscript/tests/scripts/completion/index/array_type.cfg
new file mode 100644
index 0000000000..5cd5565d00
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/array_type.cfg
@@ -0,0 +1,9 @@
+[output]
+include=[
+ {"display": "outer"},
+ {"display": "inner"},
+]
+exclude=[
+ {"display": "append"},
+ {"display": "\"append\""},
+]
diff --git a/modules/gdscript/tests/scripts/completion/index/array_type.gd b/modules/gdscript/tests/scripts/completion/index/array_type.gd
new file mode 100644
index 0000000000..e0a15da556
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/array_type.gd
@@ -0,0 +1,10 @@
+extends Node
+
+var outer
+
+func _ready() -> void:
+ var inner
+
+ var array: Array
+
+ array[i➡]
diff --git a/modules/gdscript/tests/scripts/completion/index/array_value.cfg b/modules/gdscript/tests/scripts/completion/index/array_value.cfg
new file mode 100644
index 0000000000..5cd5565d00
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/array_value.cfg
@@ -0,0 +1,9 @@
+[output]
+include=[
+ {"display": "outer"},
+ {"display": "inner"},
+]
+exclude=[
+ {"display": "append"},
+ {"display": "\"append\""},
+]
diff --git a/modules/gdscript/tests/scripts/completion/index/array_value.gd b/modules/gdscript/tests/scripts/completion/index/array_value.gd
new file mode 100644
index 0000000000..17451725bc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/array_value.gd
@@ -0,0 +1,10 @@
+extends Node
+
+var outer
+
+func _ready() -> void:
+ var inner
+
+ var array = []
+
+ array[i➡]
diff --git a/modules/gdscript/tests/scripts/completion/index/const_dictionary_keys.cfg b/modules/gdscript/tests/scripts/completion/index/const_dictionary_keys.cfg
new file mode 100644
index 0000000000..ecea284b5d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/const_dictionary_keys.cfg
@@ -0,0 +1,11 @@
+[output]
+include=[
+ {"display": "\"key1\""},
+ {"display": "\"key2\""},
+]
+exclude=[
+ {"display": "keys"},
+ {"display": "\"keys\""},
+ {"display": "key1"},
+ {"display": "key2"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/index/const_dictionary_keys.gd b/modules/gdscript/tests/scripts/completion/index/const_dictionary_keys.gd
new file mode 100644
index 0000000000..06498c57a6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/const_dictionary_keys.gd
@@ -0,0 +1,13 @@
+extends Node
+
+var outer
+
+const dict = {
+ "key1": "value",
+ "key2": null,
+}
+
+func _ready() -> void:
+ var inner
+
+ dict["➡"]
diff --git a/modules/gdscript/tests/scripts/completion/index/dictionary_type.cfg b/modules/gdscript/tests/scripts/completion/index/dictionary_type.cfg
new file mode 100644
index 0000000000..cddf7b8cc8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/dictionary_type.cfg
@@ -0,0 +1,9 @@
+[output]
+include=[
+ {"display": "outer"},
+ {"display": "inner"},
+]
+exclude=[
+ {"display": "keys"},
+ {"display": "\"keys\""},
+]
diff --git a/modules/gdscript/tests/scripts/completion/index/dictionary_type.gd b/modules/gdscript/tests/scripts/completion/index/dictionary_type.gd
new file mode 100644
index 0000000000..b02c62eea5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/dictionary_type.gd
@@ -0,0 +1,10 @@
+extends Node
+
+var outer
+
+func _ready() -> void:
+ var inner
+
+ var dict: Dictionary
+
+ dict[i➡]
diff --git a/modules/gdscript/tests/scripts/completion/index/dictionary_value.cfg b/modules/gdscript/tests/scripts/completion/index/dictionary_value.cfg
new file mode 100644
index 0000000000..cddf7b8cc8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/dictionary_value.cfg
@@ -0,0 +1,9 @@
+[output]
+include=[
+ {"display": "outer"},
+ {"display": "inner"},
+]
+exclude=[
+ {"display": "keys"},
+ {"display": "\"keys\""},
+]
diff --git a/modules/gdscript/tests/scripts/completion/index/dictionary_value.gd b/modules/gdscript/tests/scripts/completion/index/dictionary_value.gd
new file mode 100644
index 0000000000..60bf391716
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/dictionary_value.gd
@@ -0,0 +1,10 @@
+extends Node
+
+var outer
+
+func _ready() -> void:
+ var inner
+
+ var dict = {}
+
+ dict[i➡]
diff --git a/modules/gdscript/tests/scripts/completion/index/local_dictionary_keys.cfg b/modules/gdscript/tests/scripts/completion/index/local_dictionary_keys.cfg
new file mode 100644
index 0000000000..ecea284b5d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/local_dictionary_keys.cfg
@@ -0,0 +1,11 @@
+[output]
+include=[
+ {"display": "\"key1\""},
+ {"display": "\"key2\""},
+]
+exclude=[
+ {"display": "keys"},
+ {"display": "\"keys\""},
+ {"display": "key1"},
+ {"display": "key2"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/index/local_dictionary_keys.gd b/modules/gdscript/tests/scripts/completion/index/local_dictionary_keys.gd
new file mode 100644
index 0000000000..2220cdcc59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/local_dictionary_keys.gd
@@ -0,0 +1,13 @@
+extends Node
+
+var outer
+
+func _ready() -> void:
+ var inner
+
+ var dict: Dictionary = {
+ "key1": "value",
+ "key2": null,
+ }
+
+ dict["➡"]
diff --git a/modules/gdscript/tests/scripts/completion/index/property_dictionary_keys.cfg b/modules/gdscript/tests/scripts/completion/index/property_dictionary_keys.cfg
new file mode 100644
index 0000000000..8da525bff8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/property_dictionary_keys.cfg
@@ -0,0 +1,9 @@
+[output]
+exclude=[
+ {"display": "keys"},
+ {"display": "\"keys\""},
+ {"display": "key1"},
+ {"display": "key2"},
+ {"display": "\"key1\""},
+ {"display": "\"key2\""},
+]
diff --git a/modules/gdscript/tests/scripts/completion/index/property_dictionary_keys.gd b/modules/gdscript/tests/scripts/completion/index/property_dictionary_keys.gd
new file mode 100644
index 0000000000..ba8d7f76fd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/property_dictionary_keys.gd
@@ -0,0 +1,13 @@
+extends Node
+
+var outer
+
+var dict = {
+ "key1": "value",
+ "key2": null,
+}
+
+func _ready() -> void:
+ var inner
+
+ dict["➡"]
diff --git a/modules/gdscript/tests/scripts/completion/index/untyped_local.cfg b/modules/gdscript/tests/scripts/completion/index/untyped_local.cfg
new file mode 100644
index 0000000000..1173043f94
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/untyped_local.cfg
@@ -0,0 +1,5 @@
+[output]
+include=[
+ {"display": "outer"},
+ {"display": "inner"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/index/untyped_local.gd b/modules/gdscript/tests/scripts/completion/index/untyped_local.gd
new file mode 100644
index 0000000000..1a1157af02
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/untyped_local.gd
@@ -0,0 +1,10 @@
+extends Node
+
+var outer
+
+func _ready() -> void:
+ var inner
+
+ var array
+
+ array[i➡]
diff --git a/modules/gdscript/tests/scripts/completion/index/untyped_property.cfg b/modules/gdscript/tests/scripts/completion/index/untyped_property.cfg
new file mode 100644
index 0000000000..1173043f94
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/untyped_property.cfg
@@ -0,0 +1,5 @@
+[output]
+include=[
+ {"display": "outer"},
+ {"display": "inner"},
+]
diff --git a/modules/gdscript/tests/scripts/completion/index/untyped_property.gd b/modules/gdscript/tests/scripts/completion/index/untyped_property.gd
new file mode 100644
index 0000000000..9fa23da504
--- /dev/null
+++ b/modules/gdscript/tests/scripts/completion/index/untyped_property.gd
@@ -0,0 +1,9 @@
+extends Node
+
+var outer
+var array
+
+func _ready() -> void:
+ var inner
+
+ array[i➡]
diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
index 676f764c11..64117349e0 100644
--- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
+++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
@@ -52,24 +52,24 @@ Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_sta
while (!queue.is_empty()) {
List<Node *>::Element *E = queue.front();
Node *node = E->get();
- ImporterMeshInstance3D *mesh_3d = cast_to<ImporterMeshInstance3D>(node);
- if (mesh_3d) {
- MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D);
- Ref<ImporterMesh> mesh = mesh_3d->get_mesh();
+ ImporterMeshInstance3D *importer_mesh_3d = Object::cast_to<ImporterMeshInstance3D>(node);
+ if (importer_mesh_3d) {
+ Ref<ImporterMesh> mesh = importer_mesh_3d->get_mesh();
if (mesh.is_valid()) {
+ MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D);
Ref<ArrayMesh> array_mesh = mesh->get_mesh();
mesh_instance_node_3d->set_name(node->get_name());
- mesh_instance_node_3d->set_transform(mesh_3d->get_transform());
+ mesh_instance_node_3d->set_transform(importer_mesh_3d->get_transform());
mesh_instance_node_3d->set_mesh(array_mesh);
- mesh_instance_node_3d->set_skin(mesh_3d->get_skin());
- mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
+ mesh_instance_node_3d->set_skin(importer_mesh_3d->get_skin());
+ mesh_instance_node_3d->set_skeleton_path(importer_mesh_3d->get_skeleton_path());
node->replace_by(mesh_instance_node_3d);
- _copy_meta(mesh_3d, mesh_instance_node_3d);
+ _copy_meta(importer_mesh_3d, mesh_instance_node_3d);
_copy_meta(mesh.ptr(), array_mesh.ptr());
delete_queue.push_back(node);
node = mesh_instance_node_3d;
} else {
- memdelete(mesh_instance_node_3d);
+ WARN_PRINT("glTF: ImporterMeshInstance3D does not have a valid mesh. This should not happen. Continuing anyway.");
}
}
int child_count = node->get_child_count();
diff --git a/modules/mbedtls/tls_context_mbedtls.cpp b/modules/mbedtls/tls_context_mbedtls.cpp
index eaea7b9293..f5c196596e 100644
--- a/modules/mbedtls/tls_context_mbedtls.cpp
+++ b/modules/mbedtls/tls_context_mbedtls.cpp
@@ -153,7 +153,7 @@ Error TLSContextMbedTLS::init_client(int p_transport, const String &p_hostname,
int authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
bool unsafe = p_options->is_unsafe_client();
- if (unsafe && p_options->get_trusted_ca_chain().is_valid()) {
+ if (unsafe && p_options->get_trusted_ca_chain().is_null()) {
authmode = MBEDTLS_SSL_VERIFY_NONE;
}
diff --git a/modules/openxr/doc_classes/OpenXRAPIExtension.xml b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
index 4419d24dd3..432b331eec 100644
--- a/modules/openxr/doc_classes/OpenXRAPIExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
@@ -17,12 +17,25 @@
<link title="XrPosef documentation">https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrPosef.html</link>
</tutorials>
<methods>
+ <method name="begin_debug_label_region">
+ <return type="void" />
+ <param index="0" name="label_name" type="String" />
+ <description>
+ Begins a new debug label region, this label will be reported in debug messages for any calls following this until [method end_debug_label_region] is called. Debug labels can be stacked.
+ </description>
+ </method>
<method name="can_render">
<return type="bool" />
<description>
Returns [code]true[/code] if OpenXR is initialized for rendering with an XR viewport.
</description>
</method>
+ <method name="end_debug_label_region">
+ <return type="void" />
+ <description>
+ Marks the end of a debug label region. Removes the latest debug label region added by calling [method begin_debug_label_region].
+ </description>
+ </method>
<method name="get_error_string">
<return type="String" />
<param index="0" name="result" type="int" />
@@ -88,6 +101,13 @@
Returns the id of the system, which is a [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSystemId.html]XrSystemId[/url] cast to an integer.
</description>
</method>
+ <method name="insert_debug_label">
+ <return type="void" />
+ <param index="0" name="label_name" type="String" />
+ <description>
+ Inserts a debug label, this label is reported in any debug message resulting from the OpenXR calls that follows, until any of [method begin_debug_label_region], [method end_debug_label_region], or [method insert_debug_label] is called.
+ </description>
+ </method>
<method name="is_environment_blend_mode_alpha_supported">
<return type="int" enum="OpenXRAPIExtension.OpenXRAlphaBlendModeSupport" />
<description>
@@ -127,6 +147,15 @@
If set to [code]true[/code], an OpenXR extension is loaded which is capable of emulating the [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] blend mode.
</description>
</method>
+ <method name="set_object_name">
+ <return type="void" />
+ <param index="0" name="object_type" type="int" />
+ <param index="1" name="object_handle" type="int" />
+ <param index="2" name="object_name" type="String" />
+ <description>
+ Set the object name of an OpenXR object, used for debug output. [param object_type] must be a valid OpenXR [code]XrObjectType[/code] enum and [param object_handle] must be a valid OpenXR object handle.
+ </description>
+ </method>
<method name="transform_from_pose">
<return type="Transform3D" />
<param index="0" name="pose" type="const void*" />
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
index 994b08af53..8a448afc08 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
@@ -58,7 +58,7 @@ HashMap<String, bool *> OpenXRCompositionLayerExtension::get_requested_extension
return request_extensions;
}
-void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_instance) {
+void OpenXRCompositionLayerExtension::on_session_created(const XrSession p_session) {
OpenXRAPI::get_singleton()->register_composition_layer_provider(this);
}
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.h b/modules/openxr/extensions/openxr_composition_layer_extension.h
index 4fefc416e6..34e330a60a 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.h
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.h
@@ -49,7 +49,7 @@ public:
virtual ~OpenXRCompositionLayerExtension() override;
virtual HashMap<String, bool *> get_requested_extensions() override;
- virtual void on_session_created(const XrSession p_instance) override;
+ virtual void on_session_created(const XrSession p_session) override;
virtual void on_session_destroyed() override;
virtual void on_pre_render() override;
diff --git a/modules/openxr/extensions/openxr_debug_utils_extension.cpp b/modules/openxr/extensions/openxr_debug_utils_extension.cpp
new file mode 100644
index 0000000000..10dbe629f7
--- /dev/null
+++ b/modules/openxr/extensions/openxr_debug_utils_extension.cpp
@@ -0,0 +1,287 @@
+/**************************************************************************/
+/* openxr_debug_utils_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "openxr_debug_utils_extension.h"
+
+#include "../openxr_api.h"
+#include "core/config/project_settings.h"
+#include "core/string/print_string.h"
+
+#include <openxr/openxr.h>
+
+OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::singleton = nullptr;
+
+OpenXRDebugUtilsExtension *OpenXRDebugUtilsExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRDebugUtilsExtension::OpenXRDebugUtilsExtension() {
+ singleton = this;
+}
+
+OpenXRDebugUtilsExtension::~OpenXRDebugUtilsExtension() {
+ singleton = nullptr;
+}
+
+HashMap<String, bool *> OpenXRDebugUtilsExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_EXT_DEBUG_UTILS_EXTENSION_NAME] = &debug_utils_ext;
+
+ return request_extensions;
+}
+
+void OpenXRDebugUtilsExtension::on_instance_created(const XrInstance p_instance) {
+ if (debug_utils_ext) {
+ EXT_INIT_XR_FUNC(xrCreateDebugUtilsMessengerEXT);
+ EXT_INIT_XR_FUNC(xrDestroyDebugUtilsMessengerEXT);
+ EXT_INIT_XR_FUNC(xrSetDebugUtilsObjectNameEXT);
+ EXT_INIT_XR_FUNC(xrSessionBeginDebugUtilsLabelRegionEXT);
+ EXT_INIT_XR_FUNC(xrSessionEndDebugUtilsLabelRegionEXT);
+ EXT_INIT_XR_FUNC(xrSessionInsertDebugUtilsLabelEXT);
+
+ debug_utils_ext = xrCreateDebugUtilsMessengerEXT_ptr && xrDestroyDebugUtilsMessengerEXT_ptr && xrSetDebugUtilsObjectNameEXT_ptr && xrSessionBeginDebugUtilsLabelRegionEXT_ptr && xrSessionEndDebugUtilsLabelRegionEXT_ptr && xrSessionInsertDebugUtilsLabelEXT_ptr;
+ } else {
+ WARN_PRINT("OpenXR: The debug utils extension is not available on this runtime. Debug logging is not enabled!");
+ }
+
+ // On successful init, setup our default messenger.
+ if (debug_utils_ext) {
+ int max_severity = GLOBAL_GET("xr/openxr/extensions/debug_utils");
+ int types = GLOBAL_GET("xr/openxr/extensions/debug_message_types");
+
+ XrDebugUtilsMessageSeverityFlagsEXT message_severities = 0;
+
+ if (max_severity >= 1) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
+ }
+ if (max_severity >= 2) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
+ }
+ if (max_severity >= 3) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
+ }
+ if (max_severity >= 4) {
+ message_severities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
+ }
+
+ XrDebugUtilsMessageTypeFlagsEXT message_types = 0;
+
+ // These should match up but just to be safe and future proof...
+ if (types & 1) {
+ message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
+ }
+ if (types & 2) {
+ message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
+ }
+ if (types & 4) {
+ message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
+ }
+ if (types & 8) {
+ message_types |= XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT;
+ }
+
+ XrDebugUtilsMessengerCreateInfoEXT callback_info = {
+ XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, // type
+ nullptr, // next
+ message_severities, // messageSeverities
+ message_types, // messageTypes
+ &OpenXRDebugUtilsExtension::_debug_callback, // userCallback
+ nullptr, // userData
+ };
+
+ XrResult result = xrCreateDebugUtilsMessengerEXT(p_instance, &callback_info, &default_messenger);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to create debug callback [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+
+ set_object_name(XR_OBJECT_TYPE_INSTANCE, uint64_t(p_instance), "Main Godot OpenXR Instance");
+ }
+}
+
+void OpenXRDebugUtilsExtension::on_instance_destroyed() {
+ if (default_messenger != XR_NULL_HANDLE) {
+ XrResult result = xrDestroyDebugUtilsMessengerEXT(default_messenger);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to destroy debug callback [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+
+ default_messenger = XR_NULL_HANDLE;
+ }
+
+ xrCreateDebugUtilsMessengerEXT_ptr = nullptr;
+ xrDestroyDebugUtilsMessengerEXT_ptr = nullptr;
+ xrSetDebugUtilsObjectNameEXT_ptr = nullptr;
+ xrSessionBeginDebugUtilsLabelRegionEXT_ptr = nullptr;
+ xrSessionEndDebugUtilsLabelRegionEXT_ptr = nullptr;
+ xrSessionInsertDebugUtilsLabelEXT_ptr = nullptr;
+ debug_utils_ext = false;
+}
+
+bool OpenXRDebugUtilsExtension::get_active() {
+ return debug_utils_ext;
+}
+
+void OpenXRDebugUtilsExtension::set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const char *p_object_name) {
+ ERR_FAIL_COND(!debug_utils_ext);
+ ERR_FAIL_NULL(xrSetDebugUtilsObjectNameEXT_ptr);
+
+ const XrDebugUtilsObjectNameInfoEXT space_name_info = {
+ XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, // type
+ nullptr, // next
+ p_object_type, // objectType
+ p_object_handle, // objectHandle
+ p_object_name, // objectName
+ };
+
+ XrResult result = xrSetDebugUtilsObjectNameEXT_ptr(OpenXRAPI::get_singleton()->get_instance(), &space_name_info);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to set object name [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+}
+
+void OpenXRDebugUtilsExtension::begin_debug_label_region(const char *p_label_name) {
+ ERR_FAIL_COND(!debug_utils_ext);
+ ERR_FAIL_NULL(xrSessionBeginDebugUtilsLabelRegionEXT_ptr);
+
+ const XrDebugUtilsLabelEXT session_active_region_label = {
+ XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
+ NULL, // next
+ p_label_name, // labelName
+ };
+
+ XrResult result = xrSessionBeginDebugUtilsLabelRegionEXT_ptr(OpenXRAPI::get_singleton()->get_session(), &session_active_region_label);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to begin label region [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+}
+
+void OpenXRDebugUtilsExtension::end_debug_label_region() {
+ ERR_FAIL_COND(!debug_utils_ext);
+ ERR_FAIL_NULL(xrSessionEndDebugUtilsLabelRegionEXT_ptr);
+
+ XrResult result = xrSessionEndDebugUtilsLabelRegionEXT_ptr(OpenXRAPI::get_singleton()->get_session());
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to end label region [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+}
+
+void OpenXRDebugUtilsExtension::insert_debug_label(const char *p_label_name) {
+ ERR_FAIL_COND(!debug_utils_ext);
+ ERR_FAIL_NULL(xrSessionInsertDebugUtilsLabelEXT_ptr);
+
+ const XrDebugUtilsLabelEXT session_active_region_label = {
+ XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
+ NULL, // next
+ p_label_name, // labelName
+ };
+
+ XrResult result = xrSessionInsertDebugUtilsLabelEXT_ptr(OpenXRAPI::get_singleton()->get_session(), &session_active_region_label);
+ if (XR_FAILED(result)) {
+ ERR_PRINT("OpenXR: Failed to insert label [" + OpenXRAPI::get_singleton()->get_error_string(result) + "]");
+ }
+}
+
+XrBool32 XRAPI_PTR OpenXRDebugUtilsExtension::_debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data) {
+ OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
+
+ if (debug_utils) {
+ return debug_utils->debug_callback(p_message_severity, p_message_types, p_callback_data, p_user_data);
+ }
+
+ return XR_FALSE;
+}
+
+XrBool32 OpenXRDebugUtilsExtension::debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data) {
+ String msg;
+
+ ERR_FAIL_NULL_V(p_callback_data, XR_FALSE);
+
+ if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) {
+ msg = ", type: General";
+ } else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) {
+ msg = ", type: Validation";
+ } else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
+ msg = ", type: Performance";
+ } else if (p_message_types == XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT) {
+ msg = ", type: Conformance";
+ } else {
+ msg = ", type: Unknown (" + String::num_uint64(p_message_types) + ")";
+ }
+
+ if (p_callback_data->functionName) {
+ msg += ", function Name: " + String(p_callback_data->functionName);
+ }
+ if (p_callback_data->messageId) {
+ msg += "\nMessage ID: " + String(p_callback_data->messageId);
+ }
+ if (p_callback_data->message) {
+ msg += "\nMessage: " + String(p_callback_data->message);
+ }
+
+ if (p_callback_data->objectCount > 0) {
+ String objects;
+
+ for (uint32_t i = 0; i < p_callback_data->objectCount; i++) {
+ if (!objects.is_empty()) {
+ objects += ", ";
+ }
+ objects += p_callback_data->objects[i].objectName;
+ }
+
+ msg += "\nObjects: " + objects;
+ }
+
+ if (p_callback_data->sessionLabelCount > 0) {
+ String labels;
+
+ for (uint32_t i = 0; i < p_callback_data->sessionLabelCount; i++) {
+ if (!labels.is_empty()) {
+ labels += ", ";
+ }
+ labels += p_callback_data->sessionLabels[i].labelName;
+ }
+
+ msg += "\nLabels: " + labels;
+ }
+
+ if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
+ ERR_PRINT("OpenXR: Severity: Error" + msg);
+ } else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
+ WARN_PRINT("OpenXR: Severity: Warning" + msg);
+ } else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
+ print_line("OpenXR: Severity: Info" + msg);
+ } else if (p_message_severity == XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
+ // This is a bit double because we won't output this unless verbose messaging in Godot is on.
+ print_verbose("OpenXR: Severity: Verbose" + msg);
+ }
+
+ return XR_FALSE;
+}
diff --git a/modules/openxr/extensions/openxr_debug_utils_extension.h b/modules/openxr/extensions/openxr_debug_utils_extension.h
new file mode 100644
index 0000000000..1ee4c2a101
--- /dev/null
+++ b/modules/openxr/extensions/openxr_debug_utils_extension.h
@@ -0,0 +1,76 @@
+/**************************************************************************/
+/* openxr_debug_utils_extension.h */
+/**************************************************************************/
+/* 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 OPENXR_DEBUG_UTILS_EXTENSION_H
+#define OPENXR_DEBUG_UTILS_EXTENSION_H
+
+#include "../util.h"
+#include "openxr_extension_wrapper.h"
+
+class OpenXRDebugUtilsExtension : public OpenXRExtensionWrapper {
+public:
+ static OpenXRDebugUtilsExtension *get_singleton();
+
+ OpenXRDebugUtilsExtension();
+ virtual ~OpenXRDebugUtilsExtension() override;
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+ virtual void on_instance_created(const XrInstance p_instance) override;
+ virtual void on_instance_destroyed() override;
+
+ bool get_active();
+
+ void set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const char *p_object_name);
+ void begin_debug_label_region(const char *p_label_name);
+ void end_debug_label_region();
+ void insert_debug_label(const char *p_label_name);
+
+private:
+ static OpenXRDebugUtilsExtension *singleton;
+
+ // related extensions
+ bool debug_utils_ext = false;
+
+ // debug handlers
+ XrDebugUtilsMessengerEXT default_messenger = XR_NULL_HANDLE;
+
+ static XrBool32 XRAPI_PTR _debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
+ XrBool32 debug_callback(XrDebugUtilsMessageSeverityFlagsEXT p_message_severity, XrDebugUtilsMessageTypeFlagsEXT p_message_types, const XrDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
+
+ // OpenXR API call wrappers
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateDebugUtilsMessengerEXT, (XrInstance), p_instance, (const XrDebugUtilsMessengerCreateInfoEXT *), p_create_info, (XrDebugUtilsMessengerEXT *), p_messenger)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyDebugUtilsMessengerEXT, (XrDebugUtilsMessengerEXT), p_messenger)
+ EXT_PROTO_XRRESULT_FUNC2(xrSetDebugUtilsObjectNameEXT, (XrInstance), p_instance, (const XrDebugUtilsObjectNameInfoEXT *), p_name_info)
+ EXT_PROTO_XRRESULT_FUNC2(xrSessionBeginDebugUtilsLabelRegionEXT, (XrSession), p_session, (const XrDebugUtilsLabelEXT *), p_label_info)
+ EXT_PROTO_XRRESULT_FUNC1(xrSessionEndDebugUtilsLabelRegionEXT, (XrSession), p_session)
+ EXT_PROTO_XRRESULT_FUNC2(xrSessionInsertDebugUtilsLabelEXT, (XrSession), p_session, (const XrDebugUtilsLabelEXT *), p_label_info)
+};
+
+#endif // OPENXR_DEBUG_UTILS_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index 8d05657afc..09a9556dfa 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -76,7 +76,7 @@ public:
virtual void on_before_instance_created() {} // `on_before_instance_created` is called before we create our OpenXR instance.
virtual void on_instance_created(const XrInstance p_instance) {} // `on_instance_created` is called right after we've successfully created our OpenXR instance.
virtual void on_instance_destroyed() {} // `on_instance_destroyed` is called right before we destroy our OpenXR instance.
- virtual void on_session_created(const XrSession p_instance) {} // `on_session_created` is called right after we've successfully created our OpenXR session.
+ virtual void on_session_created(const XrSession p_session) {} // `on_session_created` is called right after we've successfully created our OpenXR session.
virtual void on_session_destroyed() {} // `on_session_destroyed` is called right before we destroy our OpenXR session.
// `on_process` is called as part of our OpenXR process handling,
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index e4ec318a42..ecf7c05789 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -54,6 +54,7 @@
#endif
#include "extensions/openxr_composition_layer_depth_extension.h"
+#include "extensions/openxr_debug_utils_extension.h"
#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_fb_foveation_extension.h"
@@ -316,6 +317,46 @@ String OpenXRAPI::get_swapchain_format_name(int64_t p_swapchain_format) const {
return String("Swapchain format ") + String::num_int64(int64_t(p_swapchain_format));
}
+void OpenXRAPI::set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const String &p_object_name) {
+ OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
+ if (!debug_utils || !debug_utils->get_active()) {
+ // Not enabled/active? Ignore.
+ return;
+ }
+
+ debug_utils->set_object_name(p_object_type, p_object_handle, p_object_name.utf8().get_data());
+}
+
+void OpenXRAPI::begin_debug_label_region(const String &p_label_name) {
+ OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
+ if (!debug_utils || !debug_utils->get_active()) {
+ // Not enabled/active? Ignore.
+ return;
+ }
+
+ debug_utils->begin_debug_label_region(p_label_name.utf8().get_data());
+}
+
+void OpenXRAPI::end_debug_label_region() {
+ OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
+ if (!debug_utils || !debug_utils->get_active()) {
+ // Not enabled/active? Ignore.
+ return;
+ }
+
+ debug_utils->end_debug_label_region();
+}
+
+void OpenXRAPI::insert_debug_label(const String &p_label_name) {
+ OpenXRDebugUtilsExtension *debug_utils = OpenXRDebugUtilsExtension::get_singleton();
+ if (!debug_utils || !debug_utils->get_active()) {
+ // Not enabled/active? Ignore.
+ return;
+ }
+
+ debug_utils->insert_debug_label(p_label_name.utf8().get_data());
+}
+
bool OpenXRAPI::load_layer_properties() {
// This queries additional layers that are available and can be initialized when we create our OpenXR instance
if (layer_properties != nullptr) {
@@ -826,6 +867,10 @@ bool OpenXRAPI::create_session() {
return false;
}
+ set_object_name(XR_OBJECT_TYPE_SESSION, uint64_t(session), "Main Godot OpenXR Session");
+
+ begin_debug_label_region("Godot session active");
+
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_session_created(session);
}
@@ -916,6 +961,8 @@ bool OpenXRAPI::setup_play_space() {
print_line("OpenXR: Failed to create LOCAL space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
will_emulate_local_floor = false;
}
+
+ set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(local_floor_emulation.local_space), "Emulation local space");
}
if (local_floor_emulation.stage_space == XR_NULL_HANDLE) {
@@ -931,6 +978,8 @@ bool OpenXRAPI::setup_play_space() {
print_line("OpenXR: Failed to create STAGE space in order to emulate LOCAL_FLOOR [", get_error_string(result), "]");
will_emulate_local_floor = false;
}
+
+ set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(local_floor_emulation.stage_space), "Emulation stage space");
}
if (!will_emulate_local_floor) {
@@ -972,6 +1021,8 @@ bool OpenXRAPI::setup_play_space() {
play_space = new_play_space;
reference_space = new_reference_space;
+ set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(play_space), "Play space");
+
local_floor_emulation.enabled = will_emulate_local_floor;
local_floor_emulation.should_reset_floor_height = will_emulate_local_floor;
@@ -1007,6 +1058,8 @@ bool OpenXRAPI::setup_view_space() {
return false;
}
+ set_object_name(XR_OBJECT_TYPE_SPACE, uint64_t(view_space), "View space");
+
return true;
}
@@ -1181,6 +1234,8 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, color_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_count)) {
return false;
}
+
+ set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain()), "Main color swapchain");
}
// We create our depth swapchain if:
@@ -1191,6 +1246,8 @@ bool OpenXRAPI::create_main_swapchains(Size2i p_size) {
if (!render_state.main_swapchains[OPENXR_SWAPCHAIN_DEPTH].create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_swapchain_format, render_state.main_swapchain_size.width, render_state.main_swapchain_size.height, sample_count, view_count)) {
return false;
}
+
+ set_object_name(XR_OBJECT_TYPE_SWAPCHAIN, uint64_t(render_state.main_swapchains[OPENXR_SWAPCHAIN_COLOR].get_swapchain()), "Main depth swapchain");
}
// We create our velocity swapchain if:
@@ -1309,6 +1366,8 @@ void OpenXRAPI::destroy_session() {
wrapper->on_session_destroyed();
}
+ end_debug_label_region();
+
xrDestroySession(session);
session = XR_NULL_HANDLE;
}
@@ -2215,6 +2274,9 @@ void OpenXRAPI::pre_render() {
}
}
+ // We should get our frame no from the rendering server, but this will do.
+ begin_debug_label_region(String("Session Frame ") + String::num_uint64(++render_state.frame));
+
// let's start our frame..
XrFrameBeginInfo frame_begin_info = {
XR_TYPE_FRAME_BEGIN_INFO, // type
@@ -2333,6 +2395,8 @@ void OpenXRAPI::end_frame() {
return;
}
+ end_debug_label_region(); // Session frame #
+
// neither eye is rendered
return;
}
@@ -2407,6 +2471,8 @@ void OpenXRAPI::end_frame() {
print_line("OpenXR: failed to end frame! [", get_error_string(result), "]");
return;
}
+
+ end_debug_label_region(); // Session frame #
}
float OpenXRAPI::get_display_refresh_rate() const {
@@ -2822,6 +2888,8 @@ RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_n
return RID();
}
+ set_object_name(XR_OBJECT_TYPE_ACTION_SET, uint64_t(action_set.handle), p_name);
+
return action_set_owner.make_rid(action_set);
}
@@ -2997,6 +3065,8 @@ RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String
return RID();
}
+ set_object_name(XR_OBJECT_TYPE_ACTION, uint64_t(action.handle), p_name);
+
return action_owner.make_rid(action);
}
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 88455379b9..0d1e4eb414 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -336,6 +336,7 @@ private:
XrTime predicted_display_time = 0;
XrSpace play_space = XR_NULL_HANDLE;
double render_target_size_multiplier = 1.0;
+ uint64_t frame = 0;
uint32_t view_count = 0;
XrView *views = nullptr;
@@ -422,6 +423,10 @@ public:
XrResult get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
String get_error_string(XrResult result) const;
String get_swapchain_format_name(int64_t p_swapchain_format) const;
+ void set_object_name(XrObjectType p_object_type, uint64_t p_object_handle, const String &p_object_name);
+ void begin_debug_label_region(const String &p_label_name);
+ void end_debug_label_region();
+ void insert_debug_label(const String &p_label_name);
OpenXRInterface *get_xr_interface() const { return xr_interface; }
void set_xr_interface(OpenXRInterface *p_xr_interface);
diff --git a/modules/openxr/openxr_api_extension.cpp b/modules/openxr/openxr_api_extension.cpp
index a1744fa1db..f3bc178d3a 100644
--- a/modules/openxr/openxr_api_extension.cpp
+++ b/modules/openxr/openxr_api_extension.cpp
@@ -43,6 +43,10 @@ void OpenXRAPIExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_instance_proc_addr", "name"), &OpenXRAPIExtension::get_instance_proc_addr);
ClassDB::bind_method(D_METHOD("get_error_string", "result"), &OpenXRAPIExtension::get_error_string);
ClassDB::bind_method(D_METHOD("get_swapchain_format_name", "swapchain_format"), &OpenXRAPIExtension::get_swapchain_format_name);
+ ClassDB::bind_method(D_METHOD("set_object_name", "object_type", "object_handle", "object_name"), &OpenXRAPIExtension::set_object_name);
+ ClassDB::bind_method(D_METHOD("begin_debug_label_region", "label_name"), &OpenXRAPIExtension::begin_debug_label_region);
+ ClassDB::bind_method(D_METHOD("end_debug_label_region"), &OpenXRAPIExtension::end_debug_label_region);
+ ClassDB::bind_method(D_METHOD("insert_debug_label", "label_name"), &OpenXRAPIExtension::insert_debug_label);
ClassDB::bind_method(D_METHOD("is_initialized"), &OpenXRAPIExtension::is_initialized);
ClassDB::bind_method(D_METHOD("is_running"), &OpenXRAPIExtension::is_running);
@@ -116,6 +120,30 @@ String OpenXRAPIExtension::get_swapchain_format_name(int64_t p_swapchain_format)
return OpenXRAPI::get_singleton()->get_swapchain_format_name(p_swapchain_format);
}
+void OpenXRAPIExtension::set_object_name(int64_t p_object_type, uint64_t p_object_handle, const String &p_object_name) {
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+
+ OpenXRAPI::get_singleton()->set_object_name(XrObjectType(p_object_type), p_object_handle, p_object_name);
+}
+
+void OpenXRAPIExtension::begin_debug_label_region(const String &p_label_name) {
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+
+ OpenXRAPI::get_singleton()->begin_debug_label_region(p_label_name);
+}
+
+void OpenXRAPIExtension::end_debug_label_region() {
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+
+ OpenXRAPI::get_singleton()->end_debug_label_region();
+}
+
+void OpenXRAPIExtension::insert_debug_label(const String &p_label_name) {
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+
+ OpenXRAPI::get_singleton()->insert_debug_label(p_label_name);
+}
+
bool OpenXRAPIExtension::is_initialized() {
ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
return OpenXRAPI::get_singleton()->is_initialized();
diff --git a/modules/openxr/openxr_api_extension.h b/modules/openxr/openxr_api_extension.h
index cff2c4738e..1b88b418f6 100644
--- a/modules/openxr/openxr_api_extension.h
+++ b/modules/openxr/openxr_api_extension.h
@@ -64,6 +64,10 @@ public:
uint64_t get_instance_proc_addr(String p_name);
String get_error_string(uint64_t result);
String get_swapchain_format_name(int64_t p_swapchain_format);
+ void set_object_name(int64_t p_object_type, uint64_t p_object_handle, const String &p_object_name);
+ void begin_debug_label_region(const String &p_label_name);
+ void end_debug_label_region();
+ void insert_debug_label(const String &p_label_name);
bool is_initialized();
bool is_running();
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 61c294eff5..f3fda2517c 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -48,6 +48,7 @@
#include "extensions/openxr_composition_layer_depth_extension.h"
#include "extensions/openxr_composition_layer_extension.h"
+#include "extensions/openxr_debug_utils_extension.h"
#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_hand_interaction_extension.h"
@@ -133,6 +134,9 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRVisibilityMaskExtension));
// register gated extensions
+ if (int(GLOBAL_GET("xr/openxr/extensions/debug_utils")) > 0) {
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRDebugUtilsExtension));
+ }
if (GLOBAL_GET("xr/openxr/extensions/hand_tracking")) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHandTrackingExtension));
}
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 6fa3f2c9d6..270112e624 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -133,9 +133,17 @@ String DisplayServerWindows::get_name() const {
}
void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
+ if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
+ // Hide cursor before moving.
+ if (hCursor == nullptr) {
+ hCursor = SetCursor(nullptr);
+ } else {
+ SetCursor(nullptr);
+ }
+ }
+
if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) {
// Mouse is grabbed (captured or confined).
-
WindowID window_id = _get_focused_window_or_popup();
if (!windows.has(window_id)) {
window_id = MAIN_WINDOW_ID;
@@ -165,13 +173,8 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
_register_raw_input_devices(INVALID_WINDOW_ID);
}
- if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
- if (hCursor == nullptr) {
- hCursor = SetCursor(nullptr);
- } else {
- SetCursor(nullptr);
- }
- } else {
+ if (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED) {
+ // Show cursor.
CursorShape c = cursor_shape;
cursor_shape = CURSOR_MAX;
cursor_set_shape(c);
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 40b265785f..fb95c69b52 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -166,15 +166,9 @@ void OS_Windows::initialize_debugging() {
static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
String err_str;
if (p_errorexp && p_errorexp[0]) {
- err_str = String::utf8(p_errorexp);
+ err_str = String::utf8(p_errorexp) + "\n";
} else {
- err_str = String::utf8(p_file) + ":" + itos(p_line) + " - " + String::utf8(p_error);
- }
-
- if (p_editor_notify) {
- err_str += " (User)\n";
- } else {
- err_str += "\n";
+ err_str = String::utf8(p_file) + ":" + itos(p_line) + " - " + String::utf8(p_error) + "\n";
}
OutputDebugStringW((LPCWSTR)err_str.utf16().ptr());
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 412eb83515..e8be38e680 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -211,7 +211,13 @@ void CodeEdit::_notification(int p_what) {
tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
} else {
if (code_completion_options[l].default_value.get_type() == Variant::COLOR) {
- draw_rect(Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value);
+ const Color color = code_completion_options[l].default_value;
+ const Rect2 rect = Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size);
+ if (color.a < 1.0) {
+ draw_texture_rect(theme_cache.completion_color_bg, rect, true);
+ }
+
+ draw_rect(rect, color);
}
tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
}
@@ -2771,6 +2777,7 @@ void CodeEdit::_bind_methods() {
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, can_fold_code_region_icon, "can_fold_code_region");
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, folded_code_region_icon, "folded_code_region");
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CodeEdit, folded_eol_icon);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CodeEdit, completion_color_bg);
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, breakpoint_color);
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, breakpoint_icon, "breakpoint");
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index 580435f65e..09340be035 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -245,6 +245,7 @@ private:
Ref<Texture2D> can_fold_code_region_icon;
Ref<Texture2D> folded_code_region_icon;
Ref<Texture2D> folded_eol_icon;
+ Ref<Texture2D> completion_color_bg;
Color breakpoint_color = Color(1, 1, 1);
Ref<Texture2D> breakpoint_icon = Ref<Texture2D>();
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 2c08d36e7e..4212cd709f 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -36,7 +36,7 @@
Size2 SpinBox::get_minimum_size() const {
Size2 ms = line_edit->get_combined_minimum_size();
- ms.width += last_w;
+ ms.width += sizing_cache.buttons_block_width;
return ms;
}
@@ -128,7 +128,7 @@ void SpinBox::_range_click_timeout() {
}
}
-void SpinBox::_release_mouse() {
+void SpinBox::_release_mouse_from_drag_mode() {
if (drag.enabled) {
drag.enabled = false;
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_HIDDEN);
@@ -137,6 +137,14 @@ void SpinBox::_release_mouse() {
}
}
+void SpinBox::_mouse_exited() {
+ if (state_cache.up_button_hovered || state_cache.down_button_hovered) {
+ state_cache.up_button_hovered = false;
+ state_cache.down_button_hovered = false;
+ queue_redraw();
+ }
+}
+
void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
@@ -144,18 +152,36 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
return;
}
+ Ref<InputEventMouse> me = p_event;
Ref<InputEventMouseButton> mb = p_event;
+ Ref<InputEventMouseMotion> mm = p_event;
double step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
- if (mb.is_valid() && mb->is_pressed()) {
- bool up = mb->get_position().y < (get_size().height / 2);
+ Vector2 mpos;
+ bool mouse_on_up_button = false;
+ bool mouse_on_down_button = false;
+ if (mb.is_valid() || mm.is_valid()) {
+ Rect2 up_button_rc = Rect2(sizing_cache.buttons_left, 0, sizing_cache.buttons_width, sizing_cache.button_up_height);
+ Rect2 down_button_rc = Rect2(sizing_cache.buttons_left, sizing_cache.second_button_top, sizing_cache.buttons_width, sizing_cache.button_down_height);
+
+ mpos = me->get_position();
+ mouse_on_up_button = up_button_rc.has_point(mpos);
+ mouse_on_down_button = down_button_rc.has_point(mpos);
+ }
+
+ if (mb.is_valid() && mb->is_pressed()) {
switch (mb->get_button_index()) {
case MouseButton::LEFT: {
line_edit->grab_focus();
- set_value(get_value() + (up ? step : -step));
+ if (mouse_on_up_button || mouse_on_down_button) {
+ set_value(get_value() + (mouse_on_up_button ? step : -step));
+ }
+ state_cache.up_button_pressed = mouse_on_up_button;
+ state_cache.down_button_pressed = mouse_on_down_button;
+ queue_redraw();
range_click_timer->set_wait_time(0.6);
range_click_timer->set_one_shot(true);
@@ -166,7 +192,9 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
} break;
case MouseButton::RIGHT: {
line_edit->grab_focus();
- set_value((up ? get_max() : get_min()));
+ if (mouse_on_up_button || mouse_on_down_button) {
+ set_value(mouse_on_up_button ? get_max() : get_min());
+ }
} break;
case MouseButton::WHEEL_UP: {
if (line_edit->has_focus()) {
@@ -186,14 +214,30 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
}
if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
+ if (state_cache.up_button_pressed || state_cache.down_button_pressed) {
+ state_cache.up_button_pressed = false;
+ state_cache.down_button_pressed = false;
+ queue_redraw();
+ }
+
//set_default_cursor_shape(CURSOR_ARROW);
range_click_timer->stop();
- _release_mouse();
+ _release_mouse_from_drag_mode();
drag.allowed = false;
line_edit->clear_pending_select_all_on_focus();
}
- Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ bool old_up_hovered = state_cache.up_button_hovered;
+ bool old_down_hovered = state_cache.down_button_hovered;
+
+ state_cache.up_button_hovered = mouse_on_up_button;
+ state_cache.down_button_hovered = mouse_on_down_button;
+
+ if (old_up_hovered != state_cache.up_button_hovered || old_down_hovered != state_cache.down_button_hovered) {
+ queue_redraw();
+ }
+ }
if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
if (drag.enabled) {
@@ -239,41 +283,131 @@ void SpinBox::_line_edit_focus_exit() {
_text_submitted(line_edit->get_text());
}
-inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) {
- int w = icon->get_width();
- if ((w != last_w)) {
+inline void SpinBox::_compute_sizes() {
+ int buttons_block_wanted_width = theme_cache.buttons_width + theme_cache.field_and_buttons_separation;
+ int buttons_block_icon_enforced_width = _get_widest_button_icon_width() + theme_cache.field_and_buttons_separation;
+
+ int w = theme_cache.set_min_buttons_width_from_icons != 0 ? MAX(buttons_block_icon_enforced_width, buttons_block_wanted_width) : buttons_block_wanted_width;
+
+ if (w != sizing_cache.buttons_block_width) {
line_edit->set_offset(SIDE_LEFT, 0);
line_edit->set_offset(SIDE_RIGHT, -w);
- last_w = w;
+ sizing_cache.buttons_block_width = w;
}
+
+ Size2i size = get_size();
+
+ sizing_cache.buttons_width = w - theme_cache.field_and_buttons_separation;
+ sizing_cache.buttons_vertical_separation = CLAMP(theme_cache.buttons_vertical_separation, 0, size.height);
+ sizing_cache.buttons_left = is_layout_rtl() ? 0 : size.width - sizing_cache.buttons_width;
+ sizing_cache.button_up_height = (size.height - sizing_cache.buttons_vertical_separation) / 2;
+ sizing_cache.button_down_height = size.height - sizing_cache.button_up_height - sizing_cache.buttons_vertical_separation;
+ sizing_cache.second_button_top = size.height - sizing_cache.button_down_height;
+
+ sizing_cache.buttons_separator_top = sizing_cache.button_up_height;
+ sizing_cache.field_and_buttons_separator_left = is_layout_rtl() ? sizing_cache.buttons_width : size.width - sizing_cache.buttons_block_width;
+ sizing_cache.field_and_buttons_separator_width = theme_cache.field_and_buttons_separation;
+}
+
+inline int SpinBox::_get_widest_button_icon_width() {
+ int max = 0;
+ max = MAX(max, theme_cache.updown_icon->get_width());
+ max = MAX(max, theme_cache.up_icon->get_width());
+ max = MAX(max, theme_cache.up_hover_icon->get_width());
+ max = MAX(max, theme_cache.up_pressed_icon->get_width());
+ max = MAX(max, theme_cache.up_disabled_icon->get_width());
+ max = MAX(max, theme_cache.down_icon->get_width());
+ max = MAX(max, theme_cache.down_hover_icon->get_width());
+ max = MAX(max, theme_cache.down_pressed_icon->get_width());
+ max = MAX(max, theme_cache.down_disabled_icon->get_width());
+ return max;
}
void SpinBox::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_DRAW: {
_update_text(true);
- _adjust_width_for_icon(theme_cache.updown_icon);
+ _compute_sizes();
RID ci = get_canvas_item();
Size2i size = get_size();
- if (is_layout_rtl()) {
- theme_cache.updown_icon->draw(ci, Point2i(0, (size.height - theme_cache.updown_icon->get_height()) / 2));
- } else {
- theme_cache.updown_icon->draw(ci, Point2i(size.width - theme_cache.updown_icon->get_width(), (size.height - theme_cache.updown_icon->get_height()) / 2));
+ Ref<StyleBox> up_stylebox = theme_cache.up_base_stylebox;
+ Ref<StyleBox> down_stylebox = theme_cache.down_base_stylebox;
+ Ref<Texture2D> up_icon = theme_cache.up_icon;
+ Ref<Texture2D> down_icon = theme_cache.down_icon;
+ Color up_icon_modulate = theme_cache.up_icon_modulate;
+ Color down_icon_modulate = theme_cache.down_icon_modulate;
+
+ bool is_fully_disabled = !is_editable();
+
+ if (state_cache.up_button_disabled || is_fully_disabled) {
+ up_stylebox = theme_cache.up_disabled_stylebox;
+ up_icon = theme_cache.up_disabled_icon;
+ up_icon_modulate = theme_cache.up_disabled_icon_modulate;
+ } else if (state_cache.up_button_pressed && !drag.enabled) {
+ up_stylebox = theme_cache.up_pressed_stylebox;
+ up_icon = theme_cache.up_pressed_icon;
+ up_icon_modulate = theme_cache.up_pressed_icon_modulate;
+ } else if (state_cache.up_button_hovered && !drag.enabled) {
+ up_stylebox = theme_cache.up_hover_stylebox;
+ up_icon = theme_cache.up_hover_icon;
+ up_icon_modulate = theme_cache.up_hover_icon_modulate;
}
+
+ if (state_cache.down_button_disabled || is_fully_disabled) {
+ down_stylebox = theme_cache.down_disabled_stylebox;
+ down_icon = theme_cache.down_disabled_icon;
+ down_icon_modulate = theme_cache.down_disabled_icon_modulate;
+ } else if (state_cache.down_button_pressed && !drag.enabled) {
+ down_stylebox = theme_cache.down_pressed_stylebox;
+ down_icon = theme_cache.down_pressed_icon;
+ down_icon_modulate = theme_cache.down_pressed_icon_modulate;
+ } else if (state_cache.down_button_hovered && !drag.enabled) {
+ down_stylebox = theme_cache.down_hover_stylebox;
+ down_icon = theme_cache.down_hover_icon;
+ down_icon_modulate = theme_cache.down_hover_icon_modulate;
+ }
+
+ int updown_icon_left = sizing_cache.buttons_left + (sizing_cache.buttons_width - theme_cache.updown_icon->get_width()) / 2;
+ int updown_icon_top = (size.height - theme_cache.updown_icon->get_height()) / 2;
+
+ // Compute center icon positions once we know which one is used.
+ int up_icon_left = sizing_cache.buttons_left + (sizing_cache.buttons_width - up_icon->get_width()) / 2;
+ int up_icon_top = (sizing_cache.button_up_height - up_icon->get_height()) / 2;
+ int down_icon_left = sizing_cache.buttons_left + (sizing_cache.buttons_width - down_icon->get_width()) / 2;
+ int down_icon_top = sizing_cache.second_button_top + (sizing_cache.button_down_height - down_icon->get_height()) / 2;
+
+ // Draw separators.
+ draw_style_box(theme_cache.up_down_buttons_separator, Rect2(sizing_cache.buttons_left, sizing_cache.buttons_separator_top, sizing_cache.buttons_width, sizing_cache.buttons_vertical_separation));
+ draw_style_box(theme_cache.field_and_buttons_separator, Rect2(sizing_cache.field_and_buttons_separator_left, 0, sizing_cache.field_and_buttons_separator_width, size.height));
+
+ // Draw buttons.
+ draw_style_box(up_stylebox, Rect2(sizing_cache.buttons_left, 0, sizing_cache.buttons_width, sizing_cache.button_up_height));
+ draw_style_box(down_stylebox, Rect2(sizing_cache.buttons_left, sizing_cache.second_button_top, sizing_cache.buttons_width, sizing_cache.button_down_height));
+
+ // Draw arrows.
+ theme_cache.updown_icon->draw(ci, Point2i(updown_icon_left, updown_icon_top));
+ draw_texture(up_icon, Point2i(up_icon_left, up_icon_top), up_icon_modulate);
+ draw_texture(down_icon, Point2i(down_icon_left, down_icon_top), down_icon_modulate);
+
+ } break;
+
+ case NOTIFICATION_MOUSE_EXIT: {
+ _mouse_exited();
} break;
case NOTIFICATION_ENTER_TREE: {
- _adjust_width_for_icon(theme_cache.updown_icon);
+ _compute_sizes();
_update_text();
+ _update_buttons_state_for_current_value();
} break;
case NOTIFICATION_VISIBILITY_CHANGED:
drag.allowed = false;
[[fallthrough]];
case NOTIFICATION_EXIT_TREE: {
- _release_mouse();
+ _release_mouse_from_drag_mode();
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
@@ -353,6 +487,7 @@ bool SpinBox::is_select_all_on_focus() const {
void SpinBox::set_editable(bool p_enabled) {
line_edit->set_editable(p_enabled);
+ queue_redraw();
}
bool SpinBox::is_editable() const {
@@ -371,6 +506,22 @@ double SpinBox::get_custom_arrow_step() const {
return custom_arrow_step;
}
+void SpinBox::_value_changed(double p_value) {
+ _update_buttons_state_for_current_value();
+}
+
+void SpinBox::_update_buttons_state_for_current_value() {
+ double value = get_value();
+ bool should_disable_up = value == get_max() && !is_greater_allowed();
+ bool should_disable_down = value == get_min() && !is_lesser_allowed();
+
+ if (state_cache.up_button_disabled != should_disable_up || state_cache.down_button_disabled != should_disable_down) {
+ state_cache.up_button_disabled = should_disable_up;
+ state_cache.down_button_disabled = should_disable_down;
+ queue_redraw();
+ }
+}
+
void SpinBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &SpinBox::set_horizontal_alignment);
ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &SpinBox::get_horizontal_alignment);
@@ -397,13 +548,48 @@ void SpinBox::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_arrow_step", PROPERTY_HINT_RANGE, "0,10000,0.0001,or_greater"), "set_custom_arrow_step", "get_custom_arrow_step");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus");
+ BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, buttons_vertical_separation);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, field_and_buttons_separation);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, buttons_width);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, set_min_buttons_width_from_icons);
+
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, updown_icon, "updown");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, up_icon, "up");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, up_hover_icon, "up_hover");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, up_pressed_icon, "up_pressed");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, up_disabled_icon, "up_disabled");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, down_icon, "down");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, down_hover_icon, "down_hover");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, down_pressed_icon, "down_pressed");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, down_disabled_icon, "down_disabled");
+
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_base_stylebox, "up_background");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_hover_stylebox, "up_background_hovered");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_pressed_stylebox, "up_background_pressed");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_disabled_stylebox, "up_background_disabled");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, down_base_stylebox, "down_background");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, down_hover_stylebox, "down_background_hovered");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, down_pressed_stylebox, "down_background_pressed");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, down_disabled_stylebox, "down_background_disabled");
+
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, up_icon_modulate, "up_icon_modulate");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, up_hover_icon_modulate, "up_hover_icon_modulate");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, up_pressed_icon_modulate, "up_pressed_icon_modulate");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, up_disabled_icon_modulate, "up_disabled_icon_modulate");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, down_icon_modulate, "down_icon_modulate");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, down_hover_icon_modulate, "down_hover_icon_modulate");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, down_pressed_icon_modulate, "down_pressed_icon_modulate");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, down_disabled_icon_modulate, "down_disabled_icon_modulate");
+
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, field_and_buttons_separator, "field_and_buttons_separator");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_down_buttons_separator, "up_down_buttons_separator");
}
SpinBox::SpinBox() {
line_edit = memnew(LineEdit);
add_child(line_edit, false, INTERNAL_MODE_FRONT);
+ line_edit->set_theme_type_variation("SpinBoxInnerLineEdit");
line_edit->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
line_edit->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 4d49626d71..7c6974f6a8 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -39,12 +39,24 @@ class SpinBox : public Range {
GDCLASS(SpinBox, Range);
LineEdit *line_edit = nullptr;
- int last_w = 0;
bool update_on_text_changed = false;
+ struct SizingCache {
+ int buttons_block_width = 0;
+ int buttons_width = 0;
+ int buttons_vertical_separation = 0;
+ int buttons_left = 0;
+ int button_up_height = 0;
+ int button_down_height = 0;
+ int second_button_top = 0;
+ int buttons_separator_top = 0;
+ int field_and_buttons_separator_left = 0;
+ int field_and_buttons_separator_width = 0;
+ } sizing_cache;
+
Timer *range_click_timer = nullptr;
void _range_click_timeout();
- void _release_mouse();
+ void _release_mouse_from_drag_mode();
void _update_text(bool p_keep_line_edit = false);
void _text_submitted(const String &p_string);
@@ -65,17 +77,66 @@ class SpinBox : public Range {
double diff_y = 0.0;
} drag;
+ struct StateCache {
+ bool up_button_hovered = false;
+ bool up_button_pressed = false;
+ bool up_button_disabled = false;
+ bool down_button_hovered = false;
+ bool down_button_pressed = false;
+ bool down_button_disabled = false;
+ } state_cache;
+
void _line_edit_focus_enter();
void _line_edit_focus_exit();
- inline void _adjust_width_for_icon(const Ref<Texture2D> &icon);
+ inline void _compute_sizes();
+ inline int _get_widest_button_icon_width();
struct ThemeCache {
Ref<Texture2D> updown_icon;
+ Ref<Texture2D> up_icon;
+ Ref<Texture2D> up_hover_icon;
+ Ref<Texture2D> up_pressed_icon;
+ Ref<Texture2D> up_disabled_icon;
+ Ref<Texture2D> down_icon;
+ Ref<Texture2D> down_hover_icon;
+ Ref<Texture2D> down_pressed_icon;
+ Ref<Texture2D> down_disabled_icon;
+
+ Ref<StyleBox> up_base_stylebox;
+ Ref<StyleBox> up_hover_stylebox;
+ Ref<StyleBox> up_pressed_stylebox;
+ Ref<StyleBox> up_disabled_stylebox;
+ Ref<StyleBox> down_base_stylebox;
+ Ref<StyleBox> down_hover_stylebox;
+ Ref<StyleBox> down_pressed_stylebox;
+ Ref<StyleBox> down_disabled_stylebox;
+
+ Color up_icon_modulate;
+ Color up_hover_icon_modulate;
+ Color up_pressed_icon_modulate;
+ Color up_disabled_icon_modulate;
+ Color down_icon_modulate;
+ Color down_hover_icon_modulate;
+ Color down_pressed_icon_modulate;
+ Color down_disabled_icon_modulate;
+
+ Ref<StyleBox> field_and_buttons_separator;
+ Ref<StyleBox> up_down_buttons_separator;
+
+ int buttons_vertical_separation = 0;
+ int field_and_buttons_separation = 0;
+ int buttons_width = 0;
+ int set_min_buttons_width_from_icons = 0;
+
} theme_cache;
+ void _mouse_exited();
+ void _update_buttons_state_for_current_value();
+
protected:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
+ void _value_changed(double p_value) override;
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 422783b01b..b6835541bf 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -2928,7 +2928,10 @@ void TextEdit::_update_ime_text() {
Size2 TextEdit::get_minimum_size() const {
Size2 size = theme_cache.style_normal->get_minimum_size();
if (fit_content_height) {
- size.y += content_height_cache;
+ size.y += content_size_cache.y;
+ }
+ if (fit_content_width) {
+ size.x += content_size_cache.x;
}
return size;
}
@@ -3098,7 +3101,7 @@ void TextEdit::apply_ime() {
insert_text_at_caret(insert_ime_text);
}
-void TextEdit::set_editable(const bool p_editable) {
+void TextEdit::set_editable(bool p_editable) {
if (editable == p_editable) {
return;
}
@@ -3223,7 +3226,7 @@ bool TextEdit::is_indent_wrapped_lines() const {
}
// User controls
-void TextEdit::set_overtype_mode_enabled(const bool p_enabled) {
+void TextEdit::set_overtype_mode_enabled(bool p_enabled) {
if (overtype_mode == p_enabled) {
return;
}
@@ -4473,7 +4476,7 @@ TextEdit::CaretType TextEdit::get_caret_type() const {
return caret_type;
}
-void TextEdit::set_caret_blink_enabled(const bool p_enabled) {
+void TextEdit::set_caret_blink_enabled(bool p_enabled) {
if (caret_blink_enabled == p_enabled) {
return;
}
@@ -4515,7 +4518,7 @@ bool TextEdit::is_drawing_caret_when_editable_disabled() const {
return draw_caret_when_editable_disabled;
}
-void TextEdit::set_move_caret_on_right_click_enabled(const bool p_enabled) {
+void TextEdit::set_move_caret_on_right_click_enabled(bool p_enabled) {
move_caret_on_right_click = p_enabled;
}
@@ -4523,7 +4526,7 @@ bool TextEdit::is_move_caret_on_right_click_enabled() const {
return move_caret_on_right_click;
}
-void TextEdit::set_caret_mid_grapheme_enabled(const bool p_enabled) {
+void TextEdit::set_caret_mid_grapheme_enabled(bool p_enabled) {
caret_mid_grapheme_enabled = p_enabled;
}
@@ -4633,7 +4636,7 @@ void TextEdit::add_caret_at_carets(bool p_below) {
for (int i = 0; i < num_carets; i++) {
const int caret_line = get_caret_line(i);
const int caret_column = get_caret_column(i);
- const bool is_selected = has_selection(i) || carets[i].last_fit_x != carets[i].selection.origin_last_fit_x;
+ bool is_selected = has_selection(i) || carets[i].last_fit_x != carets[i].selection.origin_last_fit_x;
const int selection_origin_line = get_selection_origin_line(i);
const int selection_origin_column = get_selection_origin_column(i);
const int caret_wrap_index = get_caret_wrap_index(i);
@@ -5098,7 +5101,7 @@ String TextEdit::get_word_under_caret(int p_caret) const {
}
/* Selection. */
-void TextEdit::set_selecting_enabled(const bool p_enabled) {
+void TextEdit::set_selecting_enabled(bool p_enabled) {
if (selecting_enabled == p_enabled) {
return;
}
@@ -5114,7 +5117,7 @@ bool TextEdit::is_selecting_enabled() const {
return selecting_enabled;
}
-void TextEdit::set_deselect_on_focus_loss_enabled(const bool p_enabled) {
+void TextEdit::set_deselect_on_focus_loss_enabled(bool p_enabled) {
if (deselect_on_focus_loss_enabled == p_enabled) {
return;
}
@@ -5129,7 +5132,7 @@ bool TextEdit::is_deselect_on_focus_loss_enabled() const {
return deselect_on_focus_loss_enabled;
}
-void TextEdit::set_drag_and_drop_selection_enabled(const bool p_enabled) {
+void TextEdit::set_drag_and_drop_selection_enabled(bool p_enabled) {
drag_and_drop_selection_enabled = p_enabled;
}
@@ -5689,7 +5692,7 @@ Vector<String> TextEdit::get_line_wrapped_text(int p_line) const {
/* Viewport */
// Scrolling.
-void TextEdit::set_smooth_scroll_enabled(const bool p_enabled) {
+void TextEdit::set_smooth_scroll_enabled(bool p_enabled) {
v_scroll->set_smooth_scroll_enabled(p_enabled);
smooth_scroll_enabled = p_enabled;
}
@@ -5698,7 +5701,7 @@ bool TextEdit::is_smooth_scroll_enabled() const {
return smooth_scroll_enabled;
}
-void TextEdit::set_scroll_past_end_of_file_enabled(const bool p_enabled) {
+void TextEdit::set_scroll_past_end_of_file_enabled(bool p_enabled) {
if (scroll_past_end_of_file_enabled == p_enabled) {
return;
}
@@ -5752,7 +5755,7 @@ float TextEdit::get_v_scroll_speed() const {
return v_scroll_speed;
}
-void TextEdit::set_fit_content_height_enabled(const bool p_enabled) {
+void TextEdit::set_fit_content_height_enabled(bool p_enabled) {
if (fit_content_height == p_enabled) {
return;
}
@@ -5764,6 +5767,18 @@ bool TextEdit::is_fit_content_height_enabled() const {
return fit_content_height;
}
+void TextEdit::set_fit_content_width_enabled(bool p_enabled) {
+ if (fit_content_width == p_enabled) {
+ return;
+ }
+ fit_content_width = p_enabled;
+ update_minimum_size();
+}
+
+bool TextEdit::is_fit_content_width_enabled() const {
+ return fit_content_width;
+}
+
double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
ERR_FAIL_COND_V(p_wrap_index < 0, 0);
@@ -6317,7 +6332,7 @@ bool TextEdit::is_highlight_current_line_enabled() const {
return highlight_current_line;
}
-void TextEdit::set_highlight_all_occurrences(const bool p_enabled) {
+void TextEdit::set_highlight_all_occurrences(bool p_enabled) {
if (highlight_all_occurrences == p_enabled) {
return;
}
@@ -6735,6 +6750,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fit_content_height_enabled", "enabled"), &TextEdit::set_fit_content_height_enabled);
ClassDB::bind_method(D_METHOD("is_fit_content_height_enabled"), &TextEdit::is_fit_content_height_enabled);
+ ClassDB::bind_method(D_METHOD("set_fit_content_width_enabled", "enabled"), &TextEdit::set_fit_content_width_enabled);
+ ClassDB::bind_method(D_METHOD("is_fit_content_width_enabled"), &TextEdit::is_fit_content_width_enabled);
+
ClassDB::bind_method(D_METHOD("get_scroll_pos_for_line", "line", "wrap_index"), &TextEdit::get_scroll_pos_for_line, DEFVAL(0));
// Visible lines.
@@ -6859,6 +6877,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical", PROPERTY_HINT_NONE, "suffix:lines"), "set_v_scroll", "get_v_scroll");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal", PROPERTY_HINT_NONE, "suffix:px"), "set_h_scroll", "get_h_scroll");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_fit_content_height"), "set_fit_content_height_enabled", "is_fit_content_height_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_fit_content_width"), "set_fit_content_width_enabled", "is_fit_content_width_enabled");
ADD_GROUP("Minimap", "minimap_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "set_draw_minimap", "is_drawing_minimap");
@@ -7846,8 +7865,8 @@ void TextEdit::_update_scrollbars() {
total_width += minimap_width;
}
- content_height_cache = MAX(total_rows, 1) * get_line_height();
- if (fit_content_height) {
+ content_size_cache = Vector2i(total_width + 10, MAX(total_rows, 1) * get_line_height());
+ if (fit_content_height || fit_content_width) {
update_minimum_size();
}
@@ -8054,7 +8073,7 @@ void TextEdit::_update_minimap_hover() {
const Point2 mp = get_local_mouse_pos();
const int xmargin_end = get_size().width - theme_cache.style_normal->get_margin(SIDE_RIGHT);
- const bool hovering_sidebar = mp.x > xmargin_end - minimap_width && mp.x < xmargin_end;
+ bool hovering_sidebar = mp.x > xmargin_end - minimap_width && mp.x < xmargin_end;
if (!hovering_sidebar) {
if (hovering_minimap) {
// Only redraw if the hovering status changed.
@@ -8068,7 +8087,7 @@ void TextEdit::_update_minimap_hover() {
const int row = get_minimap_line_at_pos(mp);
- const bool new_hovering_minimap = row >= get_first_visible_line() && row <= get_last_full_visible_line();
+ bool new_hovering_minimap = row >= get_first_visible_line() && row <= get_last_full_visible_line();
if (new_hovering_minimap != hovering_minimap) {
// Only redraw if the hovering status changed.
hovering_minimap = new_hovering_minimap;
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index c8cd7b0e4d..1f2fd6619a 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -505,8 +505,9 @@ private:
HScrollBar *h_scroll = nullptr;
VScrollBar *v_scroll = nullptr;
- float content_height_cache = 0.0;
+ Vector2i content_size_cache;
bool fit_content_height = false;
+ bool fit_content_width = false;
bool scroll_past_end_of_file_enabled = false;
// Smooth scrolling.
@@ -734,7 +735,7 @@ public:
void cancel_ime();
void apply_ime();
- void set_editable(const bool p_editable);
+ void set_editable(bool p_editable);
bool is_editable() const;
void set_text_direction(TextDirection p_text_direction);
@@ -755,7 +756,7 @@ public:
bool is_indent_wrapped_lines() const;
// User controls
- void set_overtype_mode_enabled(const bool p_enabled);
+ void set_overtype_mode_enabled(bool p_enabled);
bool is_overtype_mode_enabled() const;
void set_context_menu_enabled(bool p_enabled);
@@ -862,7 +863,7 @@ public:
void set_caret_type(CaretType p_type);
CaretType get_caret_type() const;
- void set_caret_blink_enabled(const bool p_enabled);
+ void set_caret_blink_enabled(bool p_enabled);
bool is_caret_blink_enabled() const;
void set_caret_blink_interval(const float p_interval);
@@ -871,10 +872,10 @@ public:
void set_draw_caret_when_editable_disabled(bool p_enable);
bool is_drawing_caret_when_editable_disabled() const;
- void set_move_caret_on_right_click_enabled(const bool p_enabled);
+ void set_move_caret_on_right_click_enabled(bool p_enabled);
bool is_move_caret_on_right_click_enabled() const;
- void set_caret_mid_grapheme_enabled(const bool p_enabled);
+ void set_caret_mid_grapheme_enabled(bool p_enabled);
bool is_caret_mid_grapheme_enabled() const;
void set_multiple_carets_enabled(bool p_enabled);
@@ -910,13 +911,13 @@ public:
String get_word_under_caret(int p_caret = -1) const;
/* Selection. */
- void set_selecting_enabled(const bool p_enabled);
+ void set_selecting_enabled(bool p_enabled);
bool is_selecting_enabled() const;
- void set_deselect_on_focus_loss_enabled(const bool p_enabled);
+ void set_deselect_on_focus_loss_enabled(bool p_enabled);
bool is_deselect_on_focus_loss_enabled() const;
- void set_drag_and_drop_selection_enabled(const bool p_enabled);
+ void set_drag_and_drop_selection_enabled(bool p_enabled);
bool is_drag_and_drop_selection_enabled() const;
void set_selection_mode(SelectionMode p_mode);
@@ -965,10 +966,10 @@ public:
/* Viewport. */
// Scrolling.
- void set_smooth_scroll_enabled(const bool p_enabled);
+ void set_smooth_scroll_enabled(bool p_enabled);
bool is_smooth_scroll_enabled() const;
- void set_scroll_past_end_of_file_enabled(const bool p_enabled);
+ void set_scroll_past_end_of_file_enabled(bool p_enabled);
bool is_scroll_past_end_of_file_enabled() const;
VScrollBar *get_v_scroll_bar() const;
@@ -983,9 +984,12 @@ public:
void set_v_scroll_speed(float p_speed);
float get_v_scroll_speed() const;
- void set_fit_content_height_enabled(const bool p_enabled);
+ void set_fit_content_height_enabled(bool p_enabled);
bool is_fit_content_height_enabled() const;
+ void set_fit_content_width_enabled(bool p_enabled);
+ bool is_fit_content_width_enabled() const;
+
double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const;
// Visible lines.
@@ -1071,7 +1075,7 @@ public:
void set_highlight_current_line(bool p_enabled);
bool is_highlight_current_line_enabled() const;
- void set_highlight_all_occurrences(const bool p_enabled);
+ void set_highlight_all_occurrences(bool p_enabled);
bool is_highlight_all_occurrences_enabled() const;
void set_draw_control_chars(bool p_enabled);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 46fcdcf7f6..5830bea258 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -773,17 +773,21 @@ TreeItem *TreeItem::create_child(int p_index) {
TreeItem *item_prev = nullptr;
TreeItem *item_next = first_child;
- int idx = 0;
- while (item_next) {
- if (idx == p_index) {
- item_next->prev = ti;
- ti->next = item_next;
- break;
- }
+ if (p_index < 0 && last_child) {
+ item_prev = last_child;
+ } else {
+ int idx = 0;
+ while (item_next) {
+ if (idx == p_index) {
+ item_next->prev = ti;
+ ti->next = item_next;
+ break;
+ }
- item_prev = item_next;
- item_next = item_next->next;
- idx++;
+ item_prev = item_next;
+ item_next = item_next->next;
+ idx++;
+ }
}
if (item_prev) {
@@ -804,6 +808,10 @@ TreeItem *TreeItem::create_child(int p_index) {
}
}
+ if (item_prev == last_child) {
+ last_child = ti;
+ }
+
ti->parent = this;
ti->parent_visible_in_tree = is_visible_in_tree();
@@ -820,17 +828,13 @@ void TreeItem::add_child(TreeItem *p_item) {
p_item->parent_visible_in_tree = is_visible_in_tree();
p_item->_handle_visibility_changed(p_item->parent_visible_in_tree);
- TreeItem *item_prev = first_child;
- while (item_prev && item_prev->next) {
- item_prev = item_prev->next;
- }
-
- if (item_prev) {
- item_prev->next = p_item;
- p_item->prev = item_prev;
+ if (last_child) {
+ last_child->next = p_item;
+ p_item->prev = last_child;
} else {
first_child = p_item;
}
+ last_child = p_item;
if (!children_cache.is_empty()) {
children_cache.append(p_item);
@@ -910,13 +914,8 @@ TreeItem *TreeItem::_get_prev_in_tree(bool p_wrap, bool p_include_invisible) {
}
} else {
current = prev_item;
- while ((!current->collapsed || p_include_invisible) && current->first_child) {
- //go to the very end
-
- current = current->first_child;
- while (current->next) {
- current = current->next;
- }
+ while ((!current->collapsed || p_include_invisible) && current->last_child) {
+ current = current->last_child;
}
}
@@ -1037,6 +1036,8 @@ void TreeItem::clear_children() {
}
first_child = nullptr;
+ last_child = nullptr;
+ children_cache.clear();
};
int TreeItem::get_index() {
@@ -1141,6 +1142,7 @@ void TreeItem::move_after(TreeItem *p_item) {
if (next) {
parent->children_cache.clear();
} else {
+ parent->last_child = this;
// If the cache is empty, it has not been built but there
// are items in the tree (note p_item != nullptr,) so we cannot update it.
if (!parent->children_cache.is_empty()) {
@@ -4468,15 +4470,8 @@ TreeItem *Tree::get_root() const {
TreeItem *Tree::get_last_item() const {
TreeItem *last = root;
-
- while (last) {
- if (last->next) {
- last = last->next;
- } else if (last->first_child && !last->collapsed) {
- last = last->first_child;
- } else {
- break;
- }
+ while (last && last->last_child && !last->collapsed) {
+ last = last->last_child;
}
return last;
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 3200459b5a..9b1541f4b9 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -136,6 +136,7 @@ private:
TreeItem *prev = nullptr; // previous in list
TreeItem *next = nullptr; // next in list
TreeItem *first_child = nullptr;
+ TreeItem *last_child = nullptr;
Vector<TreeItem *> children_cache;
bool is_root = false; // for tree root
diff --git a/scene/resources/shader.compat.inc b/scene/resources/shader.compat.inc
new file mode 100644
index 0000000000..b68020605f
--- /dev/null
+++ b/scene/resources/shader.compat.inc
@@ -0,0 +1,46 @@
+/**************************************************************************/
+/* shader.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 Shader::_set_default_texture_parameter_bind_compat_95126(const StringName &p_name, const Ref<Texture2D> &p_texture, int p_index) {
+ set_default_texture_parameter(p_name, p_texture, p_index);
+}
+
+Ref<Texture2D> Shader::_get_default_texture_parameter_bind_compat_95126(const StringName &p_name, int p_index) const {
+ return get_default_texture_parameter(p_name, p_index);
+}
+
+void Shader::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("set_default_texture_parameter", "name", "texture", "index"), &Shader::_set_default_texture_parameter_bind_compat_95126, DEFVAL(0));
+ ClassDB::bind_compatibility_method(D_METHOD("get_default_texture_parameter", "name", "index"), &Shader::_get_default_texture_parameter_bind_compat_95126, DEFVAL(0));
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index dfe5bd4a47..f343229cd8 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "shader.h"
+#include "shader.compat.inc"
#include "core/io/file_access.h"
#include "servers/rendering/shader_language.h"
@@ -185,10 +186,10 @@ RID Shader::get_rid() const {
return shader;
}
-void Shader::set_default_texture_parameter(const StringName &p_name, const Ref<Texture2D> &p_texture, int p_index) {
+void Shader::set_default_texture_parameter(const StringName &p_name, const Ref<Texture> &p_texture, int p_index) {
if (p_texture.is_valid()) {
if (!default_textures.has(p_name)) {
- default_textures[p_name] = HashMap<int, Ref<Texture2D>>();
+ default_textures[p_name] = HashMap<int, Ref<Texture>>();
}
default_textures[p_name][p_index] = p_texture;
RS::get_singleton()->shader_set_default_texture_parameter(shader, p_name, p_texture->get_rid(), p_index);
@@ -206,7 +207,7 @@ void Shader::set_default_texture_parameter(const StringName &p_name, const Ref<T
emit_changed();
}
-Ref<Texture2D> Shader::get_default_texture_parameter(const StringName &p_name, int p_index) const {
+Ref<Texture> Shader::get_default_texture_parameter(const StringName &p_name, int p_index) const {
if (default_textures.has(p_name) && default_textures[p_name].has(p_index)) {
return default_textures[p_name][p_index];
}
@@ -214,7 +215,7 @@ Ref<Texture2D> Shader::get_default_texture_parameter(const StringName &p_name, i
}
void Shader::get_default_texture_parameter_list(List<StringName> *r_textures) const {
- for (const KeyValue<StringName, HashMap<int, Ref<Texture2D>>> &E : default_textures) {
+ for (const KeyValue<StringName, HashMap<int, Ref<Texture>>> &E : default_textures) {
r_textures->push_back(E.key);
}
}
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 921143c219..682fbd7ea6 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -58,7 +58,7 @@ private:
String code;
String include_path;
- HashMap<StringName, HashMap<int, Ref<Texture2D>>> default_textures;
+ HashMap<StringName, HashMap<int, Ref<Texture>>> default_textures;
void _dependency_changed();
void _recompile();
@@ -66,6 +66,12 @@ private:
Array _get_shader_uniform_list(bool p_get_groups = false);
protected:
+#ifndef DISABLE_DEPRECATED
+ void _set_default_texture_parameter_bind_compat_95126(const StringName &p_name, const Ref<Texture2D> &p_texture, int p_index = 0);
+ Ref<Texture2D> _get_default_texture_parameter_bind_compat_95126(const StringName &p_name, int p_index = 0) const;
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
+
static void _bind_methods();
public:
@@ -80,8 +86,8 @@ public:
void get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_groups = false) const;
- void set_default_texture_parameter(const StringName &p_name, const Ref<Texture2D> &p_texture, int p_index = 0);
- Ref<Texture2D> get_default_texture_parameter(const StringName &p_name, int p_index = 0) const;
+ void set_default_texture_parameter(const StringName &p_name, const Ref<Texture> &p_texture, int p_index = 0);
+ Ref<Texture> get_default_texture_parameter(const StringName &p_name, int p_index = 0) const;
void get_default_texture_parameter_list(List<StringName> *r_textures) const;
virtual bool is_text_shader() const;
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index a144c5ba83..d0e55f4065 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -833,7 +833,7 @@ VisualShader::Type VisualShader::get_shader_type() const {
}
void VisualShader::add_varying(const String &p_name, VaryingMode p_mode, VaryingType p_type) {
- ERR_FAIL_COND(!p_name.is_valid_identifier());
+ ERR_FAIL_COND(!p_name.is_valid_ascii_identifier());
ERR_FAIL_INDEX((int)p_mode, (int)VARYING_MODE_MAX);
ERR_FAIL_INDEX((int)p_type, (int)VARYING_TYPE_MAX);
ERR_FAIL_COND(varyings.has(p_name));
@@ -2953,7 +2953,7 @@ void VisualShader::_update_shader() const {
const_cast<VisualShader *>(this)->set_code(final_code);
for (int i = 0; i < default_tex_params.size(); i++) {
int j = 0;
- for (List<Ref<Texture2D>>::ConstIterator itr = default_tex_params[i].params.begin(); itr != default_tex_params[i].params.end(); ++itr, ++j) {
+ for (List<Ref<Texture>>::ConstIterator itr = default_tex_params[i].params.begin(); itr != default_tex_params[i].params.end(); ++itr, ++j) {
const_cast<VisualShader *>(this)->set_default_texture_parameter(default_tex_params[i].name, *itr, j);
}
}
@@ -3082,6 +3082,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_direction_world", "CAMERA_DIRECTION_WORLD" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_position_world", "CAMERA_POSITION_WORLD" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_UINT, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "clip_space_far", "CLIP_SPACE_FAR" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom0", "CUSTOM0" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom1", "CUSTOM1" },
@@ -3119,6 +3120,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_direction_world", "CAMERA_DIRECTION_WORLD" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_position_world", "CAMERA_POSITION_WORLD" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_UINT, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clip_space_far", "CLIP_SPACE_FAR" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "exposure", "EXPOSURE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "eye_offset", "EYE_OFFSET" },
@@ -3150,6 +3152,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "attenuation", "ATTENUATION" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "backlight", "BACKLIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "clip_space_far", "CLIP_SPACE_FAR" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "diffuse", "DIFFUSE_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "exposure", "EXPOSURE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" },
@@ -4571,7 +4574,7 @@ String VisualShaderNodeGroupBase::get_outputs() const {
}
bool VisualShaderNodeGroupBase::is_valid_port_name(const String &p_name) const {
- if (!p_name.is_valid_identifier()) {
+ if (!p_name.is_valid_ascii_identifier()) {
return false;
}
for (int i = 0; i < get_input_port_count(); i++) {
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 2b213948de..8ec52fcaaa 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -66,7 +66,7 @@ public:
struct DefaultTextureParam {
StringName name;
- List<Ref<Texture2D>> params;
+ List<Ref<Texture>> params;
};
enum VaryingMode {
diff --git a/scene/resources/visual_shader_nodes.compat.inc b/scene/resources/visual_shader_nodes.compat.inc
new file mode 100644
index 0000000000..31d96d9c0f
--- /dev/null
+++ b/scene/resources/visual_shader_nodes.compat.inc
@@ -0,0 +1,63 @@
+/**************************************************************************/
+/* visual_shader_nodes.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
+
+// VisualShaderNodeCubemap
+
+void VisualShaderNodeCubemap::_set_cube_map_bind_compat_95126(Ref<Cubemap> p_cube_map) {
+ set_cube_map(p_cube_map);
+}
+
+Ref<Cubemap> VisualShaderNodeCubemap::_get_cube_map_bind_compat_95126() const {
+ return cube_map;
+}
+
+void VisualShaderNodeCubemap::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("set_cube_map", "value"), &VisualShaderNodeCubemap::_set_cube_map_bind_compat_95126);
+ ClassDB::bind_compatibility_method(D_METHOD("get_cube_map"), &VisualShaderNodeCubemap::_get_cube_map_bind_compat_95126);
+}
+
+// VisualShaderNodeTexture2DArray
+
+void VisualShaderNodeTexture2DArray::_set_texture_array_bind_compat_95126(Ref<Texture2DArray> p_texture_array) {
+ set_texture_array(p_texture_array);
+}
+
+Ref<Texture2DArray> VisualShaderNodeTexture2DArray::_get_texture_array_bind_compat_95126() const {
+ return texture_array;
+}
+
+void VisualShaderNodeTexture2DArray::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("set_texture_array", "value"), &VisualShaderNodeTexture2DArray::_set_texture_array_bind_compat_95126);
+ ClassDB::bind_compatibility_method(D_METHOD("get_texture_array"), &VisualShaderNodeTexture2DArray::_get_texture_array_bind_compat_95126);
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 5e148c9276..26666538af 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "visual_shader_nodes.h"
+#include "visual_shader_nodes.compat.inc"
#include "scene/resources/image_texture.h"
@@ -1353,12 +1354,12 @@ String VisualShaderNodeTexture2DArray::generate_global(Shader::Mode p_mode, Visu
return String();
}
-void VisualShaderNodeTexture2DArray::set_texture_array(Ref<Texture2DArray> p_texture_array) {
+void VisualShaderNodeTexture2DArray::set_texture_array(Ref<TextureLayered> p_texture_array) {
texture_array = p_texture_array;
emit_changed();
}
-Ref<Texture2DArray> VisualShaderNodeTexture2DArray::get_texture_array() const {
+Ref<TextureLayered> VisualShaderNodeTexture2DArray::get_texture_array() const {
return texture_array;
}
@@ -1375,7 +1376,7 @@ void VisualShaderNodeTexture2DArray::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture_array", "value"), &VisualShaderNodeTexture2DArray::set_texture_array);
ClassDB::bind_method(D_METHOD("get_texture_array"), &VisualShaderNodeTexture2DArray::get_texture_array);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_array", PROPERTY_HINT_RESOURCE_TYPE, "Texture2DArray"), "set_texture_array", "get_texture_array");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_array", PROPERTY_HINT_RESOURCE_TYPE, "Texture2DArray,CompressedTexture2DArray,PlaceholderTexture2DArray,Texture2DArrayRD"), "set_texture_array", "get_texture_array");
}
VisualShaderNodeTexture2DArray::VisualShaderNodeTexture2DArray() {
@@ -1568,12 +1569,12 @@ VisualShaderNodeCubemap::Source VisualShaderNodeCubemap::get_source() const {
return source;
}
-void VisualShaderNodeCubemap::set_cube_map(Ref<Cubemap> p_cube_map) {
+void VisualShaderNodeCubemap::set_cube_map(Ref<TextureLayered> p_cube_map) {
cube_map = p_cube_map;
emit_changed();
}
-Ref<Cubemap> VisualShaderNodeCubemap::get_cube_map() const {
+Ref<TextureLayered> VisualShaderNodeCubemap::get_cube_map() const {
return cube_map;
}
@@ -1618,7 +1619,7 @@ void VisualShaderNodeCubemap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeCubemap::get_texture_type);
ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,SamplerPort"), "set_source", "get_source");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "Cubemap"), "set_cube_map", "get_cube_map");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "Cubemap,CompressedCubemap,PlaceholderCubemap,TextureCubemapRD"), "set_cube_map", "get_cube_map");
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map"), "set_texture_type", "get_texture_type");
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 279599ef9c..ff02e55fb2 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -31,6 +31,7 @@
#ifndef VISUAL_SHADER_NODES_H
#define VISUAL_SHADER_NODES_H
+#include "scene/resources/compressed_texture.h"
#include "scene/resources/curve_texture.h"
#include "scene/resources/visual_shader.h"
@@ -562,9 +563,15 @@ VARIANT_ENUM_CAST(VisualShaderNodeSample3D::Source)
class VisualShaderNodeTexture2DArray : public VisualShaderNodeSample3D {
GDCLASS(VisualShaderNodeTexture2DArray, VisualShaderNodeSample3D);
- Ref<Texture2DArray> texture_array;
+ Ref<TextureLayered> texture_array;
protected:
+#ifndef DISABLE_DEPRECATED
+ void _set_texture_array_bind_compat_95126(Ref<Texture2DArray> p_texture_array);
+ Ref<Texture2DArray> _get_texture_array_bind_compat_95126() const;
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
+
static void _bind_methods();
public:
@@ -575,8 +582,8 @@ public:
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
- void set_texture_array(Ref<Texture2DArray> p_texture_array);
- Ref<Texture2DArray> get_texture_array() const;
+ void set_texture_array(Ref<TextureLayered> p_texture_array);
+ Ref<TextureLayered> get_texture_array() const;
virtual Vector<StringName> get_editable_properties() const override;
@@ -608,7 +615,7 @@ public:
class VisualShaderNodeCubemap : public VisualShaderNode {
GDCLASS(VisualShaderNodeCubemap, VisualShaderNode);
- Ref<Cubemap> cube_map;
+ Ref<TextureLayered> cube_map;
public:
enum Source {
@@ -629,6 +636,12 @@ private:
TextureType texture_type = TYPE_DATA;
protected:
+#ifndef DISABLE_DEPRECATED
+ void _set_cube_map_bind_compat_95126(Ref<Cubemap> p_cube_map);
+ Ref<Cubemap> _get_cube_map_bind_compat_95126() const;
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
+
static void _bind_methods();
public:
@@ -650,8 +663,8 @@ public:
void set_source(Source p_source);
Source get_source() const;
- void set_cube_map(Ref<Cubemap> p_cube_map);
- Ref<Cubemap> get_cube_map() const;
+ void set_cube_map(Ref<TextureLayered> p_cube_map);
+ Ref<TextureLayered> get_cube_map() const;
void set_texture_type(TextureType p_texture_type);
TextureType get_texture_type() const;
diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp
index b2a3843b02..8a9e784c47 100644
--- a/scene/theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
@@ -504,6 +504,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("can_fold_code_region", "CodeEdit", icons["region_unfolded"]);
theme->set_icon("folded_code_region", "CodeEdit", icons["region_folded"]);
theme->set_icon("folded_eol_icon", "CodeEdit", icons["text_edit_ellipsis"]);
+ theme->set_icon("completion_color_bg", "CodeEdit", icons["mini_checkerboard"]);
theme->set_font(SceneStringName(font), "CodeEdit", Ref<Font>());
theme->set_font_size(SceneStringName(font_size), "CodeEdit", -1);
@@ -613,7 +614,41 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// SpinBox
- theme->set_icon("updown", "SpinBox", icons["updown"]);
+ theme->set_icon("updown", "SpinBox", empty_icon);
+ theme->set_icon("up", "SpinBox", icons["value_up"]);
+ theme->set_icon("up_hover", "SpinBox", icons["value_up"]);
+ theme->set_icon("up_pressed", "SpinBox", icons["value_up"]);
+ theme->set_icon("up_disabled", "SpinBox", icons["value_up"]);
+ theme->set_icon("down", "SpinBox", icons["value_down"]);
+ theme->set_icon("down_hover", "SpinBox", icons["value_down"]);
+ theme->set_icon("down_pressed", "SpinBox", icons["value_down"]);
+ theme->set_icon("down_disabled", "SpinBox", icons["value_down"]);
+
+ theme->set_stylebox("up_background", "SpinBox", make_empty_stylebox());
+ theme->set_stylebox("up_background_hovered", "SpinBox", button_hover);
+ theme->set_stylebox("up_background_pressed", "SpinBox", button_pressed);
+ theme->set_stylebox("up_background_disabled", "SpinBox", make_empty_stylebox());
+ theme->set_stylebox("down_background", "SpinBox", make_empty_stylebox());
+ theme->set_stylebox("down_background_hovered", "SpinBox", button_hover);
+ theme->set_stylebox("down_background_pressed", "SpinBox", button_pressed);
+ theme->set_stylebox("down_background_disabled", "SpinBox", make_empty_stylebox());
+
+ theme->set_color("up_icon_modulate", "SpinBox", control_font_color);
+ theme->set_color("up_hover_icon_modulate", "SpinBox", control_font_hover_color);
+ theme->set_color("up_pressed_icon_modulate", "SpinBox", control_font_hover_color);
+ theme->set_color("up_disabled_icon_modulate", "SpinBox", control_font_disabled_color);
+ theme->set_color("down_icon_modulate", "SpinBox", control_font_color);
+ theme->set_color("down_hover_icon_modulate", "SpinBox", control_font_hover_color);
+ theme->set_color("down_pressed_icon_modulate", "SpinBox", control_font_hover_color);
+ theme->set_color("down_disabled_icon_modulate", "SpinBox", control_font_disabled_color);
+
+ theme->set_stylebox("field_and_buttons_separator", "SpinBox", make_empty_stylebox());
+ theme->set_stylebox("up_down_buttons_separator", "SpinBox", make_empty_stylebox());
+
+ theme->set_constant("buttons_vertical_separation", "SpinBox", 0);
+ theme->set_constant("field_and_buttons_separation", "SpinBox", 2);
+ theme->set_constant("buttons_width", "SpinBox", 16);
+ theme->set_constant("set_min_buttons_width_from_icons", "SpinBox", 1);
// ScrollContainer
diff --git a/scene/theme/icons/value_down.svg b/scene/theme/icons/value_down.svg
new file mode 100644
index 0000000000..57837d03fd
--- /dev/null
+++ b/scene/theme/icons/value_down.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="8"><path fill="none" stroke="#fff" stroke-width="2" d="m12 2-4 3.5L4 2"/></svg> \ No newline at end of file
diff --git a/scene/theme/icons/value_up.svg b/scene/theme/icons/value_up.svg
new file mode 100644
index 0000000000..53fb102fe2
--- /dev/null
+++ b/scene/theme/icons/value_up.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="8"><path fill="none" stroke="#fff" stroke-width="2" d="m4 6 4-3.5L12 6"/></svg> \ No newline at end of file
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index 42e1f7b6dc..6846c3f693 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -636,6 +636,7 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
+ actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz";
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index cf661bb8f4..08982096c5 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -540,6 +540,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
+ actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["NODE_POSITION_WORLD"] = "read_model_matrix[3].xyz";
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
index 1420e7939a..35d29fed6a 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
@@ -7,6 +7,7 @@
#include "scene_forward_clustered_inc.glsl"
#define SHADER_IS_SRGB false
+#define SHADER_SPACE_FAR 0.0
/* INPUT ATTRIBS */
@@ -638,6 +639,7 @@ void main() {
#VERSION_DEFINES
#define SHADER_IS_SRGB false
+#define SHADER_SPACE_FAR 0.0
/* Specialization Constants (Toggles) */
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
index 90947aca80..c266161834 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
@@ -8,6 +8,7 @@
#include "scene_forward_mobile_inc.glsl"
#define SHADER_IS_SRGB false
+#define SHADER_SPACE_FAR 0.0
/* INPUT ATTRIBS */
@@ -498,6 +499,7 @@ void main() {
#VERSION_DEFINES
#define SHADER_IS_SRGB false
+#define SHADER_SPACE_FAR 0.0
/* Specialization Constants */
diff --git a/servers/rendering/rendering_device_binds.cpp b/servers/rendering/rendering_device_binds.cpp
index 986f01a52c..d9ca286b15 100644
--- a/servers/rendering/rendering_device_binds.cpp
+++ b/servers/rendering/rendering_device_binds.cpp
@@ -112,7 +112,7 @@ Error RDShaderFile::parse_versions_from_text(const String &p_text, const String
}
Vector<String> slices = l.get_slice(";", 0).split("=");
String version = slices[0].strip_edges();
- if (!version.is_valid_identifier()) {
+ if (!version.is_valid_ascii_identifier()) {
base_error = "Version names must be valid identifiers, found '" + version + "' instead.";
break;
}
diff --git a/servers/rendering/shader_preprocessor.cpp b/servers/rendering/shader_preprocessor.cpp
index dbd1374941..27e39551ba 100644
--- a/servers/rendering/shader_preprocessor.cpp
+++ b/servers/rendering/shader_preprocessor.cpp
@@ -173,7 +173,7 @@ String ShaderPreprocessor::Tokenizer::get_identifier(bool *r_is_cursor, bool p_s
}
String id = vector_to_string(text);
- if (!id.is_valid_identifier()) {
+ if (!id.is_valid_ascii_identifier()) {
return "";
}
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index de396cd18b..f498c0bf93 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -97,6 +97,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["MODELVIEW_NORMAL_MATRIX"] = ShaderLanguage::TYPE_MAT3;
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["MAIN_CAM_INV_VIEW_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NODE_POSITION_WORLD"] = constt(ShaderLanguage::TYPE_VEC3);
@@ -159,6 +160,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["EYE_OFFSET"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["MODEL_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["MODEL_NORMAL_MATRIX"] = constt(ShaderLanguage::TYPE_MAT3);
@@ -202,6 +204,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["DIFFUSE_LIGHT"] = ShaderLanguage::TYPE_VEC3;
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["SPECULAR_LIGHT"] = ShaderLanguage::TYPE_VEC3;
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["light"].built_ins["ALPHA"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[RS::SHADER_SPATIAL].functions["light"].can_discard = true;
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index f391c79514..860cc5d75d 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -2165,23 +2165,7 @@ TypedArray<Dictionary> TextServer::_shaped_text_get_ellipsis_glyphs_wrapper(cons
}
bool TextServer::is_valid_identifier(const String &p_string) const {
- const char32_t *str = p_string.ptr();
- int len = p_string.length();
-
- if (len == 0) {
- return false; // Empty string.
- }
-
- if (!is_unicode_identifier_start(str[0])) {
- return false;
- }
-
- for (int i = 1; i < len; i++) {
- if (!is_unicode_identifier_continue(str[i])) {
- return false;
- }
- }
- return true;
+ return p_string.is_valid_unicode_identifier();
}
bool TextServer::is_valid_letter(uint64_t p_unicode) const {
diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h
index 8515ba7644..924e93129d 100644
--- a/tests/core/object/test_class_db.h
+++ b/tests/core/object/test_class_db.h
@@ -603,7 +603,7 @@ void add_exposed_classes(Context &r_context) {
MethodData method;
method.name = method_info.name;
- TEST_FAIL_COND(!String(method.name).is_valid_identifier(),
+ TEST_FAIL_COND(!String(method.name).is_valid_ascii_identifier(),
"Method name is not a valid identifier: '", exposed_class.name, ".", method.name, "'.");
if (method_info.flags & METHOD_FLAG_VIRTUAL) {
@@ -729,7 +729,7 @@ void add_exposed_classes(Context &r_context) {
const MethodInfo &method_info = signal_map.get(K.key);
signal.name = method_info.name;
- TEST_FAIL_COND(!String(signal.name).is_valid_identifier(),
+ TEST_FAIL_COND(!String(signal.name).is_valid_ascii_identifier(),
"Signal name is not a valid identifier: '", exposed_class.name, ".", signal.name, "'.");
int i = 0;
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 933eeff524..b47e5b1eb9 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -1821,21 +1821,23 @@ TEST_CASE("[String] Join") {
}
TEST_CASE("[String] Is_*") {
- static const char *data[12] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1" };
- static bool isnum[12] = { true, true, true, false, false, false, false, false, false, false, false, false };
- static bool isint[12] = { true, true, false, false, false, false, false, false, false, false, false, false };
- static bool ishex[12] = { true, true, false, false, true, false, true, false, true, false, false, false };
- static bool ishex_p[12] = { false, false, false, false, false, false, false, true, false, false, false, false };
- static bool isflt[12] = { true, true, true, false, true, true, false, false, false, false, false, false };
- static bool isid[12] = { false, false, false, false, false, false, false, false, true, true, false, false };
+ static const char *data[13] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1", "文字" };
+ static bool isnum[13] = { true, true, true, false, false, false, false, false, false, false, false, false, false };
+ static bool isint[13] = { true, true, false, false, false, false, false, false, false, false, false, false, false };
+ static bool ishex[13] = { true, true, false, false, true, false, true, false, true, false, false, false, false };
+ static bool ishex_p[13] = { false, false, false, false, false, false, false, true, false, false, false, false, false };
+ static bool isflt[13] = { true, true, true, false, true, true, false, false, false, false, false, false, false };
+ static bool isaid[13] = { false, false, false, false, false, false, false, false, true, true, false, false, false };
+ static bool isuid[13] = { false, false, false, false, false, false, false, false, true, true, false, false, true };
for (int i = 0; i < 12; i++) {
- String s = String(data[i]);
+ String s = String::utf8(data[i]);
CHECK(s.is_numeric() == isnum[i]);
CHECK(s.is_valid_int() == isint[i]);
CHECK(s.is_valid_hex_number(false) == ishex[i]);
CHECK(s.is_valid_hex_number(true) == ishex_p[i]);
CHECK(s.is_valid_float() == isflt[i]);
- CHECK(s.is_valid_identifier() == isid[i]);
+ CHECK(s.is_valid_ascii_identifier() == isaid[i]);
+ CHECK(s.is_valid_unicode_identifier() == isuid[i]);
}
}
@@ -1863,16 +1865,16 @@ TEST_CASE("[String] validate_node_name") {
TEST_CASE("[String] validate_identifier") {
String empty_string;
- CHECK(empty_string.validate_identifier() == "_");
+ CHECK(empty_string.validate_ascii_identifier() == "_");
String numeric_only = "12345";
- CHECK(numeric_only.validate_identifier() == "_12345");
+ CHECK(numeric_only.validate_ascii_identifier() == "_12345");
String name_with_spaces = "Name with spaces";
- CHECK(name_with_spaces.validate_identifier() == "Name_with_spaces");
+ CHECK(name_with_spaces.validate_ascii_identifier() == "Name_with_spaces");
String name_with_invalid_chars = U"Invalid characters:@*#&世界";
- CHECK(name_with_invalid_chars.validate_identifier() == "Invalid_characters_______");
+ CHECK(name_with_invalid_chars.validate_ascii_identifier() == "Invalid_characters_______");
}
TEST_CASE("[String] Variant indexed get") {
diff --git a/tests/scene/test_node_2d.h b/tests/scene/test_node_2d.h
index 8cf6408438..e8e7b2880d 100644
--- a/tests/scene/test_node_2d.h
+++ b/tests/scene/test_node_2d.h
@@ -86,6 +86,131 @@ TEST_CASE("[SceneTree][Node2D]") {
}
}
+TEST_CASE("[SceneTree][Node2D] Utility methods") {
+ Node2D *test_node1 = memnew(Node2D);
+ Node2D *test_node2 = memnew(Node2D);
+ Node2D *test_node3 = memnew(Node2D);
+ Node2D *test_sibling = memnew(Node2D);
+ SceneTree::get_singleton()->get_root()->add_child(test_node1);
+
+ test_node1->set_position(Point2(100, 100));
+ test_node1->set_rotation(Math::deg_to_rad(30.0));
+ test_node1->set_scale(Size2(1, 1));
+ test_node1->add_child(test_node2);
+
+ test_node2->set_position(Point2(10, 0));
+ test_node2->set_rotation(Math::deg_to_rad(60.0));
+ test_node2->set_scale(Size2(1, 1));
+ test_node2->add_child(test_node3);
+ test_node2->add_child(test_sibling);
+
+ test_node3->set_position(Point2(0, 10));
+ test_node3->set_rotation(Math::deg_to_rad(0.0));
+ test_node3->set_scale(Size2(2, 2));
+
+ test_sibling->set_position(Point2(5, 10));
+ test_sibling->set_rotation(Math::deg_to_rad(90.0));
+ test_sibling->set_scale(Size2(2, 1));
+
+ SUBCASE("[Node2D] look_at") {
+ test_node3->look_at(Vector2(1, 1));
+
+ CHECK(test_node3->get_global_position().is_equal_approx(Point2(98.66026, 105)));
+ CHECK(Math::is_equal_approx(test_node3->get_global_rotation(), real_t(-2.32477)));
+ CHECK(test_node3->get_global_scale().is_equal_approx(Vector2(2, 2)));
+
+ CHECK(test_node3->get_position().is_equal_approx(Vector2(0, 10)));
+ CHECK(Math::is_equal_approx(test_node3->get_rotation(), real_t(2.38762)));
+ CHECK(test_node3->get_scale().is_equal_approx(Vector2(2, 2)));
+
+ test_node3->look_at(Vector2(0, 10));
+
+ CHECK(test_node3->get_global_position().is_equal_approx(Vector2(98.66026, 105)));
+ CHECK(Math::is_equal_approx(test_node3->get_global_rotation(), real_t(-2.37509)));
+ CHECK(test_node3->get_global_scale().is_equal_approx(Vector2(2, 2)));
+
+ CHECK(test_node3->get_position().is_equal_approx(Vector2(0, 10)));
+ CHECK(Math::is_equal_approx(test_node3->get_rotation(), real_t(2.3373)));
+ CHECK(test_node3->get_scale().is_equal_approx(Vector2(2, 2)));
+
+ // Don't do anything if look_at own position.
+ test_node3->look_at(test_node3->get_global_position());
+
+ CHECK(test_node3->get_global_position().is_equal_approx(Vector2(98.66026, 105)));
+ CHECK(Math::is_equal_approx(test_node3->get_global_rotation(), real_t(-2.37509)));
+ CHECK(test_node3->get_global_scale().is_equal_approx(Vector2(2, 2)));
+
+ CHECK(test_node3->get_position().is_equal_approx(Vector2(0, 10)));
+ CHECK(Math::is_equal_approx(test_node3->get_rotation(), real_t(2.3373)));
+ CHECK(test_node3->get_scale().is_equal_approx(Vector2(2, 2)));
+
+ // Revert any rotation caused by look_at, must run after look_at tests
+ test_node3->set_rotation(Math::deg_to_rad(0.0));
+ }
+
+ SUBCASE("[Node2D] get_angle_to") {
+ CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(1, 1)), real_t(2.38762)));
+ CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(0, 10)), real_t(2.3373)));
+ CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(2, -5)), real_t(2.42065)));
+ CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(-2, 5)), real_t(2.3529)));
+
+ // Return 0 when get_angle_to own position.
+ CHECK(Math::is_equal_approx(test_node3->get_angle_to(test_node3->get_global_position()), real_t(0)));
+ }
+
+ SUBCASE("[Node2D] to_local") {
+ Point2 node3_local = test_node3->to_local(Point2(1, 2));
+ CHECK(node3_local.is_equal_approx(Point2(-51.5, 48.83013)));
+
+ node3_local = test_node3->to_local(Point2(-2, 1));
+ CHECK(node3_local.is_equal_approx(Point2(-52, 50.33013)));
+
+ node3_local = test_node3->to_local(Point2(0, 0));
+ CHECK(node3_local.is_equal_approx(Point2(-52.5, 49.33013)));
+
+ node3_local = test_node3->to_local(test_node3->get_global_position());
+ CHECK(node3_local.is_equal_approx(Point2(0, 0)));
+ }
+
+ SUBCASE("[Node2D] to_global") {
+ Point2 node3_global = test_node3->to_global(Point2(1, 2));
+ CHECK(node3_global.is_equal_approx(Point2(94.66026, 107)));
+
+ node3_global = test_node3->to_global(Point2(-2, 1));
+ CHECK(node3_global.is_equal_approx(Point2(96.66026, 101)));
+
+ node3_global = test_node3->to_global(Point2(0, 0));
+ CHECK(node3_global.is_equal_approx(test_node3->get_global_position()));
+ }
+
+ SUBCASE("[Node2D] get_relative_transform_to_parent") {
+ Transform2D relative_xform = test_node3->get_relative_transform_to_parent(test_node3);
+ CHECK(relative_xform.is_equal_approx(Transform2D()));
+
+ relative_xform = test_node3->get_relative_transform_to_parent(test_node2);
+ CHECK(relative_xform.get_origin().is_equal_approx(Vector2(0, 10)));
+ CHECK(Math::is_equal_approx(relative_xform.get_rotation(), real_t(0)));
+ CHECK(relative_xform.get_scale().is_equal_approx(Vector2(2, 2)));
+
+ relative_xform = test_node3->get_relative_transform_to_parent(test_node1);
+ CHECK(relative_xform.get_origin().is_equal_approx(Vector2(1.339746, 5)));
+ CHECK(Math::is_equal_approx(relative_xform.get_rotation(), real_t(1.0472)));
+ CHECK(relative_xform.get_scale().is_equal_approx(Vector2(2, 2)));
+
+ ERR_PRINT_OFF;
+ // In case of a sibling all transforms until the root are accumulated.
+ Transform2D xform = test_node3->get_relative_transform_to_parent(test_sibling);
+ Transform2D return_xform = test_node1->get_global_transform().inverse() * test_node3->get_global_transform();
+ CHECK(xform.is_equal_approx(return_xform));
+ ERR_PRINT_ON;
+ }
+
+ memdelete(test_sibling);
+ memdelete(test_node3);
+ memdelete(test_node2);
+ memdelete(test_node1);
+}
+
} // namespace TestNode2D
#endif // TEST_NODE_2D_H
diff --git a/tests/scene/test_style_box_texture.h b/tests/scene/test_style_box_texture.h
new file mode 100644
index 0000000000..cc5be4b2d4
--- /dev/null
+++ b/tests/scene/test_style_box_texture.h
@@ -0,0 +1,194 @@
+/**************************************************************************/
+/* test_style_box_texture.h */
+/**************************************************************************/
+/* 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 TEST_STYLE_BOX_TEXTURE_H
+#define TEST_STYLE_BOX_TEXTURE_H
+
+#include "scene/resources/style_box_texture.h"
+
+#include "tests/test_macros.h"
+
+namespace TestStyleBoxTexture {
+
+TEST_CASE("[StyleBoxTexture] Constructor") {
+ Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
+
+ CHECK(style_box_texture->get_h_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_STRETCH);
+ CHECK(style_box_texture->get_v_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_STRETCH);
+ CHECK(style_box_texture->is_draw_center_enabled() == true);
+
+ CHECK(style_box_texture->get_expand_margin(SIDE_LEFT) == 0);
+ CHECK(style_box_texture->get_expand_margin(SIDE_TOP) == 0);
+ CHECK(style_box_texture->get_expand_margin(SIDE_RIGHT) == 0);
+ CHECK(style_box_texture->get_expand_margin(SIDE_BOTTOM) == 0);
+
+ CHECK(style_box_texture->get_modulate() == Color(1, 1, 1, 1));
+ CHECK(style_box_texture->get_region_rect() == Rect2(0, 0, 0, 0));
+ CHECK(style_box_texture->get_texture() == Ref<Texture2D>());
+
+ CHECK(style_box_texture->get_texture_margin(SIDE_LEFT) == 0);
+ CHECK(style_box_texture->get_texture_margin(SIDE_TOP) == 0);
+ CHECK(style_box_texture->get_texture_margin(SIDE_RIGHT) == 0);
+ CHECK(style_box_texture->get_texture_margin(SIDE_BOTTOM) == 0);
+}
+
+TEST_CASE("[StyleBoxTexture] set_texture, get_texture") {
+ Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
+ Ref<Texture2D> texture = memnew(Texture2D);
+
+ style_box_texture->set_texture(texture);
+ CHECK(style_box_texture->get_texture() == texture);
+}
+
+TEST_CASE("[StyleBoxTexture] set_texture_margin, set_texture_margin_all, set_texture_margin_individual, get_texture_margin") {
+ Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
+
+ SUBCASE("set_texture_margin, get_texture_margin") {
+ style_box_texture->set_texture_margin(SIDE_LEFT, 1);
+ style_box_texture->set_texture_margin(SIDE_TOP, 1);
+ style_box_texture->set_texture_margin(SIDE_RIGHT, 1);
+ style_box_texture->set_texture_margin(SIDE_BOTTOM, 1);
+
+ CHECK(style_box_texture->get_texture_margin(SIDE_LEFT) == 1);
+ CHECK(style_box_texture->get_texture_margin(SIDE_TOP) == 1);
+ CHECK(style_box_texture->get_texture_margin(SIDE_RIGHT) == 1);
+ CHECK(style_box_texture->get_texture_margin(SIDE_BOTTOM) == 1);
+ }
+
+ SUBCASE("set_texture_margin_all") {
+ style_box_texture->set_texture_margin_all(2);
+
+ CHECK(style_box_texture->get_texture_margin(SIDE_LEFT) == 2);
+ CHECK(style_box_texture->get_texture_margin(SIDE_TOP) == 2);
+ CHECK(style_box_texture->get_texture_margin(SIDE_RIGHT) == 2);
+ CHECK(style_box_texture->get_texture_margin(SIDE_BOTTOM) == 2);
+ }
+
+ SUBCASE("set_texture_margin_individual") {
+ style_box_texture->set_texture_margin_individual(3, 4, 5, 6);
+
+ CHECK(style_box_texture->get_texture_margin(SIDE_LEFT) == 3);
+ CHECK(style_box_texture->get_texture_margin(SIDE_TOP) == 4);
+ CHECK(style_box_texture->get_texture_margin(SIDE_RIGHT) == 5);
+ CHECK(style_box_texture->get_texture_margin(SIDE_BOTTOM) == 6);
+ }
+}
+
+TEST_CASE("[StyleBoxTexture] set_expand_margin, set_expand_margin_all, set_expand_margin_individual") {
+ Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
+
+ SUBCASE("set_expand_margin, get_expand_margin") {
+ style_box_texture->set_expand_margin(SIDE_LEFT, 1);
+ style_box_texture->set_expand_margin(SIDE_TOP, 1);
+ style_box_texture->set_expand_margin(SIDE_RIGHT, 1);
+ style_box_texture->set_expand_margin(SIDE_BOTTOM, 1);
+
+ CHECK(style_box_texture->get_expand_margin(SIDE_LEFT) == 1);
+ CHECK(style_box_texture->get_expand_margin(SIDE_TOP) == 1);
+ CHECK(style_box_texture->get_expand_margin(SIDE_RIGHT) == 1);
+ CHECK(style_box_texture->get_expand_margin(SIDE_BOTTOM) == 1);
+ }
+
+ SUBCASE("set_expand_margin_all") {
+ style_box_texture->set_expand_margin_all(2);
+
+ CHECK(style_box_texture->get_expand_margin(SIDE_LEFT) == 2);
+ CHECK(style_box_texture->get_expand_margin(SIDE_TOP) == 2);
+ CHECK(style_box_texture->get_expand_margin(SIDE_RIGHT) == 2);
+ CHECK(style_box_texture->get_expand_margin(SIDE_BOTTOM) == 2);
+ }
+
+ SUBCASE("set_expand_margin_individual") {
+ style_box_texture->set_expand_margin_individual(3, 4, 5, 6);
+
+ CHECK(style_box_texture->get_expand_margin(SIDE_LEFT) == 3);
+ CHECK(style_box_texture->get_expand_margin(SIDE_TOP) == 4);
+ CHECK(style_box_texture->get_expand_margin(SIDE_RIGHT) == 5);
+ CHECK(style_box_texture->get_expand_margin(SIDE_BOTTOM) == 6);
+ }
+}
+
+TEST_CASE("[StyleBoxTexture] set_region_rect, get_region_rect") {
+ Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
+
+ style_box_texture->set_region_rect(Rect2(1, 1, 1, 1));
+ CHECK(style_box_texture->get_region_rect() == Rect2(1, 1, 1, 1));
+}
+
+TEST_CASE("[StyleBoxTexture] set_draw_center, get_draw_center") {
+ Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
+
+ style_box_texture->set_draw_center(false);
+ CHECK(style_box_texture->is_draw_center_enabled() == false);
+}
+
+TEST_CASE("[StyleBoxTexture] set_h_axis_stretch_mode, set_v_axis_stretch_mode, get_h_axis_stretch_mode, get_v_axis_stretch_mode") {
+ Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
+
+ SUBCASE("set_h_axis_stretch_mode, get_h_axis_stretch_mode") {
+ style_box_texture->set_h_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_TILE);
+ CHECK(style_box_texture->get_h_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_TILE);
+
+ style_box_texture->set_h_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_TILE_FIT);
+ CHECK(style_box_texture->get_h_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_TILE_FIT);
+
+ style_box_texture->set_h_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_STRETCH);
+ CHECK(style_box_texture->get_h_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_STRETCH);
+ }
+
+ SUBCASE("set_v_axis_stretch_mode, get_v_axis_stretch_mode") {
+ style_box_texture->set_v_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_TILE);
+ CHECK(style_box_texture->get_v_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_TILE);
+
+ style_box_texture->set_v_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_TILE_FIT);
+ CHECK(style_box_texture->get_v_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_TILE_FIT);
+
+ style_box_texture->set_v_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_STRETCH);
+ CHECK(style_box_texture->get_v_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_STRETCH);
+ }
+}
+
+TEST_CASE("[StyleBoxTexture] set_modulate, get_modulate") {
+ Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
+
+ style_box_texture->set_modulate(Color(0, 0, 0, 0));
+ CHECK(style_box_texture->get_modulate() == Color(0, 0, 0, 0));
+}
+
+TEST_CASE("[StyleBoxTexture] get_draw_rect") {
+ Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
+
+ style_box_texture->set_expand_margin_all(5);
+ CHECK(style_box_texture->get_draw_rect(Rect2(0, 0, 1, 1)) == Rect2(-5, -5, 11, 11));
+}
+
+} // namespace TestStyleBoxTexture
+
+#endif // TEST_STYLE_BOX_TEXTURE_H
diff --git a/tests/scene/test_tree.h b/tests/scene/test_tree.h
new file mode 100644
index 0000000000..41ef39d621
--- /dev/null
+++ b/tests/scene/test_tree.h
@@ -0,0 +1,151 @@
+/**************************************************************************/
+/* test_tree.h */
+/**************************************************************************/
+/* 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 TEST_TREE_H
+#define TEST_TREE_H
+
+#include "scene/gui/tree.h"
+
+#include "tests/test_macros.h"
+
+namespace TestTree {
+
+TEST_CASE("[SceneTree][Tree]") {
+ SUBCASE("[Tree] Create and remove items.") {
+ Tree *tree = memnew(Tree);
+ TreeItem *root = tree->create_item();
+
+ TreeItem *child1 = tree->create_item();
+ CHECK_EQ(root->get_child_count(), 1);
+
+ TreeItem *child2 = tree->create_item(root);
+ CHECK_EQ(root->get_child_count(), 2);
+
+ TreeItem *child3 = tree->create_item(root, 0);
+ CHECK_EQ(root->get_child_count(), 3);
+
+ CHECK_EQ(root->get_child(0), child3);
+ CHECK_EQ(root->get_child(1), child1);
+ CHECK_EQ(root->get_child(2), child2);
+
+ root->remove_child(child3);
+ CHECK_EQ(root->get_child_count(), 2);
+
+ root->add_child(child3);
+ CHECK_EQ(root->get_child_count(), 3);
+
+ TreeItem *child4 = root->create_child();
+ CHECK_EQ(root->get_child_count(), 4);
+
+ CHECK_EQ(root->get_child(0), child1);
+ CHECK_EQ(root->get_child(1), child2);
+ CHECK_EQ(root->get_child(2), child3);
+ CHECK_EQ(root->get_child(3), child4);
+
+ memdelete(tree);
+ }
+
+ SUBCASE("[Tree] Clear items.") {
+ Tree *tree = memnew(Tree);
+ TreeItem *root = tree->create_item();
+
+ for (int i = 0; i < 10; i++) {
+ tree->create_item();
+ }
+ CHECK_EQ(root->get_child_count(), 10);
+
+ root->clear_children();
+ CHECK_EQ(root->get_child_count(), 0);
+
+ memdelete(tree);
+ }
+
+ SUBCASE("[Tree] Get last item.") {
+ Tree *tree = memnew(Tree);
+ TreeItem *root = tree->create_item();
+
+ TreeItem *last;
+ for (int i = 0; i < 10; i++) {
+ last = tree->create_item();
+ }
+ CHECK_EQ(root->get_child_count(), 10);
+ CHECK_EQ(tree->get_last_item(), last);
+
+ // Check nested.
+ TreeItem *old_last = last;
+ for (int i = 0; i < 10; i++) {
+ last = tree->create_item(old_last);
+ }
+ CHECK_EQ(tree->get_last_item(), last);
+
+ memdelete(tree);
+ }
+
+ SUBCASE("[Tree] Previous and Next items.") {
+ Tree *tree = memnew(Tree);
+ TreeItem *root = tree->create_item();
+
+ TreeItem *child1 = tree->create_item();
+ TreeItem *child2 = tree->create_item();
+ TreeItem *child3 = tree->create_item();
+ CHECK_EQ(child1->get_next(), child2);
+ CHECK_EQ(child1->get_next_in_tree(), child2);
+ CHECK_EQ(child2->get_next(), child3);
+ CHECK_EQ(child2->get_next_in_tree(), child3);
+ CHECK_EQ(child3->get_next(), nullptr);
+ CHECK_EQ(child3->get_next_in_tree(), nullptr);
+
+ CHECK_EQ(child1->get_prev(), nullptr);
+ CHECK_EQ(child1->get_prev_in_tree(), root);
+ CHECK_EQ(child2->get_prev(), child1);
+ CHECK_EQ(child2->get_prev_in_tree(), child1);
+ CHECK_EQ(child3->get_prev(), child2);
+ CHECK_EQ(child3->get_prev_in_tree(), child2);
+
+ TreeItem *nested1 = tree->create_item(child2);
+ TreeItem *nested2 = tree->create_item(child2);
+ TreeItem *nested3 = tree->create_item(child2);
+
+ CHECK_EQ(child1->get_next(), child2);
+ CHECK_EQ(child1->get_next_in_tree(), child2);
+ CHECK_EQ(child2->get_next(), child3);
+ CHECK_EQ(child2->get_next_in_tree(), nested1);
+ CHECK_EQ(child3->get_prev(), child2);
+ CHECK_EQ(child3->get_prev_in_tree(), nested3);
+ CHECK_EQ(nested1->get_prev_in_tree(), child2);
+ CHECK_EQ(nested1->get_next_in_tree(), nested2);
+
+ memdelete(tree);
+ }
+}
+
+} // namespace TestTree
+
+#endif // TEST_TREE_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index edadc52a16..5308fe5e80 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -119,6 +119,7 @@
#include "tests/scene/test_path_2d.h"
#include "tests/scene/test_path_follow_2d.h"
#include "tests/scene/test_sprite_frames.h"
+#include "tests/scene/test_style_box_texture.h"
#include "tests/scene/test_theme.h"
#include "tests/scene/test_timer.h"
#include "tests/scene/test_viewport.h"
@@ -133,6 +134,7 @@
#include "tests/scene/test_color_picker.h"
#include "tests/scene/test_graph_node.h"
#include "tests/scene/test_text_edit.h"
+#include "tests/scene/test_tree.h"
#endif // ADVANCED_GUI_DISABLED
#ifndef _3D_DISABLED