summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/input/input_map.cpp22
-rw-r--r--core/io/dir_access.cpp2
-rw-r--r--core/object/object.h2
-rw-r--r--doc/classes/@GlobalScope.xml8
-rw-r--r--doc/classes/AnimationPlayer.xml2
-rw-r--r--doc/classes/Button.xml4
-rw-r--r--doc/classes/ConfirmationDialog.xml4
-rw-r--r--doc/classes/Container.xml4
-rw-r--r--doc/classes/Control.xml2
-rw-r--r--doc/classes/DisplayServer.xml221
-rw-r--r--doc/classes/EditorFeatureProfile.xml8
-rw-r--r--doc/classes/EditorInspector.xml2
-rw-r--r--doc/classes/EditorInspectorPlugin.xml2
-rw-r--r--doc/classes/EditorSettings.xml4
-rw-r--r--doc/classes/Expression.xml4
-rw-r--r--doc/classes/GPUParticles2D.xml3
-rw-r--r--doc/classes/Input.xml2
-rw-r--r--doc/classes/MovieWriter.xml4
-rw-r--r--doc/classes/Node.xml4
-rw-r--r--doc/classes/OS.xml3
-rw-r--r--doc/classes/Object.xml8
-rw-r--r--doc/classes/OccluderInstance3D.xml2
-rw-r--r--doc/classes/PortableCompressedTexture2D.xml2
-rw-r--r--doc/classes/ProjectSettings.xml29
-rw-r--r--doc/classes/RenderingServer.xml1
-rw-r--r--doc/classes/TextEdit.xml7
-rw-r--r--doc/classes/VisualShaderNodeFloatParameter.xml2
-rw-r--r--doc/classes/VisualShaderNodeParameter.xml2
-rw-r--r--doc/classes/VoxelGIData.xml2
-rw-r--r--doc/classes/float.xml13
-rw-r--r--doc/classes/int.xml10
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp10
-rw-r--r--drivers/gles3/storage/utilities.cpp163
-rw-r--r--drivers/gles3/storage/utilities.h85
-rw-r--r--drivers/vulkan/vulkan_context.cpp46
-rw-r--r--editor/debugger/editor_debugger_node.cpp19
-rw-r--r--editor/debugger/editor_debugger_node.h1
-rw-r--r--editor/debugger/editor_debugger_tree.cpp32
-rw-r--r--editor/debugger/editor_debugger_tree.h5
-rw-r--r--editor/editor_audio_buses.cpp2
-rw-r--r--editor/editor_autoload_settings.cpp2
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/editor_properties_array_dict.cpp6
-rw-r--r--editor/editor_toaster.cpp2
-rw-r--r--editor/find_in_files.cpp2
-rw-r--r--editor/import/resource_importer_scene.cpp2
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp6
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/font_config_plugin.cpp6
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/theme_editor_plugin.cpp12
-rw-r--r--editor/plugins/theme_editor_preview.cpp2
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp10
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp10
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp6
-rw-r--r--editor/plugins/tiles/tile_set_editor.h1
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp2
-rw-r--r--editor/scene_tree_dock.cpp4
-rw-r--r--modules/enet/doc_classes/ENetPacketPeer.xml26
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp4
-rw-r--r--modules/jpg/image_loader_jpegd.cpp38
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs7
-rw-r--r--modules/mono/editor/bindings_generator.cpp89
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs32
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs74
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs480
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs28
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs67
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs38
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs45
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj1
-rw-r--r--modules/mono/glue/runtime_interop.cpp12
-rw-r--r--modules/mono/managed_callable.cpp5
-rw-r--r--modules/mono/managed_callable.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h2
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp4
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.cpp6
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.cpp2
-rw-r--r--modules/webp/resource_saver_webp.cpp6
-rw-r--r--modules/webp/webp_common.cpp2
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml10
-rw-r--r--platform/android/jni_utils.cpp16
-rw-r--r--scene/2d/tile_map.cpp38
-rw-r--r--scene/3d/ray_cast_3d.cpp2
-rw-r--r--scene/3d/shape_cast_3d.cpp2
-rw-r--r--scene/animation/animation_player.cpp2
-rw-r--r--scene/debugger/scene_debugger.cpp42
-rw-r--r--scene/debugger/scene_debugger.h13
-rw-r--r--scene/gui/code_edit.cpp16
-rw-r--r--scene/gui/color_mode.cpp84
-rw-r--r--scene/gui/color_picker.cpp28
-rw-r--r--scene/gui/text_edit.cpp125
-rw-r--r--scene/gui/text_edit.h4
-rw-r--r--scene/gui/texture_progress_bar.cpp26
-rw-r--r--scene/main/instance_placeholder.cpp2
-rw-r--r--scene/main/node.cpp19
-rw-r--r--scene/main/node.h2
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/property_utils.cpp16
-rw-r--r--scene/resources/texture.cpp2
-rw-r--r--servers/rendering/renderer_rd/shaders/cluster_render.glsl3
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl30
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl3
-rw-r--r--tests/core/io/test_image.h4
-rw-r--r--tests/core/math/test_basis.h129
-rw-r--r--tests/scene/test_text_edit.h39
115 files changed, 1840 insertions, 661 deletions
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 0c765fe23c..ea75a6b079 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -331,6 +331,10 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
{ "ui_text_caret_document_start.macos", TTRC("Caret Document Start") },
{ "ui_text_caret_document_end", TTRC("Caret Document End") },
{ "ui_text_caret_document_end.macos", TTRC("Caret Document End") },
+ { "ui_text_caret_add_below", TTRC("Caret Add Below") },
+ { "ui_text_caret_add_below.macos", TTRC("Caret Add Below") },
+ { "ui_text_caret_add_above", TTRC("Caret Add Above") },
+ { "ui_text_caret_add_above.macos", TTRC("Caret Add Above") },
{ "ui_text_scroll_up", TTRC("Scroll Up") },
{ "ui_text_scroll_up.macos", TTRC("Scroll Up") },
{ "ui_text_scroll_down", TTRC("Scroll Down") },
@@ -617,6 +621,24 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL));
default_builtin_cache.insert("ui_text_caret_document_end.macos", inputs);
+ // Text Caret Addition Below/Above
+
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
+ default_builtin_cache.insert("ui_text_caret_add_below", inputs);
+
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(Key::L | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
+ default_builtin_cache.insert("ui_text_caret_add_below.macos", inputs);
+
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
+ default_builtin_cache.insert("ui_text_caret_add_above", inputs);
+
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(Key::O | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
+ default_builtin_cache.insert("ui_text_caret_add_above.macos", inputs);
+
// Text Scrolling
inputs = List<Ref<InputEvent>>();
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 79e7fa16e3..7eb50d2261 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -351,7 +351,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
const size_t copy_buffer_limit = 65536; // 64 KB
fsrc->seek_end(0);
- int size = fsrc->get_position();
+ uint64_t size = fsrc->get_position();
fsrc->seek(0);
err = OK;
size_t buffer_size = MIN(size * sizeof(uint8_t), copy_buffer_limit);
diff --git a/core/object/object.h b/core/object/object.h
index 359ab0f211..fa3003cc1f 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -133,7 +133,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_ARRAY = 1 << 29, // Used in the inspector to group properties as elements of an array.
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR,
- PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNATIONALIZED,
+ PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNATIONALIZED,
PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE,
};
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 9eeb69d824..b89eb121b6 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2789,13 +2789,13 @@
The property is serialized and saved in the scene file (default).
</constant>
<constant name="PROPERTY_USAGE_EDITOR" value="4" enum="PropertyUsageFlags">
- The property is shown in the editor Inspector (default).
+ The property is shown in the [EditorInspector] (default).
</constant>
<constant name="PROPERTY_USAGE_CHECKABLE" value="8" enum="PropertyUsageFlags">
- The property can be checked in the editor inspector.
+ The property can be checked in the [EditorInspector].
</constant>
<constant name="PROPERTY_USAGE_CHECKED" value="16" enum="PropertyUsageFlags">
- The property is checked in the editor inspector.
+ The property is checked in the [EditorInspector].
</constant>
<constant name="PROPERTY_USAGE_INTERNATIONALIZED" value="32" enum="PropertyUsageFlags">
The property is a translatable string.
@@ -2853,7 +2853,7 @@
<constant name="PROPERTY_USAGE_EDITOR_BASIC_SETTING" value="134217728" enum="PropertyUsageFlags">
</constant>
<constant name="PROPERTY_USAGE_READ_ONLY" value="268435456" enum="PropertyUsageFlags">
- The property is read-only in the editor Inspector.
+ The property is read-only in the [EditorInspector].
</constant>
<constant name="PROPERTY_USAGE_ARRAY" value="536870912" enum="PropertyUsageFlags">
The property is an array.
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index bf5ac7fa9e..9efd3ac939 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -217,7 +217,7 @@
</member>
<member name="current_animation" type="String" setter="set_current_animation" getter="get_current_animation" default="&quot;&quot;">
The key of the currently playing animation. If no animation is playing, the property's value is an empty string. Changing this value does not restart the animation. See [method play] for more information on playing animations.
- [b]Note:[/b] while this property appears in the inspector, it's not meant to be edited, and it's not saved in the scene. This property is mainly used to get the currently playing animation, and internally for animation playback tracks. For more information, see [Animation].
+ [b]Note:[/b] While this property appears in the Inspector, it's not meant to be edited, and it's not saved in the scene. This property is mainly used to get the currently playing animation, and internally for animation playback tracks. For more information, see [Animation].
</member>
<member name="current_animation_length" type="float" setter="" getter="get_current_animation_length">
The length (in seconds) of the currently playing animation.
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index e78cdfc951..1ff51b48be 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -11,7 +11,7 @@
func _ready():
var button = Button.new()
button.text = "Click me"
- button.connect("pressed", self, "_button_pressed")
+ button.pressed.connect(self._button_pressed)
add_child(button)
func _button_pressed():
@@ -22,7 +22,7 @@
{
var button = new Button();
button.Text = "Click me";
- button.Connect("pressed", this, nameof(ButtonPressed));
+ button.Pressed += ButtonPressed;
AddChild(button);
}
diff --git a/doc/classes/ConfirmationDialog.xml b/doc/classes/ConfirmationDialog.xml
index d4c503857d..48b4df9126 100644
--- a/doc/classes/ConfirmationDialog.xml
+++ b/doc/classes/ConfirmationDialog.xml
@@ -8,10 +8,10 @@
To get cancel action, you can use:
[codeblocks]
[gdscript]
- get_cancel().connect("pressed", self, "cancelled")
+ get_cancel_button().pressed.connect(self.cancelled)
[/gdscript]
[csharp]
- GetCancel().Connect("pressed", this, nameof(Cancelled));
+ GetCancelButton().Pressed += Cancelled;
[/csharp]
[/codeblocks]
</description>
diff --git a/doc/classes/Container.xml b/doc/classes/Container.xml
index a13e1598b2..0e1ef02573 100644
--- a/doc/classes/Container.xml
+++ b/doc/classes/Container.xml
@@ -14,14 +14,14 @@
<method name="_get_allowed_size_flags_horizontal" qualifiers="virtual const">
<return type="PackedInt32Array" />
<description>
- Implement to return a list of allowed horizontal [enum Control.SizeFlags] for child nodes. This doesn't technically prevent the usages of any other size flags, if your implementation requires that. This only limits the options available to the user in the inspector dock.
+ Implement to return a list of allowed horizontal [enum Control.SizeFlags] for child nodes. This doesn't technically prevent the usages of any other size flags, if your implementation requires that. This only limits the options available to the user in the Inspector dock.
[b]Note:[/b] Having no size flags is equal to having [constant Control.SIZE_SHRINK_BEGIN]. As such, this value is always implicitly allowed.
</description>
</method>
<method name="_get_allowed_size_flags_vertical" qualifiers="virtual const">
<return type="PackedInt32Array" />
<description>
- Implement to return a list of allowed vertical [enum Control.SizeFlags] for child nodes. This doesn't technically prevent the usages of any other size flags, if your implementation requires that. This only limits the options available to the user in the inspector dock.
+ Implement to return a list of allowed vertical [enum Control.SizeFlags] for child nodes. This doesn't technically prevent the usages of any other size flags, if your implementation requires that. This only limits the options available to the user in the Inspector dock.
[b]Note:[/b] Having no size flags is equal to having [constant Control.SIZE_SHRINK_BEGIN]. As such, this value is always implicitly allowed.
</description>
</method>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 91e9b65a8a..c1fa1a2bf1 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -12,7 +12,7 @@
Call [method accept_event] so no other node receives the event. Once you accept an input, it becomes handled so [method Node._unhandled_input] will not process it.
Only one [Control] node can be in focus. Only the node in focus will receive events. To get the focus, call [method grab_focus]. [Control] nodes lose focus when another node grabs it, or if you hide the node in focus.
Sets [member mouse_filter] to [constant MOUSE_FILTER_IGNORE] to tell a [Control] node to ignore mouse or touch events. You'll need it if you place an icon on top of a button.
- [Theme] resources change the Control's appearance. If you change the [Theme] on a [Control] node, it affects all of its children. To override some of the theme's parameters, call one of the [code]add_theme_*_override[/code] methods, like [method add_theme_font_override]. You can override the theme with the inspector.
+ [Theme] resources change the Control's appearance. If you change the [Theme] on a [Control] node, it affects all of its children. To override some of the theme's parameters, call one of the [code]add_theme_*_override[/code] methods, like [method add_theme_font_override]. You can override the theme with the Inspector.
[b]Note:[/b] Theme items are [i]not[/i] [Object] properties. This means you can't access their values using [method Object.get] and [method Object.set]. Instead, use the [code]get_theme_*[/code] and [code]add_theme_*_override[/code] methods provided by this class.
</description>
<tutorials>
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 85d9fd34ca..de1a92949f 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="DisplayServer" inherits="Object" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Singleton for window management functions.
</brief_description>
<description>
+ [DisplayServer] handles everything related to window management. This is separated from [OS] as a single operating system may support multiple display servers.
+ [b]Headless mode:[/b] Starting the engine with the [code]--headless[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url] disables all rendering and window management functions. Most functions from [DisplayServer] will return dummy values in this case.
</description>
<tutorials>
</tutorials>
@@ -16,8 +19,8 @@
<method name="clipboard_get_primary" qualifiers="const">
<return type="String" />
<description>
- Returns the user's primary clipboard as a string if possible.
- [b]Note:[/b] This method is only implemented on Linux.
+ Returns the user's [url=https://unix.stackexchange.com/questions/139191/whats-the-difference-between-primary-selection-and-clipboard-buffer]primary[/url] clipboard as a string if possible. This is the clipboard that is set when the user selects text in any application, rather than when pressing [kbd]Ctrl + C[/kbd]. The clipboard data can then be pasted by clicking the middle mouse button in any application that supports the primary clipboard mechanism.
+ [b]Note:[/b] This method is only implemented on Linux (X11).
</description>
</method>
<method name="clipboard_has" qualifiers="const">
@@ -37,8 +40,8 @@
<return type="void" />
<param index="0" name="clipboard_primary" type="String" />
<description>
- Sets the user's primary clipboard content to the given string.
- [b]Note:[/b] This method is only implemented on Linux.
+ Sets the user's [url=https://unix.stackexchange.com/questions/139191/whats-the-difference-between-primary-selection-and-clipboard-buffer]primary[/url] clipboard content to the given string. This is the clipboard that is set when the user selects text in any application, rather than when pressing [kbd]Ctrl + C[/kbd]. The clipboard data can then be pasted by clicking the middle mouse button in any application that supports the primary clipboard mechanism.
+ [b]Note:[/b] This method is only implemented on Linux (X11).
</description>
</method>
<method name="create_sub_window">
@@ -48,11 +51,13 @@
<param index="2" name="flags" type="int" />
<param index="3" name="rect" type="Rect2i" default="Rect2i(0, 0, 0, 0)" />
<description>
+ Create a new subwindow (a [Window] whose presence is linked to another window). This window will appear to be embedded within the main window if [member Viewport.gui_embed_subwindows] is [code]true[/code]. Otherwise, the window will appear to be a separate window which can be dragged outside the main window. [code]vsync_mode[/code] is ignored if [member Viewport.gui_embed_subwindows] is [code]true[/code], as the subwindow will be drawn at the same time as the main window in this case.
</description>
</method>
<method name="cursor_get_shape" qualifiers="const">
<return type="int" enum="DisplayServer.CursorShape" />
<description>
+ Returns the default mouse cursor shape set by [method cursor_set_shape].
</description>
</method>
<method name="cursor_set_custom_image">
@@ -61,18 +66,21 @@
<param index="1" name="shape" type="int" enum="DisplayServer.CursorShape" default="0" />
<param index="2" name="hotspot" type="Vector2" default="Vector2(0, 0)" />
<description>
+ Sets a custom mouse cursor image for the defined [param shape]. This means the user's operating system and mouse cursor theme will no longer influence the mouse cursor's appearance. The image must be [code]256x256[/code] or smaller for correct appearance. [param hotspot] can optionally be set to define the area where the cursor will click. By default, [param hotspot] is set to [code]Vector2(0, 0)[/code], which is the top-left corner of the image. See also [method cursor_set_shape].
</description>
</method>
<method name="cursor_set_shape">
<return type="void" />
<param index="0" name="shape" type="int" enum="DisplayServer.CursorShape" />
<description>
+ Sets the default mouse cursor shape. The cursor's appearance will vary depending on the user's operating system and mouse cursor theme. See also [method cursor_get_shape] and [method cursor_set_custom_image].
</description>
</method>
<method name="delete_sub_window">
<return type="void" />
<param index="0" name="window_id" type="int" />
<description>
+ Removes the subwindow specified by [param window_id].
</description>
</method>
<method name="dialog_input_text">
@@ -82,6 +90,8 @@
<param index="2" name="existing_text" type="String" />
<param index="3" name="callback" type="Callable" />
<description>
+ Shows a text input dialog which uses the operating system's native look-and-feel. [param callback] will be called with a [String] argument equal to the text field's contents when the dialog is closed for any reason.
+ [b]Note:[/b] This method is implemented on macOS.
</description>
</method>
<method name="dialog_show">
@@ -91,17 +101,23 @@
<param index="2" name="buttons" type="PackedStringArray" />
<param index="3" name="callback" type="Callable" />
<description>
+ Shows a text dialog which uses the operating system's native look-and-feel. [param callback] will be called when the dialog is closed for any reason.
+ [b]Note:[/b] This method is implemented on macOS.
</description>
</method>
<method name="enable_for_stealing_focus">
<return type="void" />
<param index="0" name="process_id" type="int" />
<description>
+ Allows the [param process_id] PID to steal focus from this window. In other words, this disables the operating system's focus stealing protection for the specified PID.
+ [b]Note:[/b] This method is implemented on Windows.
</description>
</method>
<method name="force_process_and_drop_events">
<return type="void" />
<description>
+ Forces window manager processing while ignoring all [InputEvent]s. See also [method process_events].
+ [b]Note:[/b] This method is implemented on Windows and macOS.
</description>
</method>
<method name="get_accent_color" qualifiers="const">
@@ -127,27 +143,43 @@
<method name="get_name" qualifiers="const">
<return type="String" />
<description>
+ Returns the name of the [DisplayServer] currently in use. Most operating systems only have a single [DisplayServer], but Linux has access to more than one [DisplayServer] (although only X11 is currently implemented in Godot).
+ The names of built-in display servers are [code]Windows[/code], [code]macOS[/code], [code]X11[/code] (Linux), [code]Android[/code], [code]iOS[/code], [code]web[/code] (HTML5) and [code]headless[/code] (when started with the [code]--headless[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]).
</description>
</method>
<method name="get_screen_count" qualifiers="const">
<return type="int" />
<description>
+ Returns the number of displays available.
</description>
</method>
<method name="get_swap_cancel_ok">
<return type="bool" />
<description>
+ Returns [code]true[/code] if positions of [b]OK[/b] and [b]Cancel[/b] buttons are swapped in dialogs. This is enabled by default on Windows and UWP to follow interface conventions, and be toggled by changing [member ProjectSettings.gui/common/swap_cancel_ok].
+ [b]Note:[/b] This doesn't affect native dialogs such as the ones spawned by [method DisplayServer.dialog_show].
</description>
</method>
<method name="get_window_at_screen_position" qualifiers="const">
<return type="int" />
<param index="0" name="position" type="Vector2i" />
<description>
+ Returns the ID of the window at the specified screen [param position] (in pixels). On multi-monitor setups, the screen position is relative to the virtual desktop area. On multi-monitor setups with different screen resolutions or orientations, the origin may be located outside any display like this:
+ [codeblock]
+ * (0, 0) +-------+
+ | |
+ +-------------+ | |
+ | | | |
+ | | | |
+ +-------------+ +-------+
+ [/codeblock]
</description>
</method>
<method name="get_window_list" qualifiers="const">
<return type="PackedInt32Array" />
<description>
+ Returns the list of Godot window IDs belonging to this process.
+ [b]Note:[/b] Native dialogs are not included in this list.
</description>
</method>
<method name="global_menu_add_check_item">
@@ -665,37 +697,42 @@
<return type="bool" />
<param index="0" name="feature" type="int" enum="DisplayServer.Feature" />
<description>
+ Returns [code]true[/code] if the specified [param feature] is supported by the current [DisplayServer], [code]false[/code] otherwise.
</description>
</method>
<method name="ime_get_selection" qualifiers="const">
<return type="Vector2i" />
<description>
+ Returns the text selection in the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] composition string, with the [Vector2i]'s [code]x[/code] component being the caret position and [code]y[/code] being the length of the selection.
+ [b]Note:[/b] This method is implemented on macOS.
</description>
</method>
<method name="ime_get_text" qualifiers="const">
<return type="String" />
<description>
+ Returns the composition string contained within the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] window.
+ [b]Note:[/b] This method is implemented on macOS.
</description>
</method>
<method name="is_dark_mode" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if OS is using dark mode.
- [b]Note:[/b] This method is implemented on macOS, Windows and Linux.
+ [b]Note:[/b] This method is implemented on macOS, Windows and Linux (X11).
</description>
</method>
<method name="is_dark_mode_supported" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if OS supports dark mode.
- [b]Note:[/b] This method is implemented on macOS, Windows and Linux.
+ [b]Note:[/b] This method is implemented on macOS, Windows and Linux (X11).
</description>
</method>
<method name="keyboard_get_current_layout" qualifiers="const">
<return type="int" />
<description>
Returns active keyboard layout index.
- [b]Note:[/b] This method is implemented on Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows.
</description>
</method>
<method name="keyboard_get_keycode_from_physical" qualifiers="const">
@@ -703,14 +740,14 @@
<param index="0" name="keycode" type="int" enum="Key" />
<description>
Converts a physical (US QWERTY) [param keycode] to one in the active keyboard layout.
- [b]Note:[/b] This method is implemented on Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows.
</description>
</method>
<method name="keyboard_get_layout_count" qualifiers="const">
<return type="int" />
<description>
Returns the number of keyboard layouts.
- [b]Note:[/b] This method is implemented on Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows.
</description>
</method>
<method name="keyboard_get_layout_language" qualifiers="const">
@@ -718,7 +755,7 @@
<param index="0" name="index" type="int" />
<description>
Returns the ISO-639/BCP-47 language code of the keyboard layout at position [param index].
- [b]Note:[/b] This method is implemented on Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows.
</description>
</method>
<method name="keyboard_get_layout_name" qualifiers="const">
@@ -726,25 +763,27 @@
<param index="0" name="index" type="int" />
<description>
Returns the localized name of the keyboard layout at position [param index].
- [b]Note:[/b] This method is implemented on Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows.
</description>
</method>
<method name="keyboard_set_current_layout">
<return type="void" />
<param index="0" name="index" type="int" />
<description>
- Sets active keyboard layout.
- [b]Note:[/b] This method is implemented on Linux, macOS and Windows.
+ Sets the active keyboard layout.
+ [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows.
</description>
</method>
<method name="mouse_get_button_state" qualifiers="const">
<return type="int" enum="MouseButton" />
<description>
+ Returns the current state of mouse buttons (whether each button is pressed) as a bitmask. If multiple mouse buttons are pressed at the same time, the bits are added together. Equivalent to [method Input.get_mouse_button_mask].
</description>
</method>
<method name="mouse_get_mode" qualifiers="const">
<return type="int" enum="DisplayServer.MouseMode" />
<description>
+ Returns the current mouse mode. See also [method mouse_set_mode].
</description>
</method>
<method name="mouse_get_position" qualifiers="const">
@@ -757,11 +796,13 @@
<return type="void" />
<param index="0" name="mouse_mode" type="int" enum="DisplayServer.MouseMode" />
<description>
+ Sets the current mouse mode. See also [method mouse_get_mode].
</description>
</method>
<method name="process_events">
<return type="void" />
<description>
+ Perform window manager processing, including input flushing. See also [method force_process_and_drop_events], [method Input.flush_buffered_events] and [member Input.use_accumulated_input].
</description>
</method>
<method name="screen_get_dpi" qualifiers="const">
@@ -779,7 +820,7 @@
xxhdpi - 480 dpi
xxxhdpi - 640 dpi
[/codeblock]
- [b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows. Returns [code]72[/code] on unsupported platforms.
+ [b]Note:[/b] This method is implemented on Android, Linux (X11), macOS and Windows. Returns [code]72[/code] on unsupported platforms.
</description>
</method>
<method name="screen_get_max_scale" qualifiers="const">
@@ -794,12 +835,24 @@
<return type="int" enum="DisplayServer.ScreenOrientation" />
<param index="0" name="screen" type="int" default="-1" />
<description>
+ Returns the [param screen]'s current orientation. See also [method screen_set_orientation].
+ [b]Note:[/b] This method is implemented on Android and iOS.
</description>
</method>
<method name="screen_get_position" qualifiers="const">
<return type="Vector2i" />
<param index="0" name="screen" type="int" default="-1" />
<description>
+ Returns the screen's top-left corner position in pixels. On multi-monitor setups, the screen position is relative to the virtual desktop area. On multi-monitor setups with different screen resolutions or orientations, the origin may be located outside any display like this:
+ [codeblock]
+ * (0, 0) +-------+
+ | |
+ +-------------+ | |
+ | | | |
+ | | | |
+ +-------------+ +-------+
+ [/codeblock]
+ See also [method screen_get_size].
</description>
</method>
<method name="screen_get_refresh_rate" qualifiers="const">
@@ -829,29 +882,34 @@
<return type="Vector2i" />
<param index="0" name="screen" type="int" default="-1" />
<description>
+ Returns the screen's size in pixels. See also [method screen_get_position] and [method screen_get_usable_rect].
</description>
</method>
<method name="screen_get_usable_rect" qualifiers="const">
<return type="Rect2i" />
<param index="0" name="screen" type="int" default="-1" />
<description>
+ Returns the portion of the screen that is not obstructed by a status bar in pixels. See also [method screen_get_size].
</description>
</method>
<method name="screen_is_kept_on" qualifiers="const">
<return type="bool" />
<description>
+ Returns [code]true[/code] if the screen should never be turned off by the operating system's power-saving measures. See also [method screen_set_keep_on].
</description>
</method>
<method name="screen_is_touchscreen" qualifiers="const">
<return type="bool" />
<param index="0" name="screen" type="int" default="-1" />
<description>
+ Returns [code]true[/code] if the screen can send touch events or if [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is [code]true[/code].
</description>
</method>
<method name="screen_set_keep_on">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
+ Sets whether the screen should never be turned off by the operating system's power-saving measures. See also [method screen_is_kept_on].
</description>
</method>
<method name="screen_set_orientation">
@@ -859,18 +917,21 @@
<param index="0" name="orientation" type="int" enum="DisplayServer.ScreenOrientation" />
<param index="1" name="screen" type="int" default="-1" />
<description>
+ Sets the [param screen]'s [param orientation]. See also [method screen_get_orientation].
</description>
</method>
<method name="set_icon">
<return type="void" />
<param index="0" name="image" type="Image" />
<description>
+ Sets the window icon (usually displayed in the top-left corner) in the operating system's [i]native[/i] format. To use icons in the operating system's native format, use [method set_native_icon] instead.
</description>
</method>
<method name="set_native_icon">
<return type="void" />
<param index="0" name="filename" type="String" />
<description>
+ Sets the window icon (usually displayed in the top-left corner) in the operating system's [i]native[/i] format. The file at [param filename] must be in [code].ico[/code] format on Windows or [code].icns[/code] on macOS. By using specially crafted [code].ico[/code] or [code].icns[/code] icons, [method set_native_icon] allows specifying different icons depending on the size the icon is displayed at. This size is determined by the operating system and user preferences (including the display scale factor). To use icons in other formats, use [method set_icon] instead.
</description>
</method>
<method name="tablet_get_current_driver" qualifiers="const">
@@ -911,7 +972,7 @@
- [code]name[/code] is voice name.
- [code]id[/code] is voice identifier.
- [code]language[/code] is language code in [code]lang_Variant[/code] format. [code]lang[/code] part is a 2 or 3-letter code based on the ISO-639 standard, in lowercase. And [code]Variant[/code] part is an engine dependent string describing country, region or/and dialect.
- [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
</description>
</method>
<method name="tts_get_voices_for_language" qualifiers="const">
@@ -919,35 +980,35 @@
<param index="0" name="language" type="String" />
<description>
Returns an [PackedStringArray] of voice identifiers for the [param language].
- [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
</description>
</method>
<method name="tts_is_paused" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the synthesizer is in a paused state.
- [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
</description>
</method>
<method name="tts_is_speaking" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the synthesizer is generating speech, or have utterance waiting in the queue.
- [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
</description>
</method>
<method name="tts_pause">
<return type="void" />
<description>
Puts the synthesizer into a paused state.
- [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
</description>
</method>
<method name="tts_resume">
<return type="void" />
<description>
Resumes the synthesizer if it was paused.
- [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
</description>
</method>
<method name="tts_set_utterance_callback">
@@ -959,7 +1020,7 @@
- [code]TTS_UTTERANCE_STARTED[/code], [code]TTS_UTTERANCE_ENDED[/code], and [code]TTS_UTTERANCE_CANCELED[/code] callable's method should take one [int] parameter, the utterance id.
- [code]TTS_UTTERANCE_BOUNDARY[/code] callable's method should take two [int] parameters, the index of the character and the utterance id.
[b]Note:[/b] The granularity of the boundary callbacks is engine dependent.
- [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
</description>
</method>
<method name="tts_speak">
@@ -978,16 +1039,16 @@
- [param pitch] ranges from [code]0.0[/code] (lowest) to [code]2.0[/code] (highest), [code]1.0[/code] is default pitch for the current voice.
- [param rate] ranges from [code]0.1[/code] (lowest) to [code]10.0[/code] (highest), [code]1.0[/code] is a normal speaking rate. Other values act as a percentage relative.
- [param utterance_id] is passed as a parameter to the callback functions.
- [b]Note:[/b] On Windows and Linux, utterance [param text] can use SSML markup. SSML support is engine and voice dependent. If the engine does not support SSML, you should strip out all XML markup before calling [method tts_speak].
+ [b]Note:[/b] On Windows and Linux (X11), utterance [param text] can use SSML markup. SSML support is engine and voice dependent. If the engine does not support SSML, you should strip out all XML markup before calling [method tts_speak].
[b]Note:[/b] The granularity of pitch, rate, and volume is engine and voice dependent. Values may be truncated.
- [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
</description>
</method>
<method name="tts_stop">
<return type="void" />
<description>
Stops synthesis in progress and removes all utterances from the queue.
- [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows.
</description>
</method>
<method name="virtual_keyboard_get_height" qualifiers="const">
@@ -1033,12 +1094,14 @@
<param index="0" name="instance_id" type="int" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Sets the [Window] instance ID ([method Object.get_instance_id]) the [param window_id] should be attached to. See also [method window_get_attached_instance_id].
</description>
</method>
<method name="window_can_draw" qualifiers="const">
<return type="bool" />
<param index="0" name="window_id" type="int" default="0" />
<description>
+ Returns [code]true[/code] if anything can be drawn in the window specified by [param window_id], [code]false[/code] otherwise. Using the [code]--disable-render-loop[/code] command line argument or a headless build will return [code]false[/code].
</description>
</method>
<method name="window_get_active_popup" qualifiers="const">
@@ -1051,12 +1114,14 @@
<return type="int" />
<param index="0" name="window_id" type="int" default="0" />
<description>
+ Returns the [method Object.get_instance_id] of the [Window] the [param window_id] is attached to. also [method window_get_attached_instance_id].
</description>
</method>
<method name="window_get_current_screen" qualifiers="const">
<return type="int" />
<param index="0" name="window_id" type="int" default="0" />
<description>
+ Returns the screen the window specified by [param window_id] is currently positioned on. If the screen overlaps multiple displays, the screen where the window's center is located is returned. See also [method window_set_current_screen].
</description>
</method>
<method name="window_get_flag" qualifiers="const">
@@ -1071,12 +1136,14 @@
<return type="Vector2i" />
<param index="0" name="window_id" type="int" default="0" />
<description>
+ Returns the window's maximum size (in pixels). See also [method window_set_max_size].
</description>
</method>
<method name="window_get_min_size" qualifiers="const">
<return type="Vector2i" />
<param index="0" name="window_id" type="int" default="0" />
<description>
+ Returns the window's minimum size (in pixels). See also [method window_set_min_size].
</description>
</method>
<method name="window_get_mode" qualifiers="const">
@@ -1092,7 +1159,7 @@
<param index="1" name="window_id" type="int" default="0" />
<description>
Returns internal structure pointers for use in plugins.
- [b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Android, Linux (X11), macOS and Windows.
</description>
</method>
<method name="window_get_popup_safe_rect" qualifiers="const">
@@ -1113,6 +1180,7 @@
<return type="Vector2i" />
<param index="0" name="window_id" type="int" default="0" />
<description>
+ Returns the size of the window specified by [param window_id] (in pixels), including the borders drawn by the operating system. See also [method window_get_size].
</description>
</method>
<method name="window_get_safe_title_margins" qualifiers="const">
@@ -1126,6 +1194,7 @@
<return type="Vector2i" />
<param index="0" name="window_id" type="int" default="0" />
<description>
+ Returns the size of the window specified by [param window_id] (in pixels), excluding the borders drawn by the operating system. This is also called the "client area". See also [method window_get_real_size], [method window_set_size] and [method window_get_position].
</description>
</method>
<method name="window_get_vsync_mode" qualifiers="const">
@@ -1153,12 +1222,14 @@
<return type="void" />
<param index="0" name="window_id" type="int" default="0" />
<description>
+ Moves the window specified by [param window_id] to the foreground, so that it is visible over other windows.
</description>
</method>
<method name="window_request_attention">
<return type="void" />
<param index="0" name="window_id" type="int" default="0" />
<description>
+ Makes the window specified by [param window_id] request attention, which is materialized by the window title and taskbar entry blinking until the window is focused. This usually has no visible effect if the window is currently focused. The exact behavior varies depending on the operating system.
</description>
</method>
<method name="window_set_current_screen">
@@ -1166,6 +1237,7 @@
<param index="0" name="screen" type="int" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Moves the window specified by [param window_id] to the specified [param screen]. See also [method window_get_current_screen].
</description>
</method>
<method name="window_set_drop_files_callback">
@@ -1173,6 +1245,8 @@
<param index="0" name="callback" type="Callable" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Sets the [param callback] that should be called when files are dropped from the operating system's file manager to the window specified by [param window_id].
+ [b]Note:[/b] This method is implemented on Windows, macOS, Linux (X11) and Web.
</description>
</method>
<method name="window_set_exclusive">
@@ -1199,6 +1273,7 @@
<param index="0" name="active" type="bool" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Sets whether [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] should be enabled for the window specified by [param window_id]. See also [method window_set_ime_position].
</description>
</method>
<method name="window_set_ime_position">
@@ -1206,6 +1281,7 @@
<param index="0" name="position" type="Vector2i" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Sets the position of the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] popup for the specified [param window_id]. Only effective if [method window_set_ime_active] was set to [code]true[/code] for the specified [param window_id].
</description>
</method>
<method name="window_set_input_event_callback">
@@ -1213,6 +1289,7 @@
<param index="0" name="callback" type="Callable" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Sets the [param callback] that should be called when any [InputEvent] is sent to the window specified by [param window_id].
</description>
</method>
<method name="window_set_input_text_callback">
@@ -1220,6 +1297,7 @@
<param index="0" name="callback" type="Callable" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Sets the [param callback] that should be called when text is entered using the virtual keyboard to the window specified by [param window_id].
</description>
</method>
<method name="window_set_max_size">
@@ -1227,6 +1305,8 @@
<param index="0" name="max_size" type="Vector2i" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Sets the maximum size of the window specified by [param window_id] in pixels. Normally, the user will not be able to drag the window to make it smaller than the specified size. See also [method window_get_max_size].
+ [b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit.
</description>
</method>
<method name="window_set_min_size">
@@ -1234,8 +1314,9 @@
<param index="0" name="min_size" type="Vector2i" />
<param index="1" name="window_id" type="int" default="0" />
<description>
- Sets the minimum size for the given window to [param min_size] (in pixels).
+ Sets the minimum size for the given window to [param min_size] (in pixels). Normally, the user will not be able to drag the window to make it larger than the specified size. See also [method window_get_min_size].
[b]Note:[/b] By default, the main window has a minimum size of [code]Vector2i(64, 64)[/code]. This prevents issues that can arise when the window is resized to a near-zero size.
+ [b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit.
</description>
</method>
<method name="window_set_mode">
@@ -1276,8 +1357,8 @@
DisplayServer.WindowSetMousePassthrough(new Vector2[] {});
[/csharp]
[/codeblocks]
- [b]Note:[/b] On Windows, the portion of a window that lies outside the region is not drawn, while on Linux and macOS it is.
- [b]Note:[/b] This method is implemented on Linux, macOS and Windows.
+ [b]Note:[/b] On Windows, the portion of a window that lies outside the region is not drawn, while on Linux (X11) and macOS it is.
+ [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows.
</description>
</method>
<method name="window_set_popup_safe_rect">
@@ -1293,7 +1374,16 @@
<param index="0" name="position" type="Vector2i" />
<param index="1" name="window_id" type="int" default="0" />
<description>
- Sets the position of the given window to [param position].
+ Sets the position of the given window to [param position]. On multi-monitor setups, the screen position is relative to the virtual desktop area. On multi-monitor setups with different screen resolutions or orientations, the origin may be located outside any display like this:
+ [codeblock]
+ * (0, 0) +-------+
+ | |
+ +-------------+ | |
+ | | | |
+ | | | |
+ +-------------+ +-------+
+ [/codeblock]
+ See also [method window_get_position] and [method window_set_size].
</description>
</method>
<method name="window_set_rect_changed_callback">
@@ -1301,6 +1391,7 @@
<param index="0" name="callback" type="Callable" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Sets the [param callback] that will be called when the window specified by [param window_id] is moved or resized.
</description>
</method>
<method name="window_set_size">
@@ -1308,7 +1399,7 @@
<param index="0" name="size" type="Vector2i" />
<param index="1" name="window_id" type="int" default="0" />
<description>
- Sets the size of the given window to [param size].
+ Sets the size of the given window to [param size] (in pixels). See also [method window_get_size] and [method window_get_position].
</description>
</method>
<method name="window_set_title">
@@ -1317,6 +1408,7 @@
<param index="1" name="window_id" type="int" default="0" />
<description>
Sets the title of the given window to [param title].
+ [b]Note:[/b] Avoid changing the window title every frame, as this can cause performance issues on certain window managers. Try to change the window title only a few times per second at most.
</description>
</method>
<method name="window_set_transient">
@@ -1352,51 +1444,70 @@
<param index="0" name="callback" type="Callable" />
<param index="1" name="window_id" type="int" default="0" />
<description>
+ Sets the [param callback] that will be called when an event occurs in the window specified by [param window_id].
</description>
</method>
</methods>
<constants>
<constant name="FEATURE_GLOBAL_MENU" value="0" enum="Feature">
+ Display server supports global menu. This allows the application to display its menu items in the operating system's top bar. [b]macOS[/b]
</constant>
<constant name="FEATURE_SUBWINDOWS" value="1" enum="Feature">
+ Display server supports multiple windows that can be moved outside of the main window. [b]Windows, macOS, Linux (X11)[/b]
</constant>
<constant name="FEATURE_TOUCHSCREEN" value="2" enum="Feature">
+ Display server supports touchscreen input. [b]Windows, Linux (X11), Android, iOS, Web[/b]
</constant>
<constant name="FEATURE_MOUSE" value="3" enum="Feature">
+ Display server supports mouse input. [b]Windows, macOS, Linux (X11), Android, Web[/b]
</constant>
<constant name="FEATURE_MOUSE_WARP" value="4" enum="Feature">
+ Display server supports warping mouse coordinates to keep the mouse cursor constrained within an area, but looping when one of the edges is reached. [b]Windows, macOS, Linux (X11)[/b]
</constant>
<constant name="FEATURE_CLIPBOARD" value="5" enum="Feature">
+ Display server supports setting and getting clipboard data. See also [constant FEATURE_CLIPBOARD_PRIMARY]. [b]Windows, macOS, Linux (X11), Android, iOS, Web[/b]
</constant>
<constant name="FEATURE_VIRTUAL_KEYBOARD" value="6" enum="Feature">
+ Display server supports popping up a virtual keyboard when requested to input text without a physical keyboard. [b]Android, iOS, Web[/b]
</constant>
<constant name="FEATURE_CURSOR_SHAPE" value="7" enum="Feature">
+ Display server supports setting the mouse cursor shape to be different from the default. [b]Windows, macOS, Linux (X11), Android, Web[/b]
</constant>
<constant name="FEATURE_CUSTOM_CURSOR_SHAPE" value="8" enum="Feature">
+ Display server supports setting the mouse cursor shape to a custom image. [b]Windows, macOS, Linux (X11), Web[/b]
</constant>
<constant name="FEATURE_NATIVE_DIALOG" value="9" enum="Feature">
+ Display server supports spawning dialogs using the operating system's native look-and-feel. [b]macOS[/b]
</constant>
<constant name="FEATURE_IME" value="10" enum="Feature">
+ Display server supports [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url], which is commonly used for inputting Chinese/Japanese/Korean text. This is handled by the operating system, rather than by Godot. [b]Windows, macOS, Linux (X11)[/b]
</constant>
<constant name="FEATURE_WINDOW_TRANSPARENCY" value="11" enum="Feature">
+ Display server supports windows can use per-pixel transparency to make windows behind them partially or fully visible. [b]Windows, macOS, Linux (X11)[/b]
</constant>
<constant name="FEATURE_HIDPI" value="12" enum="Feature">
+ Display server supports querying the operating system's display scale factor. This allows for [i]reliable[/i] automatic hiDPI display detection, as opposed to guessing based on the screen resolution and reported display DPI (which can be unreliable due to broken monitor EDID). [b]Windows, macOS[/b]
</constant>
<constant name="FEATURE_ICON" value="13" enum="Feature">
+ Display server supports changing the window icon (usually displayed in the top-left corner). [b]Windows, macOS, Linux (X11)[/b]
</constant>
<constant name="FEATURE_NATIVE_ICON" value="14" enum="Feature">
+ Display server supports changing the window icon (usually displayed in the top-left corner). [b]Windows, macOS[/b]
</constant>
<constant name="FEATURE_ORIENTATION" value="15" enum="Feature">
+ Display server supports changing the screen orientation. [b]Android, iOS[/b]
</constant>
<constant name="FEATURE_SWAP_BUFFERS" value="16" enum="Feature">
+ Display server supports V-Sync status can be changed from the default (which is forced to be enabled platforms not supporting this feature). [b]Windows, macOS, Linux (X11)[/b]
</constant>
<constant name="FEATURE_CLIPBOARD_PRIMARY" value="18" enum="Feature">
+ Display server supports Primary clipboard can be used. This is a different clipboard from [constant FEATURE_CLIPBOARD]. [b]Linux (X11)[/b]
</constant>
<constant name="FEATURE_TEXT_TO_SPEECH" value="19" enum="Feature">
- Display server supports text-to-speech. See [code]tts_*[/code] methods.
+ Display server supports text-to-speech. See [code]tts_*[/code] methods. [b]Windows, macOS, Linux (X11), Android, iOS, Web[/b]
</constant>
<constant name="FEATURE_EXTEND_TO_TITLE" value="20" enum="Feature">
- Display server supports expanding window content to the title. See [constant WINDOW_FLAG_EXTEND_TO_TITLE].
+ Display server supports expanding window content to the title. See [constant WINDOW_FLAG_EXTEND_TO_TITLE]. [b]macOS[/b]
</constant>
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
Makes the mouse cursor visible if it is hidden.
@@ -1415,24 +1526,34 @@
Confines the mouse cursor to the game window, and make it hidden.
</constant>
<constant name="SCREEN_OF_MAIN_WINDOW" value="-1">
+ Represents the screen where the main window is located. This is usually the default value in functions that allow specifying one of several screens.
</constant>
<constant name="MAIN_WINDOW_ID" value="0">
+ The ID of the main window spawned by the engine, which can be passed to methods expecting a [code]window_id[/code].
</constant>
<constant name="INVALID_WINDOW_ID" value="-1">
+ The ID that refers to a nonexisting window. This is be returned by some [DisplayServer] methods if no window matches the requested result.
</constant>
<constant name="SCREEN_LANDSCAPE" value="0" enum="ScreenOrientation">
+ Default landscape orientation.
</constant>
<constant name="SCREEN_PORTRAIT" value="1" enum="ScreenOrientation">
+ Default portrait orienstation.
</constant>
<constant name="SCREEN_REVERSE_LANDSCAPE" value="2" enum="ScreenOrientation">
+ Reverse landscape orientation (upside down).
</constant>
<constant name="SCREEN_REVERSE_PORTRAIT" value="3" enum="ScreenOrientation">
+ Reverse portrait orientation (upside down).
</constant>
<constant name="SCREEN_SENSOR_LANDSCAPE" value="4" enum="ScreenOrientation">
+ Automatic landscape orientation (default or reverse depending on sensor).
</constant>
<constant name="SCREEN_SENSOR_PORTRAIT" value="5" enum="ScreenOrientation">
+ Automatic portrait orientation (default or reverse depending on sensor).
</constant>
<constant name="SCREEN_SENSOR" value="6" enum="ScreenOrientation">
+ Automatic landscape or portrait orientation (default or reverse depending on sensor).
</constant>
<constant name="KEYBOARD_TYPE_DEFAULT" value="0" enum="VirtualKeyboardType">
Default text virtual keyboard.
@@ -1460,40 +1581,58 @@
Virtual keyboard with additional keys to assist with typing URLs.
</constant>
<constant name="CURSOR_ARROW" value="0" enum="CursorShape">
+ Arrow cursor shape. This is the default when not pointing anything that overrides the mouse cursor, such as a [LineEdit] or [TextEdit].
</constant>
<constant name="CURSOR_IBEAM" value="1" enum="CursorShape">
+ I-beam cursor shape. This is used by default when hovering a control that accepts text input, such as [LineEdit] or [TextEdit].
</constant>
<constant name="CURSOR_POINTING_HAND" value="2" enum="CursorShape">
+ Pointing hand cursor shape. This is used by default when hovering a [LinkButton] or an URL tag in a [RichTextLabel].â‹…
</constant>
<constant name="CURSOR_CROSS" value="3" enum="CursorShape">
+ Crosshair cursor. This is intended to be displayed when the user needs precise aim over an element, such as a rectangle selection tool or a color picker.
</constant>
<constant name="CURSOR_WAIT" value="4" enum="CursorShape">
+ Wait cursor. On most cursor themes, this displays a spinning icon [i]besides[/i] the arrow. Intended to be used for non-blocking operations (when the user can do something else at the moment). See also [constant CURSOR_BUSY].
</constant>
<constant name="CURSOR_BUSY" value="5" enum="CursorShape">
+ Wait cursor. On most cursor themes, this [i]replaces[/i] the arrow with a spinning icon. Intended to be used for blocking operations (when the user can't do anything else at the moment). See also [constant CURSOR_WAIT].
</constant>
<constant name="CURSOR_DRAG" value="6" enum="CursorShape">
+ Dragging hand cursor. This is displayed during drag-and-drop operations. See also [constant CURSOR_CAN_DROP].
</constant>
<constant name="CURSOR_CAN_DROP" value="7" enum="CursorShape">
+ "Can drop" cursor. This is displayed during drag-and-drop operations if hovering over a [Control] that can accept the drag-and-drop event. On most cursor themes, this displays a dragging hand with an arrow symbol besides it. See also [constant CURSOR_DRAG].
</constant>
<constant name="CURSOR_FORBIDDEN" value="8" enum="CursorShape">
+ Forbidden cursor. This is displayed during drag-and-drop operations if the hovered [Control] can't accept the drag-and-drop event.
</constant>
<constant name="CURSOR_VSIZE" value="9" enum="CursorShape">
+ Vertical resize cursor. Intended to be displayed when the hovered [Control] can be vertically resized using the mouse. See also [constant CURSOR_VSPLIT].
</constant>
<constant name="CURSOR_HSIZE" value="10" enum="CursorShape">
+ Horizontal resize cursor. Intended to be displayed when the hovered [Control] can be horizontally resized using the mouse. See also [constant CURSOR_HSPLIT].
</constant>
<constant name="CURSOR_BDIAGSIZE" value="11" enum="CursorShape">
+ Secondary diagonal resize cursor (top-right/bottom-left). Intended to be displayed when the hovered [Control] can be resized on both axes at once using the mouse.
</constant>
<constant name="CURSOR_FDIAGSIZE" value="12" enum="CursorShape">
+ Main diagonal resize cursor (top-left/bottom-right). Intended to be displayed when the hovered [Control] can be resized on both axes at once using the mouse.
</constant>
<constant name="CURSOR_MOVE" value="13" enum="CursorShape">
+ Move cursor. Intended to be displayed when the hovered [Control] can be moved using the mouse.
</constant>
<constant name="CURSOR_VSPLIT" value="14" enum="CursorShape">
+ Vertical split cursor. This is displayed when hovering a [Control] with splits that can be vertically resized using the mouse, such as [VSplitContainer]. On some cursor themes, this cursor may have the same appearance as [constant CURSOR_VSIZE].
</constant>
<constant name="CURSOR_HSPLIT" value="15" enum="CursorShape">
+ Horizontal split cursor. This is displayed when hovering a [Control] with splits that can be horizontally resized using the mouse, such as [HSplitContainer]. On some cursor themes, this cursor may have the same appearance as [constant CURSOR_HSIZE].
</constant>
<constant name="CURSOR_HELP" value="16" enum="CursorShape">
+ Help cursor. On most cursor themes, this displays a question mark icon instead of the mouse cursor. Intended to be used when the user has requested help on the next element that will be clicked.
</constant>
<constant name="CURSOR_MAX" value="17" enum="CursorShape">
+ Represents the size of the [enum CursorShape] enum.
</constant>
<constant name="WINDOW_MODE_WINDOWED" value="0" enum="WindowMode">
Windowed mode, i.e. [Window] doesn't occupy the whole screen (unless set to the size of the screen).
@@ -1505,7 +1644,7 @@
Maximized window mode, i.e. [Window] will occupy whole screen area except task bar and still display its borders. Normally happens when the minimize button is pressed.
</constant>
<constant name="WINDOW_MODE_FULLSCREEN" value="3" enum="WindowMode">
- Full screen window mode. Note that this is not [i]exclusive[/i] full screen. On Windows and Linux, a borderless window is used to emulate full screen. On macOS, a new desktop is used to display the running project.
+ Full screen window mode. Note that this is not [i]exclusive[/i] full screen. On Windows and Linux (X11), a borderless window is used to emulate full screen. On macOS, a new desktop is used to display the running project.
Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode.
</constant>
<constant name="WINDOW_MODE_EXCLUSIVE_FULLSCREEN" value="4" enum="WindowMode">
@@ -1525,7 +1664,7 @@
<constant name="WINDOW_FLAG_TRANSPARENT" value="3" enum="WindowFlags">
The window background can be transparent.
[b]Note:[/b] This flag has no effect if [member ProjectSettings.display/window/per_pixel_transparency/allowed] is set to [code]false[/code].
- [b]Note:[/b] Transparency support is implemented on Linux, macOS and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities.
+ [b]Note:[/b] Transparency support is implemented on Linux (X11), macOS and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities.
</constant>
<constant name="WINDOW_FLAG_NO_FOCUS" value="4" enum="WindowFlags">
The window can't be focused. No-focus window will ignore all input, except mouse clicks.
@@ -1570,26 +1709,26 @@
[b]Note:[/b] This flag is implemented on macOS.
</constant>
<constant name="VSYNC_DISABLED" value="0" enum="VSyncMode">
- No vertical synchronization, which means the engine will display frames as fast as possible (tearing may be visible).
+ No vertical synchronization, which means the engine will display frames as fast as possible (tearing may be visible). Framerate is unlimited (nonwithstanding [member Engine.max_fps]).
</constant>
<constant name="VSYNC_ENABLED" value="1" enum="VSyncMode">
- Default vertical synchronization mode, the image is displayed only on vertical blanking intervals (no tearing is visible).
+ Default vertical synchronization mode, the image is displayed only on vertical blanking intervals (no tearing is visible). Framerate is limited by the monitor refresh rate (nonwithstanding [member Engine.max_fps]).
</constant>
<constant name="VSYNC_ADAPTIVE" value="2" enum="VSyncMode">
- Behaves like [constant VSYNC_DISABLED] when the framerate drops below the screen's refresh rate to reduce stuttering (tearing may be visible), otherwise vertical synchronization is enabled to avoid tearing.
+ Behaves like [constant VSYNC_DISABLED] when the framerate drops below the screen's refresh rate to reduce stuttering (tearing may be visible). Otherwise, vertical synchronization is enabled to avoid tearing. Framerate is limited by the monitor refresh rate (nonwithstanding [member Engine.max_fps]).
</constant>
<constant name="VSYNC_MAILBOX" value="3" enum="VSyncMode">
- Displays the most recent image in the queue on vertical blanking intervals, while rendering to the other images (no tearing is visible).
- Although not guaranteed, the images can be rendered as fast as possible, which may reduce input lag.
+ Displays the most recent image in the queue on vertical blanking intervals, while rendering to the other images (no tearing is visible). Framerate is unlimited (nonwithstanding [member Engine.max_fps]).
+ Although not guaranteed, the images can be rendered as fast as possible, which may reduce input lag (also called "Fast" V-Sync mode). [constant VSYNC_MAILBOX] works best when at least twice as many frames as the display refresh rate are rendered.
</constant>
<constant name="DISPLAY_HANDLE" value="0" enum="HandleType">
Display handle:
- - Linux: [code]X11::Display*[/code] for the display.
+ - Linux (X11): [code]X11::Display*[/code] for the display.
</constant>
<constant name="WINDOW_HANDLE" value="1" enum="HandleType">
Window handle:
- Windows: [code]HWND[/code] for the window.
- - Linux: [code]X11::Window*[/code] for the window.
+ - Linux (X11): [code]X11::Window*[/code] for the window.
- macOS: [code]NSWindow*[/code] for the window.
- iOS: [code]UIViewController*[/code] for the view controller.
- Android: [code]jObject[/code] for the activity.
diff --git a/doc/classes/EditorFeatureProfile.xml b/doc/classes/EditorFeatureProfile.xml
index e216059364..99e97fc25f 100644
--- a/doc/classes/EditorFeatureProfile.xml
+++ b/doc/classes/EditorFeatureProfile.xml
@@ -28,7 +28,7 @@
<return type="bool" />
<param index="0" name="class_name" type="StringName" />
<description>
- Returns [code]true[/code] if editing for the class specified by [param class_name] is disabled. When disabled, the class will still appear in the Create New Node dialog but the inspector will be read-only when selecting a node that extends the class.
+ Returns [code]true[/code] if editing for the class specified by [param class_name] is disabled. When disabled, the class will still appear in the Create New Node dialog but the Inspector will be read-only when selecting a node that extends the class.
</description>
</method>
<method name="is_class_property_disabled" qualifiers="const">
@@ -36,7 +36,7 @@
<param index="0" name="class_name" type="StringName" />
<param index="1" name="property" type="StringName" />
<description>
- Returns [code]true[/code] if [param property] is disabled in the class specified by [param class_name]. When a property is disabled, it won't appear in the inspector when selecting a node that extends the class specified by [param class_name].
+ Returns [code]true[/code] if [param property] is disabled in the class specified by [param class_name]. When a property is disabled, it won't appear in the Inspector when selecting a node that extends the class specified by [param class_name].
</description>
</method>
<method name="is_feature_disabled" qualifiers="const">
@@ -73,7 +73,7 @@
<param index="0" name="class_name" type="StringName" />
<param index="1" name="disable" type="bool" />
<description>
- If [param disable] is [code]true[/code], disables editing for the class specified by [param class_name]. When disabled, the class will still appear in the Create New Node dialog but the inspector will be read-only when selecting a node that extends the class.
+ If [param disable] is [code]true[/code], disables editing for the class specified by [param class_name]. When disabled, the class will still appear in the Create New Node dialog but the Inspector will be read-only when selecting a node that extends the class.
</description>
</method>
<method name="set_disable_class_property">
@@ -82,7 +82,7 @@
<param index="1" name="property" type="StringName" />
<param index="2" name="disable" type="bool" />
<description>
- If [param disable] is [code]true[/code], disables editing for [param property] in the class specified by [param class_name]. When a property is disabled, it won't appear in the inspector when selecting a node that extends the class specified by [param class_name].
+ If [param disable] is [code]true[/code], disables editing for [param property] in the class specified by [param class_name]. When a property is disabled, it won't appear in the Inspector when selecting a node that extends the class specified by [param class_name].
</description>
</method>
<method name="set_disable_feature">
diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml
index a1a43dd5bf..108c670f59 100644
--- a/doc/classes/EditorInspector.xml
+++ b/doc/classes/EditorInspector.xml
@@ -33,7 +33,7 @@
<signal name="object_id_selected">
<param index="0" name="id" type="int" />
<description>
- Emitted when the Edit button of an [Object] has been pressed in the inspector. This is mainly used in the remote scene tree inspector.
+ Emitted when the Edit button of an [Object] has been pressed in the inspector. This is mainly used in the remote scene tree Inspector.
</description>
</signal>
<signal name="property_deleted">
diff --git a/doc/classes/EditorInspectorPlugin.xml b/doc/classes/EditorInspectorPlugin.xml
index c8a499260e..ba2f7b24bf 100644
--- a/doc/classes/EditorInspectorPlugin.xml
+++ b/doc/classes/EditorInspectorPlugin.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorInspectorPlugin" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Plugin for adding custom property editors on inspector.
+ Plugin for adding custom property editors on the inspector.
</brief_description>
<description>
[EditorInspectorPlugin] allows adding custom property editors to [EditorInspector].
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 818dda59bc..799bd45cee 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -196,10 +196,10 @@
The thumbnail size to use in the FileSystem dock (in pixels). See also [member filesystem/file_dialog/thumbnail_size].
</member>
<member name="docks/property_editor/auto_refresh_interval" type="float" setter="" getter="">
- The refresh interval to use for the inspector dock's properties. The effect of this setting is mainly noticeable when adjusting gizmos in the 2D/3D editor and looking at the inspector at the same time. Lower values make the inspector more often, but take up more CPU time.
+ The refresh interval to use for the Inspector dock's properties. The effect of this setting is mainly noticeable when adjusting gizmos in the 2D/3D editor and looking at the inspector at the same time. Lower values make the inspector more often, but take up more CPU time.
</member>
<member name="docks/property_editor/subresource_hue_tint" type="float" setter="" getter="">
- The tint intensity to use for the subresources background in the inspector dock. The tint is used to distinguish between different subresources in the inspector. Higher values result in a more noticeable background color difference.
+ The tint intensity to use for the subresources background in the Inspector dock. The tint is used to distinguish between different subresources in the inspector. Higher values result in a more noticeable background color difference.
</member>
<member name="docks/scene_tree/auto_expand_to_selected" type="bool" setter="" getter="">
If [code]true[/code], the scene tree dock will automatically unfold nodes when a node that has folded parents is selected.
diff --git a/doc/classes/Expression.xml b/doc/classes/Expression.xml
index 3a397f56a9..4670e0c382 100644
--- a/doc/classes/Expression.xml
+++ b/doc/classes/Expression.xml
@@ -12,7 +12,7 @@
var expression = Expression.new()
func _ready():
- $LineEdit.connect("text_submitted", self, "_on_text_submitted")
+ $LineEdit.text_submitted.connect(self._on_text_submitted)
func _on_text_submitted(command):
var error = expression.parse(command)
@@ -28,7 +28,7 @@
public override void _Ready()
{
- GetNode("LineEdit").Connect("text_submitted", this, nameof(OnTextEntered));
+ GetNode("LineEdit").TextSubmitted += OnTextEntered;
}
private void OnTextEntered(string command)
diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml
index 043c08eed5..4916171900 100644
--- a/doc/classes/GPUParticles2D.xml
+++ b/doc/classes/GPUParticles2D.xml
@@ -9,7 +9,8 @@
</description>
<tutorials>
<link title="Particle systems (2D)">$DOCS_URL/tutorials/2d/particle_systems_2d.html</link>
- <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link>
+ <link title="2D Particles Demo">https://godotengine.org/asset-library/asset/118</link>
+ <link title="2D Dodge The Creeps Demo (uses GPUParticles2D for the trail behind the player)">https://godotengine.org/asset-library/asset/515</link>
</tutorials>
<methods>
<method name="capture_rect" qualifiers="const">
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index 56b95fc755..89d52caa02 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -157,7 +157,7 @@
<method name="get_mouse_button_mask" qualifiers="const">
<return type="int" enum="MouseButton" />
<description>
- Returns mouse buttons as a bitmask. If multiple mouse buttons are pressed at the same time, the bits are added together.
+ Returns mouse buttons as a bitmask. If multiple mouse buttons are pressed at the same time, the bits are added together. Equivalent to [method DisplayServer.mouse_get_button_state].
</description>
</method>
<method name="get_vector" qualifiers="const">
diff --git a/doc/classes/MovieWriter.xml b/doc/classes/MovieWriter.xml
index f2509ad2b2..1652dd52aa 100644
--- a/doc/classes/MovieWriter.xml
+++ b/doc/classes/MovieWriter.xml
@@ -4,7 +4,7 @@
Abstract class for non-real-time video recording encoders.
</brief_description>
<description>
- Godot can record videos with non-real-time simulation. Like the [code]--fixed-fps[/code] command line argument, this forces the reported [code]delta[/code] in [method Node._process] functions to be identical across frames, regardless of how long it actually took to render the frame. This can be used to record high-quality videos with perfect frame pacing regardless of your hardware's capabilities.
+ Godot can record videos with non-real-time simulation. Like the [code]--fixed-fps[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url], this forces the reported [code]delta[/code] in [method Node._process] functions to be identical across frames, regardless of how long it actually took to render the frame. This can be used to record high-quality videos with perfect frame pacing regardless of your hardware's capabilities.
Godot has 2 built-in [MovieWriter]s:
- AVI container with MJPEG for video and uncompressed audio ([code].avi[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/mjpeg_quality]. The resulting file can be viewed in most video players, but it must be converted to another format for viewing on the web or by Godot with [VideoStreamPlayer]. MJPEG does not support transparency. AVI output is currently limited to a file of 4 GB in size at most.
- PNG image sequence for video and WAV for audio ([code].png[/code] file extension). Lossless compression, large file sizes, slow encoding. Designed to be encoded to a video file with another tool such as [url=https://ffmpeg.org/]FFmpeg[/url] after recording. Transparency is currently not supported, even if the root viewport is set to be transparent.
@@ -46,7 +46,7 @@
<param index="1" name="fps" type="int" />
<param index="2" name="base_path" type="String" />
<description>
- Called once before the engine starts writing video and audio data. [param movie_size] is the width and height of the video to save. [param fps] is the number of frames per second specified in the project settings or using the [code]--fixed-fps &lt;fps&gt;[/code] command line argument.
+ Called once before the engine starts writing video and audio data. [param movie_size] is the width and height of the video to save. [param fps] is the number of frames per second specified in the project settings or using the [code]--fixed-fps &lt;fps&gt;[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
</description>
</method>
<method name="_write_end" qualifiers="virtual">
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 620895d8d8..bf52c55d67 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -39,7 +39,7 @@
<method name="_get_configuration_warnings" qualifiers="virtual const">
<return type="PackedStringArray" />
<description>
- The elements in the array returned from this method are displayed as warnings in the Scene Dock if the script that overrides it is a [code]tool[/code] script.
+ The elements in the array returned from this method are displayed as warnings in the Scene dock if the script that overrides it is a [code]tool[/code] script.
Returning an empty array produces no warnings.
Call [method update_configuration_warnings] when the warnings need to be updated for this node.
</description>
@@ -560,7 +560,7 @@
<method name="print_tree_pretty">
<return type="void" />
<description>
- Similar to [method print_tree], this prints the tree to stdout. This version displays a more graphical representation similar to what is displayed in the scene inspector. It is useful for inspecting larger trees.
+ Similar to [method print_tree], this prints the tree to stdout. This version displays a more graphical representation similar to what is displayed in the Scene Dock. It is useful for inspecting larger trees.
[b]Example output:[/b]
[codeblock]
â”–â•´TheGame
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index c8491b3a03..0c74690924 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -4,7 +4,8 @@
Operating System functions.
</brief_description>
<description>
- Operating System functions. OS wraps the most common functionality to communicate with the host operating system, such as the clipboard, video driver, delays, environment variables, execution of binaries, command line, etc.
+ Operating System functions. [OS] wraps the most common functionality to communicate with the host operating system, such as the clipboard, video driver, delays, environment variables, execution of binaries, command line, etc.
+ [b]Note:[/b] In Godot 4, [OS] functions related to window management were moved to the [DisplayServer] singleton.
</description>
<tutorials>
<link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link>
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index e663f47ddf..34cf259f63 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -384,7 +384,7 @@
<description>
Returns the object's metadata entry for the given [param name].
Throws error if the entry does not exist, unless [param default] is not [code]null[/code] (in which case the default value will be returned). See also [method has_meta], [method set_meta] and [method remove_meta].
- [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the inspector and should not be edited.
+ [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited.
</description>
</method>
<method name="get_meta_list" qualifiers="const">
@@ -430,7 +430,7 @@
<param index="0" name="name" type="StringName" />
<description>
Returns [code]true[/code] if a metadata entry is found with the given [param name]. See also [method get_meta], [method set_meta] and [method remove_meta].
- [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the inspector and should not be edited.
+ [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited.
</description>
</method>
<method name="has_method" qualifiers="const">
@@ -502,7 +502,7 @@
<param index="0" name="name" type="StringName" />
<description>
Removes a given entry from the object's metadata. See also [method has_meta], [method get_meta] and [method set_meta].
- [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the inspector and should not be edited.
+ [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited.
</description>
</method>
<method name="set">
@@ -566,7 +566,7 @@
<description>
Adds, changes or removes a given entry in the object's metadata. Metadata are serialized and can take any [Variant] value.
To remove a given entry from the object's metadata, use [method remove_meta]. Metadata is also removed if its value is set to [code]null[/code]. This means you can also use [code]set_meta("name", null)[/code] to remove metadata for [code]"name"[/code]. See also [method has_meta] and [method get_meta].
- [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the inspector and should not be edited.
+ [b]Note:[/b] Metadata that has a [param name] starting with an underscore ([code]_[/code]) is considered editor-only. Editor-only metadata is not displayed in the Inspector and should not be edited.
</description>
</method>
<method name="set_script">
diff --git a/doc/classes/OccluderInstance3D.xml b/doc/classes/OccluderInstance3D.xml
index 0bebc7ea43..bc16fba500 100644
--- a/doc/classes/OccluderInstance3D.xml
+++ b/doc/classes/OccluderInstance3D.xml
@@ -42,7 +42,7 @@
</member>
<member name="occluder" type="Occluder3D" setter="set_occluder" getter="get_occluder">
The occluder resource for this [OccluderInstance3D]. You can generate an occluder resource by selecting an [OccluderInstance3D] node then using the [b]Bake Occluders[/b] button at the top of the editor.
- You can also draw your own 2D occluder polygon by adding a new [PolygonOccluder3D] resource to the [member occluder] property in the inspector.
+ You can also draw your own 2D occluder polygon by adding a new [PolygonOccluder3D] resource to the [member occluder] property in the Inspector.
Alternatively, you can select a primitive occluder to use: [QuadOccluder3D], [BoxOccluder3D] or [SphereOccluder3D].
</member>
</members>
diff --git a/doc/classes/PortableCompressedTexture2D.xml b/doc/classes/PortableCompressedTexture2D.xml
index c7e2f2fbdc..a0492f2c07 100644
--- a/doc/classes/PortableCompressedTexture2D.xml
+++ b/doc/classes/PortableCompressedTexture2D.xml
@@ -22,7 +22,7 @@
<description>
Initializes the compressed texture from a base image. The compression mode must be provided.
If this image will be used as a normal map, the "normal map" flag is recommended, to ensure optimum quality.
- If lossy compression is requested, the quality setting can optionally be provided. This maps to Lossy WEBP compression quality.
+ If lossy compression is requested, the quality setting can optionally be provided. This maps to Lossy WebP compression quality.
</description>
</method>
<method name="get_compression_mode" qualifiers="const">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 7843104a59..925d4ec7c4 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -251,7 +251,7 @@
Changes to this setting will only be applied upon restarting the application.
</member>
<member name="application/run/disable_stdout" type="bool" setter="" getter="" default="false">
- If [code]true[/code], disables printing to standard output. This is equivalent to starting the editor or project with the [code]--quiet[/code] command line argument. See also [member application/run/disable_stderr].
+ If [code]true[/code], disables printing to standard output. This is equivalent to starting the editor or project with the [code]--quiet[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. See also [member application/run/disable_stderr].
Changes to this setting will only be applied upon restarting the application.
</member>
<member name="application/run/flush_stdout_on_print" type="bool" setter="" getter="" default="false">
@@ -297,7 +297,7 @@
<member name="audio/driver/driver" type="String" setter="" getter="">
Specifies the audio driver to use. This setting is platform-dependent as each platform supports different audio drivers. If left empty, the default audio driver will be used.
The [code]Dummy[/code] audio driver disables all audio playback and recording, which is useful for non-game applications as it reduces CPU usage. It also prevents the engine from appearing as an application playing audio in the OS' audio mixer.
- [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--audio-driver[/code] command line argument.
+ [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--audio-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
</member>
<member name="audio/driver/enable_input" type="bool" setter="" getter="" default="false">
If [code]true[/code], microphone input will be allowed. This requires appropriate permissions to be set when exporting to Android or iOS.
@@ -480,7 +480,7 @@
Print GPU profile information to standard output every second. This includes how long each frame takes the GPU to render on average, broken down into different steps of the render pipeline, such as CanvasItems, shadows, glow, etc.
</member>
<member name="debug/settings/stdout/verbose_stdout" type="bool" setter="" getter="" default="false">
- Print more information to standard output when running. It displays information such as memory leaks, which scenes and resources are being loaded, etc. This can also be enabled using the [code]--verbose[/code] or [code]-v[/code] command line argument, even on an exported project. See also [method OS.is_stdout_verbose] and [method @GlobalScope.print_verbose].
+ Print more information to standard output when running. It displays information such as memory leaks, which scenes and resources are being loaded, etc. This can also be enabled using the [code]--verbose[/code] or [code]-v[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url], even on an exported project. See also [method OS.is_stdout_verbose] and [method @GlobalScope.print_verbose].
</member>
<member name="debug/shapes/collision/contact_color" type="Color" setter="" getter="" default="Color(1, 0.2, 0.1, 0.8)">
Color of the contact points between collision shapes, visible when "Visible Collision Shapes" is enabled in the Debug menu.
@@ -630,7 +630,7 @@
</member>
<member name="editor/movie_writer/fps" type="int" setter="" getter="" default="60">
The number of frames per second to record in the video when writing a movie. Simulation speed will adjust to always match the specified framerate, which means the engine will appear to run slower at higher [member editor/movie_writer/fps] values. Certain FPS values will require you to adjust [member editor/movie_writer/mix_rate] to prevent audio from desynchronizing over time.
- This can be specified manually on the command line using the [code]--fixed-fps &lt;fps&gt;[/code] command line argument.
+ This can be specified manually on the command line using the [code]--fixed-fps &lt;fps&gt;[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
</member>
<member name="editor/movie_writer/mix_rate" type="int" setter="" getter="" default="48000">
The audio mix rate to use in the recorded audio when writing a movie (in Hz). This can be different from [member audio/driver/mix_rate], but this value must be divisible by [member editor/movie_writer/fps] to prevent audio from desynchronizing over time.
@@ -694,7 +694,8 @@
Default value for [member ScrollContainer.scroll_deadzone], which will be used for all [ScrollContainer]s unless overridden.
</member>
<member name="gui/common/swap_cancel_ok" type="bool" setter="" getter="">
- If [code]true[/code], swaps Cancel and OK buttons in dialogs on Windows and UWP to follow interface conventions.
+ If [code]true[/code], swaps [b]Cancel[/b] and [b]OK[/b] buttons in dialogs on Windows and UWP to follow interface conventions. [method DisplayServer.get_swap_cancel_ok] can be used to query whether buttons are swapped at run-time.
+ [b]Note:[/b] This doesn't affect native dialogs such as the ones spawned by [method DisplayServer.dialog_show].
</member>
<member name="gui/common/text_edit_undo_stack_max_size" type="int" setter="" getter="" default="1024">
Maximum undo/redo history size for [TextEdit] fields.
@@ -855,6 +856,18 @@
<member name="input/ui_text_backspace_word.macos" type="Dictionary" setter="" getter="">
macOS specific override for the shortcut to delete a word.
</member>
+ <member name="input/ui_text_caret_add_above" type="Dictionary" setter="" getter="">
+ Default [InputEventAction] to add an additional caret above every caret of a text
+ </member>
+ <member name="input/ui_text_caret_add_above.macos" type="Dictionary" setter="" getter="">
+ macOS specific override for the shortcut to add a caret above every caret
+ </member>
+ <member name="input/ui_text_caret_add_below" type="Dictionary" setter="" getter="">
+ Default [InputEventAction] to add an additional caret below every caret of a text
+ </member>
+ <member name="input/ui_text_caret_add_below.macos" type="Dictionary" setter="" getter="">
+ macOS specific override for the shortcut to add a caret below every caret
+ </member>
<member name="input/ui_text_caret_document_end" type="Dictionary" setter="" getter="">
Default [InputEventAction] to move the text cursor the the end of the text.
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
@@ -1017,7 +1030,7 @@
</member>
<member name="input_devices/pen_tablet/driver" type="String" setter="" getter="">
Specifies the tablet driver to use. If left empty, the default driver will be used.
- [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--tablet-driver[/code] command line argument.
+ [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--tablet-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
</member>
<member name="input_devices/pen_tablet/driver.windows" type="String" setter="" getter="">
Override for [member input_devices/pen_tablet/driver] on Windows.
@@ -1082,8 +1095,8 @@
<member name="internationalization/rendering/text_driver" type="String" setter="" getter="" default="&quot;&quot;">
Specifies the [TextServer] to use. If left empty, the default will be used.
"ICU / HarfBuzz / Graphite" is the most advanced text driver, supporting right-to-left typesetting and complex scripts (for languages like Arabic, Hebrew, etc). The "Fallback" text driver does not support right-to-left typesetting and complex scripts.
- [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--text-driver[/code] command line argument.
- [b]Note:[/b] There is an additional [code]Dummy[/code] text driver available, which disables all text rendering and font-related functionality. This driver is not listed in the project settings, but it can be enabled when running the editor or project using the [code]--text-driver Dummy[/code] command line argument.
+ [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--text-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
+ [b]Note:[/b] There is an additional [code]Dummy[/code] text driver available, which disables all text rendering and font-related functionality. This driver is not listed in the project settings, but it can be enabled when running the editor or project using the [code]--text-driver Dummy[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
</member>
<member name="layer_names/2d_navigation/layer_1" type="String" setter="" getter="" default="&quot;&quot;">
Optional name for the 2D navigation layer 1. If left empty, the layer will display as "Layer 1".
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 376ba34056..35fe7bdbd2 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -13,6 +13,7 @@
Similarly, in 2D, a canvas is needed to draw all canvas items.
In 3D, all visible objects are comprised of a resource and an instance. A resource can be a mesh, a particle system, a light, or any other 3D object. In order to be visible resources must be attached to an instance using [method instance_set_base]. The instance must also be attached to the scenario using [method instance_set_scenario] in order to be visible.
In 2D, all visible objects are some form of canvas item. In order to be visible, a canvas item needs to be the child of a canvas attached to a viewport, or it needs to be the child of another canvas item that is eventually attached to the canvas.
+ [b]Headless mode:[/b] Starting the engine with the [code]--headless[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url] disables all rendering and window management functions. Most functions from [RenderingServer] will return dummy values in this case.
</description>
<tutorials>
<link title="Optimization using Servers">$DOCS_URL/tutorials/performance/using_servers.html</link>
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 879a355f25..2c64b78b49 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -63,6 +63,13 @@
Adds a new caret at the given location. Returns the index of the new caret, or [code]-1[/code] if the location is invalid.
</description>
</method>
+ <method name="add_caret_at_carets">
+ <return type="void" />
+ <param index="0" name="below" type="bool" />
+ <description>
+ Adds an additional caret above or below every caret. If [param below] is true the new caret will be added below and above otherwise.
+ </description>
+ </method>
<method name="add_gutter">
<return type="void" />
<param index="0" name="at" type="int" default="-1" />
diff --git a/doc/classes/VisualShaderNodeFloatParameter.xml b/doc/classes/VisualShaderNodeFloatParameter.xml
index c0fd88294a..3b5bf57b4d 100644
--- a/doc/classes/VisualShaderNodeFloatParameter.xml
+++ b/doc/classes/VisualShaderNodeFloatParameter.xml
@@ -16,7 +16,7 @@
Enables usage of the [member default_value].
</member>
<member name="hint" type="int" setter="set_hint" getter="get_hint" enum="VisualShaderNodeFloatParameter.Hint" default="0">
- A hint applied to the uniform, which controls the values it can take when set through the inspector.
+ A hint applied to the uniform, which controls the values it can take when set through the Inspector.
</member>
<member name="max" type="float" setter="set_max" getter="get_max" default="1.0">
Minimum value for range hints. Used if [member hint] is set to [constant HINT_RANGE] or [constant HINT_RANGE_STEP].
diff --git a/doc/classes/VisualShaderNodeParameter.xml b/doc/classes/VisualShaderNodeParameter.xml
index c66022f77d..55b10ac810 100644
--- a/doc/classes/VisualShaderNodeParameter.xml
+++ b/doc/classes/VisualShaderNodeParameter.xml
@@ -4,7 +4,7 @@
A base type for the parameters within the visual shader graph.
</brief_description>
<description>
- A parameter represents a variable in the shader which is set externally, i.e. from the [ShaderMaterial]. Parameters are exposed as properties in the [ShaderMaterial] and can be assigned from the inspector or from a script.
+ A parameter represents a variable in the shader which is set externally, i.e. from the [ShaderMaterial]. Parameters are exposed as properties in the [ShaderMaterial] and can be assigned from the Inspector or from a script.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/VoxelGIData.xml b/doc/classes/VoxelGIData.xml
index 92b2e66e5a..bd9df18394 100644
--- a/doc/classes/VoxelGIData.xml
+++ b/doc/classes/VoxelGIData.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
[VoxelGIData] contains baked voxel global illumination for use in a [VoxelGI] node. [VoxelGIData] also offers several properties to adjust the final appearance of the global illumination. These properties can be adjusted at run-time without having to bake the [VoxelGI] node again.
- [b]Note:[/b] To prevent text-based scene files ([code].tscn[/code]) from growing too much and becoming slow to load and save, always save [VoxelGIData] to an external binary resource file ([code].res[/code]) instead of embedding it within the scene. This can be done by clicking the dropdown arrow next to the [VoxelGIData] resource, choosing [b]Edit[/b], clicking the floppy disk icon at the top of the inspector then choosing [b]Save As...[/b].
+ [b]Note:[/b] To prevent text-based scene files ([code].tscn[/code]) from growing too much and becoming slow to load and save, always save [VoxelGIData] to an external binary resource file ([code].res[/code]) instead of embedding it within the scene. This can be done by clicking the dropdown arrow next to the [VoxelGIData] resource, choosing [b]Edit[/b], clicking the floppy disk icon at the top of the Inspector then choosing [b]Save As...[/b].
</description>
<tutorials>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
diff --git a/doc/classes/float.xml b/doc/classes/float.xml
index d7232bb0e9..e3938fb5d5 100644
--- a/doc/classes/float.xml
+++ b/doc/classes/float.xml
@@ -114,12 +114,17 @@
<return type="Vector4" />
<param index="0" name="right" type="Vector4" />
<description>
+ Multiplies each component of the [Vector4] by the given [float].
</description>
</operator>
<operator name="operator *">
<return type="Vector4" />
<param index="0" name="right" type="Vector4i" />
<description>
+ Multiplies each component of the [Vector4i] by the given [float]. Returns a [Vector4].
+ [codeblock]
+ print(0.9 * Vector4i(10, 15, 20, -10)) # Prints "(9, 13.5, 18, -9)"
+ [/codeblock]
</description>
</operator>
<operator name="operator *">
@@ -140,12 +145,20 @@
<return type="float" />
<param index="0" name="right" type="float" />
<description>
+ Raises a [float] to a power of a [float].
+ [codeblock]
+ print(39.0625**0.25) # 2.5
+ [/codeblock]
</description>
</operator>
<operator name="operator **">
<return type="float" />
<param index="0" name="right" type="int" />
<description>
+ Raises a [float] to a power of an [int]. The result is a [float].
+ [codeblock]
+ print(0.9**3) # 0.729
+ [/codeblock]
</description>
</operator>
<operator name="operator +">
diff --git a/doc/classes/int.xml b/doc/classes/int.xml
index 868a8e9944..689cb7fe8e 100644
--- a/doc/classes/int.xml
+++ b/doc/classes/int.xml
@@ -161,12 +161,14 @@
<return type="Vector4" />
<param index="0" name="right" type="Vector4" />
<description>
+ Multiplies each component of the [Vector4] by the given [int].
</description>
</operator>
<operator name="operator *">
<return type="Vector4i" />
<param index="0" name="right" type="Vector4i" />
<description>
+ Multiplies each component of the [Vector4i] by the given [int].
</description>
</operator>
<operator name="operator *">
@@ -187,12 +189,20 @@
<return type="float" />
<param index="0" name="right" type="float" />
<description>
+ Raises an [int] to a power of a [float]. The result is a [float].
+ [codeblock]
+ print(8**0.25) # 1.68179283050743
+ [/codeblock]
</description>
</operator>
<operator name="operator **">
<return type="int" />
<param index="0" name="right" type="int" />
<description>
+ Raises an [int] to a power of a [int].
+ [codeblock]
+ print(5**5) # 3125
+ [/codeblock]
</description>
</operator>
<operator name="operator +">
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index ea97dff522..b2d01b02fb 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -101,8 +101,7 @@ void RasterizerGLES3::begin_frame(double frame_step) {
scene->set_time(time_total, frame_step);
GLES3::Utilities *utils = GLES3::Utilities::get_singleton();
- utils->info.render_final = utils->info.render;
- utils->info.render.reset();
+ utils->_capture_timestamps_begin();
//scene->iteration();
}
@@ -272,6 +271,13 @@ RasterizerGLES3::~RasterizerGLES3() {
}
void RasterizerGLES3::prepare_for_blitting_render_targets() {
+ // This is a hack, but this function is called one time after all viewports have been updated.
+ // So it marks the end of the frame for all viewports
+ // In the OpenGL renderer we have to call end_frame for each viewport so we can swap the
+ // buffers for each window before proceeding to the next.
+ // This allows us to only increment the frame after all viewports are done.
+ GLES3::Utilities *utils = GLES3::Utilities::get_singleton();
+ utils->capture_timestamps_end();
}
void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer) {
diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp
index 0a04f0d4d0..a2d1ba376a 100644
--- a/drivers/gles3/storage/utilities.cpp
+++ b/drivers/gles3/storage/utilities.cpp
@@ -38,16 +38,35 @@
#include "particles_storage.h"
#include "texture_storage.h"
+#include "servers/rendering/rendering_server_globals.h"
+
using namespace GLES3;
Utilities *Utilities::singleton = nullptr;
Utilities::Utilities() {
singleton = this;
+ frame = 0;
+ for (int i = 0; i < FRAME_COUNT; i++) {
+ frames[i].index = 0;
+ glGenQueries(max_timestamp_query_elements, frames[i].queries);
+
+ frames[i].timestamp_names.resize(max_timestamp_query_elements);
+ frames[i].timestamp_cpu_values.resize(max_timestamp_query_elements);
+ frames[i].timestamp_count = 0;
+
+ frames[i].timestamp_result_names.resize(max_timestamp_query_elements);
+ frames[i].timestamp_cpu_result_values.resize(max_timestamp_query_elements);
+ frames[i].timestamp_result_values.resize(max_timestamp_query_elements);
+ frames[i].timestamp_result_count = 0;
+ }
}
Utilities::~Utilities() {
singleton = nullptr;
+ for (int i = 0; i < FRAME_COUNT; i++) {
+ glDeleteQueries(max_timestamp_query_elements, frames[i].queries);
+ }
}
Vector<uint8_t> Utilities::buffer_get_data(GLenum p_target, GLuint p_buffer, uint32_t p_buffer_size) {
@@ -213,87 +232,69 @@ void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_de
/* TIMING */
-//void Utilities::render_info_begin_capture() {
-// info.snap = info.render;
-//}
-
-//void Utilities::render_info_end_capture() {
-// info.snap.object_count = info.render.object_count - info.snap.object_count;
-// info.snap.draw_call_count = info.render.draw_call_count - info.snap.draw_call_count;
-// info.snap.material_switch_count = info.render.material_switch_count - info.snap.material_switch_count;
-// info.snap.surface_switch_count = info.render.surface_switch_count - info.snap.surface_switch_count;
-// info.snap.shader_rebind_count = info.render.shader_rebind_count - info.snap.shader_rebind_count;
-// info.snap.vertices_count = info.render.vertices_count - info.snap.vertices_count;
-// info.snap._2d_item_count = info.render._2d_item_count - info.snap._2d_item_count;
-// info.snap._2d_draw_call_count = info.render._2d_draw_call_count - info.snap._2d_draw_call_count;
-//}
-
-//int Utilities::get_captured_render_info(RS::RenderInfo p_info) {
-// switch (p_info) {
-// case RS::INFO_OBJECTS_IN_FRAME: {
-// return info.snap.object_count;
-// } break;
-// case RS::INFO_VERTICES_IN_FRAME: {
-// return info.snap.vertices_count;
-// } break;
-// case RS::INFO_MATERIAL_CHANGES_IN_FRAME: {
-// return info.snap.material_switch_count;
-// } break;
-// case RS::INFO_SHADER_CHANGES_IN_FRAME: {
-// return info.snap.shader_rebind_count;
-// } break;
-// case RS::INFO_SURFACE_CHANGES_IN_FRAME: {
-// return info.snap.surface_switch_count;
-// } break;
-// case RS::INFO_DRAW_CALLS_IN_FRAME: {
-// return info.snap.draw_call_count;
-// } break;
-// /*
-// case RS::INFO_2D_ITEMS_IN_FRAME: {
-// return info.snap._2d_item_count;
-// } break;
-// case RS::INFO_2D_DRAW_CALLS_IN_FRAME: {
-// return info.snap._2d_draw_call_count;
-// } break;
-// */
-// default: {
-// return get_render_info(p_info);
-// }
-// }
-//}
-
-//int Utilities::get_render_info(RS::RenderInfo p_info) {
-// switch (p_info) {
-// case RS::INFO_OBJECTS_IN_FRAME:
-// return info.render_final.object_count;
-// case RS::INFO_VERTICES_IN_FRAME:
-// return info.render_final.vertices_count;
-// case RS::INFO_MATERIAL_CHANGES_IN_FRAME:
-// return info.render_final.material_switch_count;
-// case RS::INFO_SHADER_CHANGES_IN_FRAME:
-// return info.render_final.shader_rebind_count;
-// case RS::INFO_SURFACE_CHANGES_IN_FRAME:
-// return info.render_final.surface_switch_count;
-// case RS::INFO_DRAW_CALLS_IN_FRAME:
-// return info.render_final.draw_call_count;
-// /*
-// case RS::INFO_2D_ITEMS_IN_FRAME:
-// return info.render_final._2d_item_count;
-// case RS::INFO_2D_DRAW_CALLS_IN_FRAME:
-// return info.render_final._2d_draw_call_count;
-//*/
-// case RS::INFO_USAGE_VIDEO_MEM_TOTAL:
-// return 0; //no idea
-// case RS::INFO_VIDEO_MEM_USED:
-// return info.vertex_mem + info.texture_mem;
-// case RS::INFO_TEXTURE_MEM_USED:
-// return info.texture_mem;
-// case RS::INFO_VERTEX_MEM_USED:
-// return info.vertex_mem;
-// default:
-// return 0; //no idea either
-// }
-//}
+void Utilities::capture_timestamps_begin() {
+ capture_timestamp("Frame Begin");
+}
+
+void Utilities::capture_timestamp(const String &p_name) {
+ ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements);
+
+#ifdef GLES_OVER_GL
+ glQueryCounter(frames[frame].queries[frames[frame].timestamp_count], GL_TIMESTAMP);
+#endif
+
+ frames[frame].timestamp_names[frames[frame].timestamp_count] = p_name;
+ frames[frame].timestamp_cpu_values[frames[frame].timestamp_count] = OS::get_singleton()->get_ticks_usec();
+ frames[frame].timestamp_count++;
+}
+
+void Utilities::_capture_timestamps_begin() {
+ // frame is incremented at the end of the frame so this gives us the queries for frame - 2. By then they should be ready.
+ if (frames[frame].timestamp_count) {
+#ifdef GLES_OVER_GL
+ for (uint32_t i = 0; i < frames[frame].timestamp_count; i++) {
+ uint64_t temp = 0;
+ glGetQueryObjectui64v(frames[frame].queries[i], GL_QUERY_RESULT, &temp);
+ frames[frame].timestamp_result_values[i] = temp;
+ }
+#endif
+ SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names);
+ SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values);
+ }
+
+ frames[frame].timestamp_result_count = frames[frame].timestamp_count;
+ frames[frame].timestamp_count = 0;
+ frames[frame].index = Engine::get_singleton()->get_frames_drawn();
+ capture_timestamp("Internal Begin");
+}
+
+void Utilities::capture_timestamps_end() {
+ capture_timestamp("Internal End");
+ frame = (frame + 1) % FRAME_COUNT;
+}
+
+uint32_t Utilities::get_captured_timestamps_count() const {
+ return frames[frame].timestamp_result_count;
+}
+
+uint64_t Utilities::get_captured_timestamps_frame() const {
+ return frames[frame].index;
+}
+
+uint64_t Utilities::get_captured_timestamp_gpu_time(uint32_t p_index) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0);
+ return frames[frame].timestamp_result_values[p_index];
+}
+
+uint64_t Utilities::get_captured_timestamp_cpu_time(uint32_t p_index) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0);
+ return frames[frame].timestamp_cpu_result_values[p_index];
+}
+
+String Utilities::get_captured_timestamp_name(uint32_t p_index) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, String());
+ return frames[frame].timestamp_result_names[p_index];
+}
/* MISC */
diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h
index 936ac35cc9..55a875958e 100644
--- a/drivers/gles3/storage/utilities.h
+++ b/drivers/gles3/storage/utilities.h
@@ -79,62 +79,35 @@ public:
/* TIMING */
- struct Info {
- uint64_t texture_mem = 0;
- uint64_t vertex_mem = 0;
-
- struct Render {
- uint32_t object_count;
- uint32_t draw_call_count;
- uint32_t material_switch_count;
- uint32_t surface_switch_count;
- uint32_t shader_rebind_count;
- uint32_t vertices_count;
- uint32_t _2d_item_count;
- uint32_t _2d_draw_call_count;
-
- void reset() {
- object_count = 0;
- draw_call_count = 0;
- material_switch_count = 0;
- surface_switch_count = 0;
- shader_rebind_count = 0;
- vertices_count = 0;
- _2d_item_count = 0;
- _2d_draw_call_count = 0;
- }
- } render, render_final, snap;
-
- Info() {
- render.reset();
- render_final.reset();
- }
-
- } info;
-
- virtual void capture_timestamps_begin() override {}
- virtual void capture_timestamp(const String &p_name) override {}
- virtual uint32_t get_captured_timestamps_count() const override {
- return 0;
- }
- virtual uint64_t get_captured_timestamps_frame() const override {
- return 0;
- }
- virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override {
- return 0;
- }
- virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override {
- return 0;
- }
- virtual String get_captured_timestamp_name(uint32_t p_index) const override {
- return String();
- }
-
- // void render_info_begin_capture() override;
- // void render_info_end_capture() override;
- // int get_captured_render_info(RS::RenderInfo p_info) override;
-
- // int get_render_info(RS::RenderInfo p_info) override;
+#define MAX_QUERIES 256
+#define FRAME_COUNT 3
+
+ struct Frame {
+ GLuint queries[MAX_QUERIES];
+ TightLocalVector<String> timestamp_names;
+ TightLocalVector<uint64_t> timestamp_cpu_values;
+ uint32_t timestamp_count = 0;
+ TightLocalVector<String> timestamp_result_names;
+ TightLocalVector<uint64_t> timestamp_cpu_result_values;
+ TightLocalVector<uint64_t> timestamp_result_values;
+ uint32_t timestamp_result_count = 0;
+ uint64_t index = 0;
+ };
+
+ const uint32_t max_timestamp_query_elements = MAX_QUERIES;
+
+ Frame frames[FRAME_COUNT]; // Frames for capturing timestamps. We use 3 so we don't need to wait for commands to complete
+ uint32_t frame = 0;
+
+ virtual void capture_timestamps_begin() override;
+ virtual void capture_timestamp(const String &p_name) override;
+ virtual uint32_t get_captured_timestamps_count() const override;
+ virtual uint64_t get_captured_timestamps_frame() const override;
+ virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override;
+ virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override;
+ virtual String get_captured_timestamp_name(uint32_t p_index) const override;
+ void _capture_timestamps_begin();
+ void capture_timestamps_end();
/* MISC */
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index 9ee5a67471..56e97b742c 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -687,21 +687,43 @@ Error VulkanContext::_check_capabilities() {
vkGetPhysicalDeviceFeatures2_func(gpu, &device_features);
- vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate;
- vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate;
- vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate;
+ // We must check that the relative extension is present before assuming a
+ // feature as enabled. Actually, according to the spec we shouldn't add the
+ // structs in pNext at all, but this works fine.
+ // See also: https://github.com/godotengine/godot/issues/65409
+ for (uint32_t i = 0; i < enabled_extension_count; ++i) {
+ if (!strcmp(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, extension_names[i])) {
+ vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate;
+ vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate;
+ vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate;
- multiview_capabilities.is_supported = multiview_features.multiview;
- multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader;
- multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader;
+ continue;
+ }
- shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16;
- shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8;
+ if (!strcmp(VK_KHR_MULTIVIEW_EXTENSION_NAME, extension_names[i])) {
+ multiview_capabilities.is_supported = multiview_features.multiview;
+ multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader;
+ multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader;
- storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess;
- storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess;
- storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16;
- storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16;
+ continue;
+ }
+
+ if (!strcmp(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, extension_names[i])) {
+ shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16;
+ shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8;
+
+ continue;
+ }
+
+ if (!strcmp(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, extension_names[i])) {
+ storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess;
+ storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess;
+ storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16;
+ storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16;
+
+ continue;
+ }
+ }
}
// Check extended properties.
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 06b73cf296..ecd8fcb2ba 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -77,6 +77,7 @@ EditorDebuggerNode::EditorDebuggerNode() {
remote_scene_tree = memnew(EditorDebuggerTree);
remote_scene_tree->connect("object_selected", callable_mp(this, &EditorDebuggerNode::_remote_object_requested));
remote_scene_tree->connect("save_node", callable_mp(this, &EditorDebuggerNode::_save_node_requested));
+ remote_scene_tree->connect("button_clicked", callable_mp(this, &EditorDebuggerNode::_remote_tree_button_pressed));
SceneTreeDock::get_singleton()->add_remote_tree_editor(remote_scene_tree);
SceneTreeDock::get_singleton()->connect("remote_tree_selected", callable_mp(this, &EditorDebuggerNode::request_remote_tree));
@@ -573,6 +574,24 @@ void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {
remote_scene_tree->update_scene_tree(get_current_debugger()->get_remote_tree(), p_debugger);
}
+void EditorDebuggerNode::_remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
+ if (p_button != MouseButton::LEFT) {
+ return;
+ }
+
+ TreeItem *item = Object::cast_to<TreeItem>(p_item);
+ ERR_FAIL_COND(!item);
+
+ if (p_id == EditorDebuggerTree::BUTTON_SUBSCENE) {
+ remote_scene_tree->emit_signal(SNAME("open"), item->get_meta("scene_file_path"));
+ } else if (p_id == EditorDebuggerTree::BUTTON_VISIBILITY) {
+ ObjectID obj_id = item->get_metadata(0);
+ ERR_FAIL_COND(obj_id.is_null());
+ get_current_debugger()->update_remote_object(obj_id, "visible", !item->get_meta("visible"));
+ get_current_debugger()->request_remote_tree();
+ }
+}
+
void EditorDebuggerNode::_remote_object_updated(ObjectID p_id, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h
index e79e60b180..305f18a652 100644
--- a/editor/debugger/editor_debugger_node.h
+++ b/editor/debugger/editor_debugger_node.h
@@ -129,6 +129,7 @@ protected:
void _debugger_wants_stop(int p_id);
void _debugger_changed(int p_tab);
void _remote_tree_updated(int p_debugger);
+ void _remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _remote_object_updated(ObjectID p_id, int p_debugger);
void _remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger);
void _remote_object_requested(ObjectID p_id, int p_debugger);
diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp
index 76efcd7190..4168efda26 100644
--- a/editor/debugger/editor_debugger_tree.cpp
+++ b/editor/debugger/editor_debugger_tree.cpp
@@ -65,6 +65,7 @@ void EditorDebuggerTree::_notification(int p_what) {
void EditorDebuggerTree::_bind_methods() {
ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("save_node", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "filename"), PropertyInfo(Variant::INT, "debugger")));
+ ADD_SIGNAL(MethodInfo("open"));
}
void EditorDebuggerTree::_scene_tree_selected() {
@@ -162,7 +163,7 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
}
item->set_metadata(0, node.id);
- // Set current item as collapsed if necessary (root is never collapsed)
+ // Set current item as collapsed if necessary (root is never collapsed).
if (parent) {
if (!unfold_cache.has(node.id)) {
item->set_collapsed(true);
@@ -178,7 +179,7 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
}
} else { // Must use path
if (last_path == _get_path(item)) {
- updating_scene_tree = false; // Force emission of new selection
+ updating_scene_tree = false; // Force emission of new selection.
item->select(0);
if (filter_changed) {
scroll_item = item;
@@ -187,6 +188,33 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
}
}
+ // Add buttons.
+ const Color remote_button_color = Color(1, 1, 1, 0.8);
+ if (!node.scene_file_path.is_empty()) {
+ String node_scene_file_path = node.scene_file_path;
+ Ref<Texture2D> button_icon = get_theme_icon(SNAME("InstanceOptions"), SNAME("EditorIcons"));
+ String tooltip = vformat(TTR("This node has been instantiated from a PackedScene file:\n%s\nClick to open the original file in the Editor."), node_scene_file_path);
+
+ item->set_meta("scene_file_path", node_scene_file_path);
+ item->add_button(0, button_icon, BUTTON_SUBSCENE, false, tooltip);
+ item->set_button_color(0, item->get_button_count(0) - 1, remote_button_color);
+ }
+
+ if (node.view_flags & SceneDebuggerTree::RemoteNode::VIEW_HAS_VISIBLE_METHOD) {
+ bool node_visible = node.view_flags & SceneDebuggerTree::RemoteNode::VIEW_VISIBLE;
+ bool node_visible_in_tree = node.view_flags & SceneDebuggerTree::RemoteNode::VIEW_VISIBLE_IN_TREE;
+ Ref<Texture2D> button_icon = get_theme_icon(node_visible ? SNAME("GuiVisibilityVisible") : SNAME("GuiVisibilityHidden"), SNAME("EditorIcons"));
+ String tooltip = TTR("Toggle Visibility");
+
+ item->set_meta("visible", node_visible);
+ item->add_button(0, button_icon, BUTTON_VISIBILITY, false, tooltip);
+ if (ClassDB::is_parent_class(node.type_name, "CanvasItem") || ClassDB::is_parent_class(node.type_name, "Node3D")) {
+ item->set_button_color(0, item->get_button_count(0) - 1, node_visible_in_tree ? remote_button_color : Color(1, 1, 1, 0.6));
+ } else {
+ item->set_button_color(0, item->get_button_count(0) - 1, remote_button_color);
+ }
+ }
+
// Add in front of the parents stack if children are expected.
if (node.child_count) {
parents.push_front(Pair<TreeItem *, int>(item, node.child_count));
diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h
index 5af3a0d84a..6d590cdb7a 100644
--- a/editor/debugger/editor_debugger_tree.h
+++ b/editor/debugger/editor_debugger_tree.h
@@ -65,6 +65,11 @@ protected:
void _notification(int p_what);
public:
+ enum Button {
+ BUTTON_SUBSCENE = 0,
+ BUTTON_VISIBILITY = 1,
+ };
+
virtual Variant get_drag_data(const Point2 &p_point) override;
String get_selected_path();
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 83deff1469..0ff2227fed 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -1036,7 +1036,7 @@ void EditorAudioBuses::_notification(int p_what) {
case NOTIFICATION_DRAG_END: {
if (drop_end) {
- drop_end->queue_delete();
+ drop_end->queue_free();
drop_end = nullptr;
}
} break;
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index 98f21bda7a..acdf41c078 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -527,7 +527,7 @@ void EditorAutoloadSettings::update_autoload() {
}
if (info.node) {
- info.node->queue_delete();
+ info.node->queue_free();
info.node = nullptr;
}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 8ee82888f3..3b56d9ee2c 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -4534,7 +4534,7 @@ void EditorNode::_dock_floating_close_request(Control *p_control) {
dock_slot[window_slot]->move_child(p_control, MIN((int)window->get_meta("dock_index"), dock_slot[window_slot]->get_tab_count()));
dock_slot[window_slot]->set_current_tab(window->get_meta("dock_index"));
- window->queue_delete();
+ window->queue_free();
_update_dock_containers();
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 7cc42f4d24..bec05a0cd6 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -293,7 +293,7 @@ void EditorPropertyArray::update_property() {
continue; // Don't remove the property that the user is moving.
}
- child->queue_delete(); // Button still needed after pressed is called.
+ child->queue_free(); // Button still needed after pressed is called.
property_vbox->remove_child(child);
}
}
@@ -861,7 +861,7 @@ void EditorPropertyDictionary::update_property() {
} else {
// Queue children for deletion, deleting immediately might cause errors.
for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
- property_vbox->get_child(i)->queue_delete();
+ property_vbox->get_child(i)->queue_free();
}
}
@@ -1349,7 +1349,7 @@ void EditorPropertyLocalizableString::update_property() {
} else {
// Queue children for deletion, deleting immediately might cause errors.
for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
- property_vbox->get_child(i)->queue_delete();
+ property_vbox->get_child(i)->queue_free();
}
}
diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp
index 6b2cc99df9..504c8f0432 100644
--- a/editor/editor_toaster.cpp
+++ b/editor/editor_toaster.cpp
@@ -231,7 +231,7 @@ void EditorToaster::_auto_hide_or_free_toasts() {
// Delete the control right away (removed as child) as it might cause issues otherwise when iterative over the vbox_container children.
for (unsigned int i = 0; i < to_delete.size(); i++) {
vbox_container->remove_child(to_delete[i]);
- to_delete[i]->queue_delete();
+ to_delete[i]->queue_free();
toasts.erase(to_delete[i]);
}
diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp
index a0f4ade182..fd110cea4f 100644
--- a/editor/find_in_files.cpp
+++ b/editor/find_in_files.cpp
@@ -464,7 +464,7 @@ void FindInFilesDialog::_notification(int p_what) {
_search_text_line_edit->select_all();
// Extensions might have changed in the meantime, we clean them and instance them again.
for (int i = 0; i < _filters_container->get_child_count(); i++) {
- _filters_container->get_child(i)->queue_delete();
+ _filters_container->get_child(i)->queue_free();
}
Array exts = ProjectSettings::get_singleton()->get("editor/script/search_in_file_extensions");
for (int i = 0; i < exts.size(); ++i) {
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 756d61f712..8ede88a888 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1373,7 +1373,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
}
for (int i = 0; i < post_importer_plugins.size(); i++) {
- post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION, p_root, p_node, anim, node_settings);
+ post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION, p_root, p_node, anim, anim_settings);
}
}
}
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 2d1f4088a0..51b65e8eb0 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -460,7 +460,7 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) {
void EditorAssetLibraryItemDownload::_close() {
// Clean up downloaded file.
DirAccess::remove_file_or_error(download->get_download_file());
- queue_delete();
+ queue_free();
}
bool EditorAssetLibraryItemDownload::can_install() const {
@@ -832,7 +832,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
}
}
- image_queue[p_queue_id].request->queue_delete();
+ image_queue[p_queue_id].request->queue_free();
image_queue.erase(p_queue_id);
_update_image_queue();
@@ -868,7 +868,7 @@ void EditorAssetLibrary::_update_image_queue() {
}
while (to_delete.size()) {
- image_queue[to_delete.front()->get()].request->queue_delete();
+ image_queue[to_delete.front()->get()].request->queue_free();
image_queue.erase(to_delete.front()->get());
to_delete.pop_front();
}
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index fe33a91a41..7004e4d3a2 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5543,7 +5543,7 @@ void CanvasItemEditorViewport::_remove_preview() {
if (preview_node->get_parent()) {
for (int i = preview_node->get_child_count() - 1; i >= 0; i--) {
Node *node = preview_node->get_child(i);
- node->queue_delete();
+ node->queue_free();
preview_node->remove_child(node);
}
EditorNode::get_singleton()->get_scene_root()->remove_child(preview_node);
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index 58256ae549..33992314b0 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -260,7 +260,7 @@ void EditorPropertyFontMetaOverride::update_property() {
} else {
// Queue children for deletion, deleting immediately might cause errors.
for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
- property_vbox->get_child(i)->queue_delete();
+ property_vbox->get_child(i)->queue_free();
}
button_add = nullptr;
}
@@ -457,7 +457,7 @@ void EditorPropertyOTVariation::update_property() {
} else {
// Queue children for deletion, deleting immediately might cause errors.
for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
- property_vbox->get_child(i)->queue_delete();
+ property_vbox->get_child(i)->queue_free();
}
}
@@ -662,7 +662,7 @@ void EditorPropertyOTFeatures::update_property() {
} else {
// Queue children for deletion, deleting immediately might cause errors.
for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
- property_vbox->get_child(i)->queue_delete();
+ property_vbox->get_child(i)->queue_free();
}
button_add = nullptr;
}
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 3c9d93f13a..40008b678a 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -3862,7 +3862,7 @@ void Node3DEditorViewport::_remove_preview_node() {
if (preview_node->get_parent()) {
for (int i = preview_node->get_child_count() - 1; i >= 0; i--) {
Node *node = preview_node->get_child(i);
- node->queue_delete();
+ node->queue_free();
preview_node->remove_child(node);
}
EditorNode::get_singleton()->get_scene_root()->remove_child(preview_node);
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 83245366f9..f2e84aec13 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -1082,7 +1082,7 @@ void Skeleton3DEditor::select_bone(int p_idx) {
Skeleton3DEditor::~Skeleton3DEditor() {
singleton = nullptr;
- handles_mesh_instance->queue_delete();
+ handles_mesh_instance->queue_free();
Node3DEditor *ne = Node3DEditor::get_singleton();
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index e2ed8e44c4..5d02e4e437 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -2481,7 +2481,7 @@ void ThemeTypeEditor::_update_type_items() {
{
for (int i = color_items_list->get_child_count() - 1; i >= 0; i--) {
Node *node = color_items_list->get_child(i);
- node->queue_delete();
+ node->queue_free();
color_items_list->remove_child(node);
}
@@ -2510,7 +2510,7 @@ void ThemeTypeEditor::_update_type_items() {
{
for (int i = constant_items_list->get_child_count() - 1; i >= 0; i--) {
Node *node = constant_items_list->get_child(i);
- node->queue_delete();
+ node->queue_free();
constant_items_list->remove_child(node);
}
@@ -2543,7 +2543,7 @@ void ThemeTypeEditor::_update_type_items() {
{
for (int i = font_items_list->get_child_count() - 1; i >= 0; i--) {
Node *node = font_items_list->get_child(i);
- node->queue_delete();
+ node->queue_free();
font_items_list->remove_child(node);
}
@@ -2581,7 +2581,7 @@ void ThemeTypeEditor::_update_type_items() {
{
for (int i = font_size_items_list->get_child_count() - 1; i >= 0; i--) {
Node *node = font_size_items_list->get_child(i);
- node->queue_delete();
+ node->queue_free();
font_size_items_list->remove_child(node);
}
@@ -2614,7 +2614,7 @@ void ThemeTypeEditor::_update_type_items() {
{
for (int i = icon_items_list->get_child_count() - 1; i >= 0; i--) {
Node *node = icon_items_list->get_child(i);
- node->queue_delete();
+ node->queue_free();
icon_items_list->remove_child(node);
}
@@ -2652,7 +2652,7 @@ void ThemeTypeEditor::_update_type_items() {
{
for (int i = stylebox_items_list->get_child_count() - 1; i >= 0; i--) {
Node *node = stylebox_items_list->get_child(i);
- node->queue_delete();
+ node->queue_free();
stylebox_items_list->remove_child(node);
}
diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
index 8cc96201e7..fdd8a80ad3 100644
--- a/editor/plugins/theme_editor_preview.cpp
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -459,7 +459,7 @@ void SceneThemeEditorPreview::_reload_scene() {
for (int i = preview_content->get_child_count() - 1; i >= 0; i--) {
Node *node = preview_content->get_child(i);
- node->queue_delete();
+ node->queue_free();
preview_content->remove_child(node);
}
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 17b9035121..e760c84f7d 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -1141,7 +1141,7 @@ void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, String p
// Update everything.
if (property_editor) {
- property_editor->queue_delete();
+ property_editor->queue_free();
}
// Update the dummy object.
@@ -1200,7 +1200,7 @@ TileDataDefaultEditor::TileDataDefaultEditor() {
}
TileDataDefaultEditor::~TileDataDefaultEditor() {
- toolbar->queue_delete();
+ toolbar->queue_free();
memdelete(dummy_object);
}
@@ -1403,11 +1403,11 @@ void TileDataCollisionEditor::_polygons_changed() {
dummy_object->remove_dummy_property(vformat("polygon_%d_one_way_margin", i));
}
for (int i = polygon_editor->get_polygon_count(); property_editors.has(vformat("polygon_%d_one_way", i)); i++) {
- property_editors[vformat("polygon_%d_one_way", i)]->queue_delete();
+ property_editors[vformat("polygon_%d_one_way", i)]->queue_free();
property_editors.erase(vformat("polygon_%d_one_way", i));
}
for (int i = polygon_editor->get_polygon_count(); property_editors.has(vformat("polygon_%d_one_way_margin", i)); i++) {
- property_editors[vformat("polygon_%d_one_way_margin", i)]->queue_delete();
+ property_editors[vformat("polygon_%d_one_way_margin", i)]->queue_free();
property_editors.erase(vformat("polygon_%d_one_way_margin", i));
}
}
@@ -2658,7 +2658,7 @@ TileDataTerrainsEditor::TileDataTerrainsEditor() {
}
TileDataTerrainsEditor::~TileDataTerrainsEditor() {
- toolbar->queue_delete();
+ toolbar->queue_free();
memdelete(dummy_object);
}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 1857606f00..874057b7b9 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -671,7 +671,7 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
}
}
for (int i = tile_set->get_occlusion_layers_count(); tile_data_editors.has(vformat("occlusion_layer_%d", i)); i++) {
- tile_data_editors[vformat("occlusion_layer_%d", i)]->queue_delete();
+ tile_data_editors[vformat("occlusion_layer_%d", i)]->queue_free();
tile_data_editors.erase(vformat("occlusion_layer_%d", i));
}
@@ -710,7 +710,7 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
}
}
for (int i = tile_set->get_physics_layers_count(); tile_data_editors.has(vformat("physics_layer_%d", i)); i++) {
- tile_data_editors[vformat("physics_layer_%d", i)]->queue_delete();
+ tile_data_editors[vformat("physics_layer_%d", i)]->queue_free();
tile_data_editors.erase(vformat("physics_layer_%d", i));
}
@@ -728,7 +728,7 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
}
}
for (int i = tile_set->get_navigation_layers_count(); tile_data_editors.has(vformat("navigation_layer_%d", i)); i++) {
- tile_data_editors[vformat("navigation_layer_%d", i)]->queue_delete();
+ tile_data_editors[vformat("navigation_layer_%d", i)]->queue_free();
tile_data_editors.erase(vformat("navigation_layer_%d", i));
}
@@ -750,7 +750,7 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
}
}
for (int i = tile_set->get_custom_data_layers_count(); tile_data_editors.has(vformat("custom_data_%d", i)); i++) {
- tile_data_editors[vformat("custom_data_%d", i)]->queue_delete();
+ tile_data_editors[vformat("custom_data_%d", i)]->queue_free();
tile_data_editors.erase(vformat("custom_data_%d", i));
}
@@ -884,7 +884,7 @@ void TileSetAtlasSourceEditor::_update_atlas_view() {
// Create a bunch of buttons to add alternative tiles.
for (int i = 0; i < alternative_tiles_control->get_child_count(); i++) {
- alternative_tiles_control->get_child(i)->queue_delete();
+ alternative_tiles_control->get_child(i)->queue_free();
}
Vector2i pos;
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 3b057b7631..5e25d343b0 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -808,9 +808,3 @@ TileSetEditor::TileSetEditor() {
EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element));
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
}
-
-TileSetEditor::~TileSetEditor() {
- if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
- }
-}
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index 290c53b109..76a471db74 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -109,7 +109,6 @@ public:
void edit(Ref<TileSet> p_tile_set);
TileSetEditor();
- ~TileSetEditor();
};
#endif // TILE_SET_EDITOR_H
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index 6fdc9a80e8..3937187e38 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -131,7 +131,7 @@ void TilesEditorPlugin::_thread() {
Callable::CallError error;
item.callback.callp(args_ptr, 2, r, error);
- viewport->queue_delete();
+ viewport->queue_free();
}
}
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index ee358a8064..2787328efa 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1401,6 +1401,7 @@ void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root
void SceneTreeDock::_load_request(const String &p_path) {
EditorNode::get_singleton()->open_request(p_path);
+ _local_tree_selected();
}
void SceneTreeDock::_script_open_request(const Ref<Script> &p_script) {
@@ -3218,6 +3219,7 @@ void SceneTreeDock::add_remote_tree_editor(Control *p_remote) {
add_child(p_remote);
remote_tree = p_remote;
remote_tree->hide();
+ remote_tree->connect("open", callable_mp(this, &SceneTreeDock::_load_request));
}
void SceneTreeDock::show_remote_tree() {
@@ -3264,7 +3266,7 @@ void SceneTreeDock::_update_create_root_dialog() {
EditorSettings::get_singleton()->save();
if (node_shortcuts_toggle->is_pressed()) {
for (int i = 0; i < favorite_node_shortcuts->get_child_count(); i++) {
- favorite_node_shortcuts->get_child(i)->queue_delete();
+ favorite_node_shortcuts->get_child(i)->queue_free();
}
Ref<FileAccess> f = FileAccess::open(EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorites.Node"), FileAccess::READ);
diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml
index 67760ba5e8..52f45b2338 100644
--- a/modules/enet/doc_classes/ENetPacketPeer.xml
+++ b/modules/enet/doc_classes/ENetPacketPeer.xml
@@ -80,7 +80,7 @@
<return type="void" />
<param index="0" name="ping_interval" type="int" />
<description>
- Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes.
+ Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. The default ping interval is [code]500[/code] milliseconds.
</description>
</method>
<method name="reset">
@@ -115,33 +115,43 @@
<param index="2" name="deceleration" type="int" />
<description>
Configures throttle parameter for a peer.
- Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probably by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]).
+ Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probability by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]).
When the throttle has a value of [constant PACKET_THROTTLE_SCALE], no unreliable packets are dropped by ENet, and so 100% of all unreliable packets will be sent.
- When the throttle has a value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent.
+ When the throttle has a value of [code]0[/code], all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent.
Intermediate values for the throttle represent intermediate probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits of the local and foreign hosts are taken into account to determine a sensible limit for the throttle probability above which it should not raise even in the best of conditions.
</description>
</method>
</methods>
<constants>
<constant name="STATE_DISCONNECTED" value="0" enum="PeerState">
+ The peer is disconnected.
</constant>
<constant name="STATE_CONNECTING" value="1" enum="PeerState">
+ The peer is currently attempting to connect.
</constant>
<constant name="STATE_ACKNOWLEDGING_CONNECT" value="2" enum="PeerState">
+ The peer has acknowledged the connection request.
</constant>
<constant name="STATE_CONNECTION_PENDING" value="3" enum="PeerState">
+ The peer is currently connecting.
</constant>
<constant name="STATE_CONNECTION_SUCCEEDED" value="4" enum="PeerState">
+ The peer has successfully connected, but is not ready to communicate with yet ([constant STATE_CONNECTED]).
</constant>
<constant name="STATE_CONNECTED" value="5" enum="PeerState">
+ The peer is currently connected and ready to communicate with.
</constant>
<constant name="STATE_DISCONNECT_LATER" value="6" enum="PeerState">
+ The peer is slated to disconnect after it has no more outgoing packets to send.
</constant>
<constant name="STATE_DISCONNECTING" value="7" enum="PeerState">
+ The peer is currently disconnecting.
</constant>
<constant name="STATE_ACKNOWLEDGING_DISCONNECT" value="8" enum="PeerState">
+ The peer has acknowledged the disconnection request.
</constant>
<constant name="STATE_ZOMBIE" value="9" enum="PeerState">
+ The peer has lost connection, but is not considered truly disconnected (as the peer didn't acknowledge the disconnection request).
</constant>
<constant name="PEER_PACKET_LOSS" value="0" enum="PeerStatistic">
Mean packet loss of reliable packets as a ratio with respect to the [constant PACKET_LOSS_SCALE].
@@ -150,6 +160,7 @@
Packet loss variance.
</constant>
<constant name="PEER_PACKET_LOSS_EPOCH" value="2" enum="PeerStatistic">
+ The time at which packet loss statistics were last updated (in milliseconds since the connection started). The interval for packet loss statistics updates is 10 seconds, and at least one packet must have been sent since the last statistics update.
</constant>
<constant name="PEER_ROUND_TRIP_TIME" value="3" enum="PeerStatistic">
Mean packet round trip time for reliable packets.
@@ -164,24 +175,31 @@
Variance of the last trip time recorded.
</constant>
<constant name="PEER_PACKET_THROTTLE" value="7" enum="PeerStatistic">
+ The peer's current throttle status.
</constant>
<constant name="PEER_PACKET_THROTTLE_LIMIT" value="8" enum="PeerStatistic">
+ The maximum number of unreliable packets that should not be dropped. This value is always greater than or equal to [code]1[/code]. The initial value is equal to [constant PACKET_THROTTLE_SCALE].
</constant>
<constant name="PEER_PACKET_THROTTLE_COUNTER" value="9" enum="PeerStatistic">
+ Internal value used to increment the packet throttle counter. The value is hardcoded to [code]7[/code] and cannot be changed. You probably want to look at [constant PEER_PACKET_THROTTLE_ACCELERATION] instead.
</constant>
<constant name="PEER_PACKET_THROTTLE_EPOCH" value="10" enum="PeerStatistic">
+ The time at which throttle statistics were last updated (in milliseconds since the connection started). The interval for throttle statistics updates is [constant PEER_PACKET_THROTTLE_INTERVAL].
</constant>
<constant name="PEER_PACKET_THROTTLE_ACCELERATION" value="11" enum="PeerStatistic">
+ The throttle's acceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]more[/i] often. The default value is [code]2[/code].
</constant>
<constant name="PEER_PACKET_THROTTLE_DECELERATION" value="12" enum="PeerStatistic">
+ The throttle's deceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]less[/i] often. The default value is [code]2[/code].
</constant>
<constant name="PEER_PACKET_THROTTLE_INTERVAL" value="13" enum="PeerStatistic">
+ The interval over which the lowest mean round trip time should be measured for use by the throttle mechanism (in milliseconds). The default value is [code]5000[/code].
</constant>
<constant name="PACKET_LOSS_SCALE" value="65536">
The reference scale for packet loss. See [method get_statistic] and [constant PEER_PACKET_LOSS].
</constant>
<constant name="PACKET_THROTTLE_SCALE" value="32">
- The reference value for throttle configuration. See [method throttle_configure].
+ The reference value for throttle configuration. The default value is [code]32[/code]. See [method throttle_configure].
</constant>
<constant name="FLAG_RELIABLE" value="1">
Mark the packet to be sent as reliable.
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 898e4eb1a6..46904f0f9d 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1602,8 +1602,8 @@ void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
reduce_expression(p_assert->condition);
if (p_assert->message != nullptr) {
reduce_expression(p_assert->message);
- if (!p_assert->message->is_constant || p_assert->message->reduced_value.get_type() != Variant::STRING) {
- push_error(R"(Expected constant string for assert error message.)", p_assert->message);
+ if (!p_assert->message->get_datatype().has_no_type() && (p_assert->message->get_datatype().kind != GDScriptParser::DataType::BUILTIN || p_assert->message->get_datatype().builtin_type != Variant::STRING)) {
+ push_error(R"(Expected string for assert error message.)", p_assert->message);
}
}
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index d465467cf9..9bbe467352 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -33,8 +33,8 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include "thirdparty/jpeg-compressor/jpgd.h"
-#include "thirdparty/jpeg-compressor/jpge.h"
+#include <jpgd.h>
+#include <jpge.h>
#include <string.h>
Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
@@ -132,10 +132,6 @@ static Ref<Image> _jpegd_mem_loader_func(const uint8_t *p_png, int p_size) {
return img;
}
-static Error _jpgd_save_func(const String &p_path, const Ref<Image> &p_img, float p_quality) {
- return OK;
-}
-
class ImageLoaderJPGOSFile : public jpge::output_stream {
public:
Ref<FileAccess> f;
@@ -157,21 +153,18 @@ public:
}
};
-static Vector<uint8_t> _jpgd_buffer_save_func(const Ref<Image> &p_img, float p_quality) {
- ERR_FAIL_COND_V(p_img.is_null() || p_img->is_empty(), Vector<uint8_t>());
+static Error _jpgd_save_to_output_stream(jpge::output_stream *p_output_stream, const Ref<Image> &p_img, float p_quality) {
+ ERR_FAIL_COND_V(p_img.is_null() || p_img->is_empty(), ERR_INVALID_PARAMETER);
Ref<Image> image = p_img;
if (image->get_format() != Image::FORMAT_RGB8) {
- image->convert(Image::FORMAT_ETC2_RGB8);
+ image->convert(Image::FORMAT_RGB8);
}
jpge::params p;
p.m_quality = CLAMP(p_quality * 100, 1, 100);
- Vector<uint8_t> output;
- ImageLoaderJPGOSBuffer ob;
- ob.buffer = &output;
jpge::jpeg_encoder enc;
- enc.init(&ob, image->get_width(), image->get_height(), 3, p);
+ enc.init(p_output_stream, image->get_width(), image->get_height(), 3, p);
const uint8_t *src_data = image->get_data().ptr();
for (int i = 0; i < image->get_height(); i++) {
@@ -180,9 +173,28 @@ static Vector<uint8_t> _jpgd_buffer_save_func(const Ref<Image> &p_img, float p_q
enc.process_scanline(nullptr);
+ return OK;
+}
+
+static Vector<uint8_t> _jpgd_buffer_save_func(const Ref<Image> &p_img, float p_quality) {
+ Vector<uint8_t> output;
+ ImageLoaderJPGOSBuffer ob;
+ ob.buffer = &output;
+ if (_jpgd_save_to_output_stream(&ob, p_img, p_quality) != OK) {
+ return Vector<uint8_t>();
+ }
return output;
}
+static Error _jpgd_save_func(const String &p_path, const Ref<Image> &p_img, float p_quality) {
+ Error err;
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save JPG at path: '%s'.", p_path));
+ ImageLoaderJPGOSFile ob;
+ ob.f = file;
+ return _jpgd_save_to_output_stream(&ob, p_img, p_quality);
+}
+
ImageLoaderJPG::ImageLoaderJPG() {
Image::_jpg_mem_loader_func = _jpegd_mem_loader_func;
Image::save_jpg_func = _jpgd_save_func;
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
index d915eeac0b..d5d80df643 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -188,14 +188,14 @@ namespace Godot.SourceGenerators
if (godotClassMethods.Length > 0)
{
source.Append(" protected override bool InvokeGodotClassMethod(in godot_string_name method, ");
- source.Append("NativeVariantPtrArgs args, int argCount, out godot_variant ret)\n {\n");
+ source.Append("NativeVariantPtrArgs args, out godot_variant ret)\n {\n");
foreach (var method in godotClassMethods)
{
GenerateMethodInvoker(method, source);
}
- source.Append(" return base.InvokeGodotClassMethod(method, args, argCount, out ret);\n");
+ source.Append(" return base.InvokeGodotClassMethod(method, args, out ret);\n");
source.Append(" }\n");
}
@@ -364,7 +364,7 @@ namespace Godot.SourceGenerators
source.Append(" if (method == MethodName.");
source.Append(methodName);
- source.Append(" && argCount == ");
+ source.Append(" && args.Count == ");
source.Append(method.ParamTypes.Length);
source.Append(") {\n");
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 4e443ce26e..50196b84f0 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -167,6 +167,7 @@ namespace Godot.SourceGenerators
Common.ReportSignalDelegateSignatureMustReturnVoid(context, signalDelegateSymbol);
}
}
+
continue;
}
@@ -257,14 +258,14 @@ namespace Godot.SourceGenerators
{
source.Append(
" protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, ");
- source.Append("NativeVariantPtrArgs args, int argCount)\n {\n");
+ source.Append("NativeVariantPtrArgs args)\n {\n");
foreach (var signal in godotSignalDelegates)
{
GenerateSignalEventInvoker(signal, source);
}
- source.Append(" base.RaiseGodotClassSignalCallbacks(signal, args, argCount);\n");
+ source.Append(" base.RaiseGodotClassSignalCallbacks(signal, args);\n");
source.Append(" }\n");
}
@@ -404,7 +405,7 @@ namespace Godot.SourceGenerators
source.Append(" if (signal == SignalName.");
source.Append(signalName);
- source.Append(" && argCount == ");
+ source.Append(" && args.Count == ");
source.Append(invokeMethodData.ParamTypes.Length);
source.Append(") {\n");
source.Append(" backing_");
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 3be8dd87c0..b90321b586 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -1614,7 +1614,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
<< " bool " CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(in godot_string_name method, "
- << "NativeVariantPtrArgs args, int argCount, out godot_variant ret)\n"
+ << "NativeVariantPtrArgs args, out godot_variant ret)\n"
<< INDENT1 "{\n";
for (const MethodInterface &imethod : itype.methods) {
@@ -1630,7 +1630,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// We check both native names (snake_case) and proxy names (PascalCase)
output << INDENT2 "if ((method == " << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
<< " || method == MethodName." << imethod.proxy_name
- << ") && argCount == " << itos(imethod.arguments.size())
+ << ") && args.Count == " << itos(imethod.arguments.size())
<< " && " << CS_METHOD_HAS_GODOT_CLASS_METHOD << "((godot_string_name)"
<< CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name << ".NativeValue))\n"
<< INDENT2 "{\n";
@@ -1682,7 +1682,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
if (is_derived_type) {
- output << INDENT2 "return base." CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(method, args, argCount, out ret);\n";
+ output << INDENT2 "return base." CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(method, args, out ret);\n";
} else {
output << INDENT2 "ret = default;\n"
<< INDENT2 "return false;\n";
@@ -2200,6 +2200,11 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output) {
String arguments_sig;
+ String delegate_type_params;
+
+ if (!p_isignal.arguments.is_empty()) {
+ delegate_type_params += "<";
+ }
// Retrieve information from the arguments
const ArgumentInterface &first = p_isignal.arguments.front()->get();
@@ -2220,11 +2225,18 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
if (&iarg != &first) {
arguments_sig += ", ";
+ delegate_type_params += ", ";
}
arguments_sig += arg_type->cs_type;
arguments_sig += " ";
arguments_sig += iarg.name;
+
+ delegate_type_params += arg_type->cs_type;
+ }
+
+ if (!p_isignal.arguments.is_empty()) {
+ delegate_type_params += ">";
}
// Generate signal
@@ -2248,15 +2260,46 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append("\")]");
}
- String delegate_name = p_isignal.proxy_name;
- delegate_name += "EventHandler"; // Delegate name is [SignalName]EventHandler
+ bool is_parameterless = p_isignal.arguments.size() == 0;
- // Generate delegate
- p_output.append(MEMBER_BEGIN "public delegate void ");
- p_output.append(delegate_name);
- p_output.append("(");
- p_output.append(arguments_sig);
- p_output.append(");\n");
+ // Delegate name is [SignalName]EventHandler
+ String delegate_name = is_parameterless ? "Action" : p_isignal.proxy_name + "EventHandler";
+
+ if (!is_parameterless) {
+ // Generate delegate
+ p_output.append(MEMBER_BEGIN "public delegate void ");
+ p_output.append(delegate_name);
+ p_output.append("(");
+ p_output.append(arguments_sig);
+ p_output.append(");\n");
+
+ // Generate Callable trampoline for the delegate
+ p_output << MEMBER_BEGIN "private static unsafe void " << p_isignal.proxy_name << "Trampoline"
+ << "(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)\n"
+ << INDENT1 "{\n"
+ << INDENT2 "Callable.ThrowIfArgCountMismatch(args, " << itos(p_isignal.arguments.size()) << ");\n"
+ << INDENT2 "((" << delegate_name << ")delegateObj)(";
+
+ int idx = 0;
+ for (const ArgumentInterface &iarg : p_isignal.arguments) {
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
+
+ if (idx != 0) {
+ p_output << ",";
+ }
+
+ // TODO: We don't need to use VariantConversionCallbacks. We have the type information so we can use [cs_variant_to_managed] and [cs_managed_to_variant].
+ p_output << "\n" INDENT3 "VariantConversionCallbacks.GetToManagedCallback<"
+ << arg_type->cs_type << ">()(args[" << itos(idx) << "])";
+
+ idx++;
+ }
+
+ p_output << ");\n"
+ << INDENT2 "ret = default;\n"
+ << INDENT1 "}\n";
+ }
if (p_isignal.method_doc && p_isignal.method_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype);
@@ -2292,6 +2335,11 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append("static ");
}
+ if (!is_parameterless) {
+ // `unsafe` is needed for taking the trampoline's function pointer
+ p_output << "unsafe ";
+ }
+
p_output.append("event ");
p_output.append(delegate_name);
p_output.append(" ");
@@ -2304,8 +2352,13 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append("add => Connect(SignalName.");
}
- p_output.append(p_isignal.proxy_name);
- p_output.append(", new Callable(value));\n");
+ if (is_parameterless) {
+ // Delegate type is Action. No need for custom trampoline.
+ p_output << p_isignal.proxy_name << ", Callable.From(value));\n";
+ } else {
+ p_output << p_isignal.proxy_name
+ << ", Callable.CreateWithUnsafeTrampoline(value, &" << p_isignal.proxy_name << "Trampoline));\n";
+ }
if (p_itype.is_singleton) {
p_output.append(INDENT2 "remove => " CS_PROPERTY_SINGLETON ".Disconnect(SignalName.");
@@ -2313,8 +2366,14 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append(INDENT2 "remove => Disconnect(SignalName.");
}
- p_output.append(p_isignal.proxy_name);
- p_output.append(", new Callable(value));\n");
+ if (is_parameterless) {
+ // Delegate type is Action. No need for custom trampoline.
+ p_output << p_isignal.proxy_name << ", Callable.From(value));\n";
+ } else {
+ p_output << p_isignal.proxy_name
+ << ", Callable.CreateWithUnsafeTrampoline(value, &" << p_isignal.proxy_name << "Trampoline));\n";
+ }
+
p_output.append(CLOSE_BLOCK_L1);
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index 1c98dfcdf6..f1b46e293b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -489,25 +489,37 @@ namespace Godot.Collections
ICollection<T>,
IEnumerable<T>
{
+ private static godot_variant ToVariantFunc(in Array<T> godotArray) =>
+ VariantUtils.CreateFromArray(godotArray);
+
+ private static Array<T> FromVariantFunc(in godot_variant variant) =>
+ VariantUtils.ConvertToArrayObject<T>(variant);
+
// ReSharper disable StaticMemberInGenericType
// Warning is about unique static fields being created for each generic type combination:
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
// In our case this is exactly what we want.
- private static unsafe delegate* managed<in T, godot_variant> _convertToVariantCallback;
- private static unsafe delegate* managed<in godot_variant, T> _convertToManagedCallback;
+ private static readonly unsafe delegate* managed<in T, godot_variant> ConvertToVariantCallback;
+ private static readonly unsafe delegate* managed<in godot_variant, T> ConvertToManagedCallback;
// ReSharper restore StaticMemberInGenericType
static unsafe Array()
{
- _convertToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<T>();
- _convertToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<T>();
+ VariantConversionCallbacks.GenericConversionCallbacks[typeof(Array<T>)] =
+ (
+ (IntPtr)(delegate* managed<in Array<T>, godot_variant>)&ToVariantFunc,
+ (IntPtr)(delegate* managed<in godot_variant, Array<T>>)&FromVariantFunc
+ );
+
+ ConvertToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<T>();
+ ConvertToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<T>();
}
private static unsafe void ValidateVariantConversionCallbacks()
{
- if (_convertToVariantCallback == null || _convertToManagedCallback == null)
+ if (ConvertToVariantCallback == null || ConvertToManagedCallback == null)
{
throw new InvalidOperationException(
$"The array element type is not supported for conversion to Variant: '{typeof(T).FullName}'.");
@@ -653,7 +665,7 @@ namespace Godot.Collections
get
{
_underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
- return _convertToManagedCallback(borrowElem);
+ return ConvertToManagedCallback(borrowElem);
}
set
{
@@ -663,7 +675,7 @@ namespace Godot.Collections
godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
godot_variant* itemPtr = &ptrw[index];
(*itemPtr).Dispose();
- *itemPtr = _convertToVariantCallback(value);
+ *itemPtr = ConvertToVariantCallback(value);
}
}
@@ -675,7 +687,7 @@ namespace Godot.Collections
/// <returns>The index of the item, or -1 if not found.</returns>
public unsafe int IndexOf(T item)
{
- using var variantValue = _convertToVariantCallback(item);
+ using var variantValue = ConvertToVariantCallback(item);
var self = (godot_array)_underlyingArray.NativeValue;
return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
}
@@ -693,7 +705,7 @@ namespace Godot.Collections
if (index < 0 || index > Count)
throw new ArgumentOutOfRangeException(nameof(index));
- using var variantValue = _convertToVariantCallback(item);
+ using var variantValue = ConvertToVariantCallback(item);
var self = (godot_array)_underlyingArray.NativeValue;
NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
}
@@ -726,7 +738,7 @@ namespace Godot.Collections
/// <returns>The new size after adding the item.</returns>
public unsafe void Add(T item)
{
- using var variantValue = _convertToVariantCallback(item);
+ using var variantValue = ConvertToVariantCallback(item);
var self = (godot_array)_underlyingArray.NativeValue;
_ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
index ae44f8f4ba..354212da1b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
@@ -22,8 +22,7 @@ namespace Godot.Bridge
}
bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method),
- new NativeVariantPtrArgs(args),
- argCount, out godot_variant retValue);
+ new NativeVariantPtrArgs(args, argCount), out godot_variant retValue);
if (!methodInvoked)
{
@@ -102,7 +101,7 @@ namespace Godot.Bridge
return godot_bool.False;
}
- *outRet = Marshaling.ConvertManagedObjectToVariant(ret);
+ *outRet = ret.CopyNativeVariant();
return godot_bool.True;
}
catch (Exception e)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
index 57240624bc..44ea8fc83d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
@@ -9,7 +9,7 @@ namespace Godot.Bridge
{
// @formatter:off
public delegate* unmanaged<IntPtr, godot_variant**, int, godot_bool*, void> SignalAwaiter_SignalCallback;
- public delegate* unmanaged<IntPtr, godot_variant**, uint, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs;
+ public delegate* unmanaged<IntPtr, void*, godot_variant**, int, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs;
public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals;
public delegate* unmanaged<IntPtr, godot_array*, godot_bool> DelegateUtils_TrySerializeDelegateWithGCHandle;
public delegate* unmanaged<godot_array*, IntPtr*, godot_bool> DelegateUtils_TryDeserializeDelegateWithGCHandle;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index 092724a6b1..d83cf43eb2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -339,7 +339,7 @@ namespace Godot.Bridge
*outOwnerIsNull = godot_bool.False;
owner.RaiseGodotClassSignalCallbacks(CustomUnsafe.AsRef(eventSignalName),
- new NativeVariantPtrArgs(args), argCount);
+ new NativeVariantPtrArgs(args, argCount));
}
catch (Exception e)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
index bdedd2e87a..f9309ca13e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
@@ -26,11 +26,12 @@ namespace Godot
/// }
/// </code>
/// </example>
- public readonly struct Callable
+ public readonly partial struct Callable
{
private readonly Object _target;
private readonly StringName _method;
private readonly Delegate _delegate;
+ private readonly unsafe delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> _trampoline;
/// <summary>
/// Object that contains the method.
@@ -48,10 +49,10 @@ namespace Godot
public Delegate Delegate => _delegate;
/// <summary>
- /// Converts a <see cref="Delegate"/> to a <see cref="Callable"/>.
+ /// Trampoline function pointer for dynamically invoking <see cref="Callable.Delegate"/>.
/// </summary>
- /// <param name="delegate">The delegate to convert.</param>
- public static implicit operator Callable(Delegate @delegate) => new Callable(@delegate);
+ public unsafe delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> Trampoline
+ => _trampoline;
/// <summary>
/// Constructs a new <see cref="Callable"/> for the method called <paramref name="method"/>
@@ -59,22 +60,21 @@ namespace Godot
/// </summary>
/// <param name="target">Object that contains the method.</param>
/// <param name="method">Name of the method that will be called.</param>
- public Callable(Object target, StringName method)
+ public unsafe Callable(Object target, StringName method)
{
_target = target;
_method = method;
_delegate = null;
+ _trampoline = null;
}
- /// <summary>
- /// Constructs a new <see cref="Callable"/> for the given <paramref name="delegate"/>.
- /// </summary>
- /// <param name="delegate">Delegate method that will be called.</param>
- public Callable(Delegate @delegate)
+ private unsafe Callable(Delegate @delegate,
+ delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> trampoline)
{
_target = @delegate?.Target as Object;
_method = null;
_delegate = @delegate;
+ _trampoline = trampoline;
}
private const int VarArgsSpanThreshold = 5;
@@ -149,5 +149,59 @@ namespace Godot
NativeFuncs.godotsharp_callable_call_deferred(callable, (godot_variant**)argsPtr, argc);
}
}
+
+ /// <summary>
+ /// <para>
+ /// Constructs a new <see cref="Callable"/> using the <paramref name="trampoline"/>
+ /// function pointer to dynamically invoke the given <paramref name="delegate"/>.
+ /// </para>
+ /// <para>
+ /// The parameters passed to the <paramref name="trampoline"/> function are:
+ /// </para>
+ /// <list type="number">
+ /// <item>
+ /// <term>delegateObj</term>
+ /// <description>The given <paramref name="delegate"/>, upcast to <see cref="object"/>.</description>
+ /// </item>
+ /// <item>
+ /// <term>args</term>
+ /// <description>Array of <see cref="godot_variant"/> arguments.</description>
+ /// </item>
+ /// <item>
+ /// <term>ret</term>
+ /// <description>Return value of type <see cref="godot_variant"/>.</description>
+ /// </item>
+ ///</list>
+ /// <para>
+ /// The delegate should be downcast to a more specific delegate type before invoking.
+ /// </para>
+ /// </summary>
+ /// <example>
+ /// Usage example:
+ ///
+ /// <code>
+ /// static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ /// {
+ /// if (args.Count != 1)
+ /// throw new ArgumentException($&quot;Callable expected {1} arguments but received {args.Count}.&quot;);
+ ///
+ /// TResult res = ((Func&lt;int, string&gt;)delegateObj)(
+ /// VariantConversionCallbacks.GetToManagedCallback&lt;int&gt;()(args[0])
+ /// );
+ ///
+ /// ret = VariantConversionCallbacks.GetToVariantCallback&lt;string&gt;()(res);
+ /// }
+ ///
+ /// var callable = Callable.CreateWithUnsafeTrampoline((int num) =&gt; &quot;foo&quot; + num.ToString(), &amp;Trampoline);
+ /// var res = (string)callable.Call(10);
+ /// Console.WriteLine(res);
+ /// </code>
+ /// </example>
+ /// <param name="delegate">Delegate method that will be called.</param>
+ /// <param name="trampoline">Trampoline function pointer for invoking the delegate.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe Callable CreateWithUnsafeTrampoline(Delegate @delegate,
+ delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> trampoline)
+ => new(@delegate, trampoline);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs
new file mode 100644
index 0000000000..6c6a104019
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs
@@ -0,0 +1,480 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
+
+namespace Godot;
+
+#nullable enable
+
+public readonly partial struct Callable
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void ThrowIfArgCountMismatch(NativeVariantPtrArgs args, int countExpected,
+ [CallerArgumentExpression("args")] string? paramName = null)
+ {
+ if (countExpected != args.Count)
+ ThrowArgCountMismatch(countExpected, args.Count, paramName);
+
+ static void ThrowArgCountMismatch(int countExpected, int countReceived, string? paramName)
+ {
+ throw new ArgumentException(
+ "Invalid argument count for invoking callable." +
+ $" Expected {countExpected} arguments, received {countReceived}.",
+ paramName);
+ }
+ }
+
+ /// <summary>
+ /// Constructs a new <see cref="Callable"/> for the given <paramref name="action"/>.
+ /// </summary>
+ /// <param name="action">Action method that will be called.</param>
+ public static unsafe Callable From(
+ Action action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 0);
+
+ ((Action)delegateObj)();
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0>(
+ Action<T0> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 1);
+
+ ((Action<T0>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1>(
+ Action<T0, T1> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 2);
+
+ ((Action<T0, T1>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2>(
+ Action<T0, T1, T2> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 3);
+
+ ((Action<T0, T1, T2>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3>(
+ Action<T0, T1, T2, T3> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 4);
+
+ ((Action<T0, T1, T2, T3>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4>(
+ Action<T0, T1, T2, T3, T4> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 5);
+
+ ((Action<T0, T1, T2, T3, T4>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5>(
+ Action<T0, T1, T2, T3, T4, T5> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 6);
+
+ ((Action<T0, T1, T2, T3, T4, T5>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
+ VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6>(
+ Action<T0, T1, T2, T3, T4, T5, T6> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 7);
+
+ ((Action<T0, T1, T2, T3, T4, T5, T6>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
+ VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
+ VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7>(
+ Action<T0, T1, T2, T3, T4, T5, T6, T7> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 8);
+
+ ((Action<T0, T1, T2, T3, T4, T5, T6, T7>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
+ VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
+ VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
+ VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8>(
+ Action<T0, T1, T2, T3, T4, T5, T6, T7, T8> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 9);
+
+ ((Action<T0, T1, T2, T3, T4, T5, T6, T7, T8>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
+ VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
+ VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
+ VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7]),
+ VariantConversionCallbacks.GetToManagedCallback<T8>()(args[8])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <summary>
+ /// Constructs a new <see cref="Callable"/> for the given <paramref name="func"/>.
+ /// </summary>
+ /// <param name="func">Action method that will be called.</param>
+ public static unsafe Callable From<TResult>(
+ Func<TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 0);
+
+ TResult res = ((Func<TResult>)delegateObj)();
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, TResult>(
+ Func<T0, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 1);
+
+ TResult res = ((Func<T0, TResult>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0])
+ );
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, TResult>(
+ Func<T0, T1, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 2);
+
+ TResult res = ((Func<T0, T1, TResult>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1])
+ );
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, TResult>(
+ Func<T0, T1, T2, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 3);
+
+ TResult res = ((Func<T0, T1, T2, TResult>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2])
+ );
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, TResult>(
+ Func<T0, T1, T2, T3, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 4);
+
+ TResult res = ((Func<T0, T1, T2, T3, TResult>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3])
+ );
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, TResult>(
+ Func<T0, T1, T2, T3, T4, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 5);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, TResult>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4])
+ );
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, TResult>(
+ Func<T0, T1, T2, T3, T4, T5, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 6);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, T5, TResult>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
+ VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5])
+ );
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, TResult>(
+ Func<T0, T1, T2, T3, T4, T5, T6, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 7);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, TResult>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
+ VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
+ VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6])
+ );
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, TResult>(
+ Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 8);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
+ VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
+ VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
+ VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7])
+ );
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>(
+ Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 9);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>)delegateObj)(
+ VariantConversionCallbacks.GetToManagedCallback<T0>()(args[0]),
+ VariantConversionCallbacks.GetToManagedCallback<T1>()(args[1]),
+ VariantConversionCallbacks.GetToManagedCallback<T2>()(args[2]),
+ VariantConversionCallbacks.GetToManagedCallback<T3>()(args[3]),
+ VariantConversionCallbacks.GetToManagedCallback<T4>()(args[4]),
+ VariantConversionCallbacks.GetToManagedCallback<T5>()(args[5]),
+ VariantConversionCallbacks.GetToManagedCallback<T6>()(args[6]),
+ VariantConversionCallbacks.GetToManagedCallback<T7>()(args[7]),
+ VariantConversionCallbacks.GetToManagedCallback<T8>()(args[8])
+ );
+
+ ret = VariantConversionCallbacks.GetToVariantCallback<TResult>()(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index 9b3969d453..d19e0c08f2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -30,33 +30,23 @@ namespace Godot
}
[UnmanagedCallersOnly]
- internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, godot_variant** args, uint argc,
- godot_variant* outRet)
+ internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, void* trampoline,
+ godot_variant** args, int argc, godot_variant* outRet)
{
try
{
- // TODO: Optimize
- var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!;
- var managedArgs = new object?[argc];
-
- var parameterInfos = @delegate.Method.GetParameters();
- var paramsLength = parameterInfos.Length;
-
- if (argc != paramsLength)
+ if (trampoline == null)
{
- throw new InvalidOperationException(
- $"The delegate expects {paramsLength} arguments, but received {argc}.");
+ throw new ArgumentNullException(nameof(trampoline),
+ "Cannot dynamically invoke delegate because the trampoline is null.");
}
- for (uint i = 0; i < argc; i++)
- {
- managedArgs[i] = Marshaling.ConvertVariantToManagedObjectOfType(
- *args[i], parameterInfos[i].ParameterType);
- }
+ var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!;
+ var trampolineFn = (delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void>)trampoline;
- object? invokeRet = @delegate.DynamicInvoke(managedArgs);
+ trampolineFn(@delegate, new NativeVariantPtrArgs(args, argc), out godot_variant ret);
- *outRet = Marshaling.ConvertManagedObjectToVariant(invokeRet);
+ *outRet = ret;
}
catch (Exception e)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index 93103d0f6b..f8793332a0 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -356,35 +356,47 @@ namespace Godot.Collections
IDictionary<TKey, TValue>,
IReadOnlyDictionary<TKey, TValue>
{
+ private static godot_variant ToVariantFunc(in Dictionary<TKey, TValue> godotDictionary) =>
+ VariantUtils.CreateFromDictionary(godotDictionary);
+
+ private static Dictionary<TKey, TValue> FromVariantFunc(in godot_variant variant) =>
+ VariantUtils.ConvertToDictionaryObject<TKey, TValue>(variant);
+
// ReSharper disable StaticMemberInGenericType
// Warning is about unique static fields being created for each generic type combination:
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
// In our case this is exactly what we want.
- private static unsafe delegate* managed<in TKey, godot_variant> _convertKeyToVariantCallback;
- private static unsafe delegate* managed<in godot_variant, TKey> _convertKeyToManagedCallback;
- private static unsafe delegate* managed<in TValue, godot_variant> _convertValueToVariantCallback;
- private static unsafe delegate* managed<in godot_variant, TValue> _convertValueToManagedCallback;
+ private static readonly unsafe delegate* managed<in TKey, godot_variant> ConvertKeyToVariantCallback;
+ private static readonly unsafe delegate* managed<in godot_variant, TKey> ConvertKeyToManagedCallback;
+ private static readonly unsafe delegate* managed<in TValue, godot_variant> ConvertValueToVariantCallback;
+ private static readonly unsafe delegate* managed<in godot_variant, TValue> ConvertValueToManagedCallback;
// ReSharper restore StaticMemberInGenericType
static unsafe Dictionary()
{
- _convertKeyToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TKey>();
- _convertKeyToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TKey>();
- _convertValueToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TValue>();
- _convertValueToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TValue>();
+ VariantConversionCallbacks.GenericConversionCallbacks[typeof(Dictionary<TKey, TValue>)] =
+ (
+ (IntPtr)(delegate* managed<in Dictionary<TKey, TValue>, godot_variant>)&ToVariantFunc,
+ (IntPtr)(delegate* managed<in godot_variant, Dictionary<TKey, TValue>>)&FromVariantFunc
+ );
+
+ ConvertKeyToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TKey>();
+ ConvertKeyToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TKey>();
+ ConvertValueToVariantCallback = VariantConversionCallbacks.GetToVariantCallback<TValue>();
+ ConvertValueToManagedCallback = VariantConversionCallbacks.GetToManagedCallback<TValue>();
}
private static unsafe void ValidateVariantConversionCallbacks()
{
- if (_convertKeyToVariantCallback == null || _convertKeyToManagedCallback == null)
+ if (ConvertKeyToVariantCallback == null || ConvertKeyToManagedCallback == null)
{
throw new InvalidOperationException(
$"The dictionary key type is not supported for conversion to Variant: '{typeof(TKey).FullName}'.");
}
- if (_convertValueToVariantCallback == null || _convertValueToManagedCallback == null)
+ if (ConvertValueToVariantCallback == null || ConvertValueToManagedCallback == null)
{
throw new InvalidOperationException(
$"The dictionary value type is not supported for conversion to Variant: '{typeof(TValue).FullName}'.");
@@ -473,14 +485,14 @@ namespace Godot.Collections
{
get
{
- using var variantKey = _convertKeyToVariantCallback(key);
+ using var variantKey = ConvertKeyToVariantCallback(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant value).ToBool())
{
using (value)
- return _convertValueToManagedCallback(value);
+ return ConvertValueToManagedCallback(value);
}
else
{
@@ -489,8 +501,8 @@ namespace Godot.Collections
}
set
{
- using var variantKey = _convertKeyToVariantCallback(key);
- using var variantValue = _convertValueToVariantCallback(value);
+ using var variantKey = ConvertKeyToVariantCallback(key);
+ using var variantValue = ConvertValueToVariantCallback(value);
var self = (godot_dictionary)_underlyingDict.NativeValue;
NativeFuncs.godotsharp_dictionary_set_value(ref self,
variantKey, variantValue);
@@ -539,8 +551,8 @@ namespace Godot.Collections
using (value)
{
return new KeyValuePair<TKey, TValue>(
- _convertKeyToManagedCallback(key),
- _convertValueToManagedCallback(value));
+ ConvertKeyToManagedCallback(key),
+ ConvertValueToManagedCallback(value));
}
}
@@ -552,13 +564,13 @@ namespace Godot.Collections
/// <param name="value">The object to add.</param>
public unsafe void Add(TKey key, TValue value)
{
- using var variantKey = _convertKeyToVariantCallback(key);
+ using var variantKey = ConvertKeyToVariantCallback(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
throw new ArgumentException("An element with the same key already exists.", nameof(key));
- using var variantValue = _convertValueToVariantCallback(value);
+ using var variantValue = ConvertValueToVariantCallback(value);
NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
}
@@ -569,7 +581,7 @@ namespace Godot.Collections
/// <returns>Whether or not this dictionary contains the given key.</returns>
public unsafe bool ContainsKey(TKey key)
{
- using var variantKey = _convertKeyToVariantCallback(key);
+ using var variantKey = ConvertKeyToVariantCallback(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool();
}
@@ -580,7 +592,7 @@ namespace Godot.Collections
/// <param name="key">The key of the element to remove.</param>
public unsafe bool Remove(TKey key)
{
- using var variantKey = _convertKeyToVariantCallback(key);
+ using var variantKey = ConvertKeyToVariantCallback(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool();
}
@@ -593,13 +605,13 @@ namespace Godot.Collections
/// <returns>If an object was found for the given <paramref name="key"/>.</returns>
public unsafe bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
- using var variantKey = _convertKeyToVariantCallback(key);
+ using var variantKey = ConvertKeyToVariantCallback(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
using (retValue)
- value = found ? _convertValueToManagedCallback(retValue) : default;
+ value = found ? ConvertValueToManagedCallback(retValue) : default;
return found;
}
@@ -625,7 +637,7 @@ namespace Godot.Collections
unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
- using var variantKey = _convertKeyToVariantCallback(item.Key);
+ using var variantKey = ConvertKeyToVariantCallback(item.Key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
@@ -635,7 +647,7 @@ namespace Godot.Collections
if (!found)
return false;
- using var variantValue = _convertValueToVariantCallback(item.Value);
+ using var variantValue = ConvertValueToVariantCallback(item.Value);
return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
}
}
@@ -670,7 +682,7 @@ namespace Godot.Collections
unsafe bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
- using var variantKey = _convertKeyToVariantCallback(item.Key);
+ using var variantKey = ConvertKeyToVariantCallback(item.Key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
@@ -680,7 +692,7 @@ namespace Godot.Collections
if (!found)
return false;
- using var variantValue = _convertValueToVariantCallback(item.Value);
+ using var variantValue = ConvertValueToVariantCallback(item.Value);
if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
{
return NativeFuncs.godotsharp_dictionary_remove_key(
@@ -717,6 +729,7 @@ namespace Godot.Collections
public static implicit operator Variant(Dictionary<TKey, TValue> from) => Variant.CreateFrom(from);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static explicit operator Dictionary<TKey, TValue>(Variant from) => from.AsGodotDictionary<TKey, TValue>();
+ public static explicit operator Dictionary<TKey, TValue>(Variant from) =>
+ from.AsGodotDictionary<TKey, TValue>();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
index 76b186cd15..649661ee06 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -721,10 +721,19 @@ namespace Godot.NativeInterop
if (p_managed_callable.Delegate != null)
{
var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate);
- IntPtr objectPtr = p_managed_callable.Target != null ? Object.GetPtr(p_managed_callable.Target) : IntPtr.Zero;
- NativeFuncs.godotsharp_callable_new_with_delegate(
- GCHandle.ToIntPtr(gcHandle), objectPtr, out godot_callable callable);
- return callable;
+
+ IntPtr objectPtr = p_managed_callable.Target != null ?
+ Object.GetPtr(p_managed_callable.Target) :
+ IntPtr.Zero;
+
+ unsafe
+ {
+ NativeFuncs.godotsharp_callable_new_with_delegate(
+ GCHandle.ToIntPtr(gcHandle), (IntPtr)p_managed_callable.Trampoline,
+ objectPtr, out godot_callable callable);
+
+ return callable;
+ }
}
else
{
@@ -748,19 +757,22 @@ namespace Godot.NativeInterop
public static Callable ConvertCallableToManaged(in godot_callable p_callable)
{
if (NativeFuncs.godotsharp_callable_get_data_for_marshalling(p_callable,
- out IntPtr delegateGCHandle, out IntPtr godotObject,
- out godot_string_name name).ToBool())
+ out IntPtr delegateGCHandle, out IntPtr trampoline,
+ out IntPtr godotObject, out godot_string_name name).ToBool())
{
if (delegateGCHandle != IntPtr.Zero)
{
- return new Callable((Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target);
- }
- else
- {
- return new Callable(
- InteropUtils.UnmanagedGetManaged(godotObject),
- StringName.CreateTakingOwnershipOfDisposableValue(name));
+ unsafe
+ {
+ return Callable.CreateWithUnsafeTrampoline(
+ (Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target,
+ (delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void>)trampoline);
+ }
}
+
+ return new Callable(
+ InteropUtils.UnmanagedGetManaged(godotObject),
+ StringName.CreateTakingOwnershipOfDisposableValue(name));
}
// Some other unsupported callable
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
index 20ede9f0dd..088f4e7ecf 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -141,11 +141,11 @@ namespace Godot.NativeInterop
public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest,
in godot_string p_element);
- public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, IntPtr p_object,
- out godot_callable r_callable);
+ public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, IntPtr p_trampoline,
+ IntPtr p_object, out godot_callable r_callable);
internal static partial godot_bool godotsharp_callable_get_data_for_marshalling(in godot_callable p_callable,
- out IntPtr r_delegate_handle, out IntPtr r_object, out godot_string_name r_name);
+ out IntPtr r_delegate_handle, out IntPtr r_trampoline, out IntPtr r_object, out godot_string_name r_name);
internal static partial godot_variant godotsharp_callable_call(in godot_callable p_callable,
godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs
index 422df74c23..d8c5d99cb8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs
@@ -8,8 +8,22 @@ namespace Godot.NativeInterop
public unsafe ref struct NativeVariantPtrArgs
{
private godot_variant** _args;
+ private int _argc;
- internal NativeVariantPtrArgs(godot_variant** args) => _args = args;
+ internal NativeVariantPtrArgs(godot_variant** args, int argc)
+ {
+ _args = args;
+ _argc = argc;
+ }
+
+ /// <summary>
+ /// Returns the number of arguments.
+ /// </summary>
+ public int Count
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _argc;
+ }
public ref godot_variant this[int index]
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs
index 9cde62c7c5..4b3db0c01a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs
@@ -1,10 +1,15 @@
using System;
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
namespace Godot.NativeInterop;
+// TODO: Change VariantConversionCallbacks<T>. Store the callback in a static field for quick repeated access, instead of checking every time.
internal static unsafe class VariantConversionCallbacks
{
+ internal static System.Collections.Generic.Dictionary<Type, (IntPtr ToVariant, IntPtr FromVariant)>
+ GenericConversionCallbacks = new();
+
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
internal static delegate*<in T, godot_variant> GetToVariantCallback<T>()
{
@@ -502,6 +507,26 @@ internal static unsafe class VariantConversionCallbacks
&FromVariant;
}
+ // TODO:
+ // IsGenericType and GetGenericTypeDefinition don't work in NativeAOT's reflection-free mode.
+ // We could make the Godot collections implement an interface and use IsAssignableFrom instead.
+ // Or we could just skip the check and always look for a conversion callback for the type.
+ if (typeOfT.IsGenericType)
+ {
+ var genericTypeDef = typeOfT.GetGenericTypeDefinition();
+
+ if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>) ||
+ genericTypeDef == typeof(Godot.Collections.Array<>))
+ {
+ RuntimeHelpers.RunClassConstructor(typeOfT.TypeHandle);
+
+ if (GenericConversionCallbacks.TryGetValue(typeOfT, out var genericConversion))
+ {
+ return (delegate*<in T, godot_variant>)genericConversion.ToVariant;
+ }
+ }
+ }
+
return null;
}
@@ -1005,6 +1030,26 @@ internal static unsafe class VariantConversionCallbacks
&ToVariant;
}
+ // TODO:
+ // IsGenericType and GetGenericTypeDefinition don't work in NativeAOT's reflection-free mode.
+ // We could make the Godot collections implement an interface and use IsAssignableFrom instead.
+ // Or we could just skip the check and always look for a conversion callback for the type.
+ if (typeOfT.IsGenericType)
+ {
+ var genericTypeDef = typeOfT.GetGenericTypeDefinition();
+
+ if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>) ||
+ genericTypeDef == typeof(Godot.Collections.Array<>))
+ {
+ RuntimeHelpers.RunClassConstructor(typeOfT.TypeHandle);
+
+ if (GenericConversionCallbacks.TryGetValue(typeOfT, out var genericConversion))
+ {
+ return (delegate*<in godot_variant, T>)genericConversion.FromVariant;
+ }
+ }
+ }
+
// ReSharper restore RedundantCast
return null;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index 5cb678c280..60ee6eb6f4 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -202,7 +202,7 @@ namespace Godot
// ReSharper disable once VirtualMemberNeverOverridden.Global
protected internal virtual void RaiseGodotClassSignalCallbacks(in godot_string_name signal,
- NativeVariantPtrArgs args, int argCount)
+ NativeVariantPtrArgs args)
{
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index a63b668387..e3fb254f49 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -52,6 +52,7 @@
<Compile Include="Core\AABB.cs" />
<Compile Include="Core\Bridge\GodotSerializationInfo.cs" />
<Compile Include="Core\Bridge\MethodInfo.cs" />
+ <Compile Include="Core\Callable.generics.cs" />
<Compile Include="Core\CustomGCHandle.cs" />
<Compile Include="Core\Array.cs" />
<Compile Include="Core\Attributes\AssemblyHasScriptsAttribute.cs" />
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
index 2717b945f6..e20a88076a 100644
--- a/modules/mono/glue/runtime_interop.cpp
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -447,15 +447,16 @@ void godotsharp_packed_string_array_add(PackedStringArray *r_dest, const String
r_dest->append(*p_element);
}
-void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, const Object *p_object, Callable *r_callable) {
+void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, void *p_trampoline,
+ const Object *p_object, Callable *r_callable) {
// TODO: Use pooling for ManagedCallable instances.
ObjectID objid = p_object ? p_object->get_instance_id() : ObjectID();
- CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle, objid));
+ CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle, p_trampoline, objid));
memnew_placement(r_callable, Callable(managed_callable));
}
bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable,
- GCHandleIntPtr *r_delegate_handle, Object **r_object, StringName *r_name) {
+ GCHandleIntPtr *r_delegate_handle, void **r_trampoline, Object **r_object, StringName *r_name) {
if (p_callable->is_custom()) {
CallableCustom *custom = p_callable->get_custom();
CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
@@ -463,18 +464,21 @@ bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable,
if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) {
ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom);
*r_delegate_handle = managed_callable->get_delegate();
+ *r_trampoline = managed_callable->get_trampoline();
*r_object = nullptr;
memnew_placement(r_name, StringName());
return true;
} else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
*r_delegate_handle = { nullptr };
+ *r_trampoline = nullptr;
*r_object = ObjectDB::get_instance(signal_awaiter_callable->get_object());
memnew_placement(r_name, StringName(signal_awaiter_callable->get_signal()));
return true;
} else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) {
EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom);
*r_delegate_handle = { nullptr };
+ *r_trampoline = nullptr;
*r_object = ObjectDB::get_instance(event_signal_callable->get_object());
memnew_placement(r_name, StringName(event_signal_callable->get_signal()));
return true;
@@ -482,11 +486,13 @@ bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable,
// Some other CallableCustom. We only support ManagedCallable.
*r_delegate_handle = { nullptr };
+ *r_trampoline = nullptr;
*r_object = nullptr;
memnew_placement(r_name, StringName());
return false;
} else {
*r_delegate_handle = { nullptr };
+ *r_trampoline = nullptr;
*r_object = ObjectDB::get_instance(p_callable->get_object_id());
memnew_placement(r_name, StringName(p_callable->get_method()));
return true;
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index 0c2c533090..28edc41d98 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -92,7 +92,7 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant
ERR_FAIL_COND(delegate_handle.value == nullptr);
GDMonoCache::managed_callbacks.DelegateUtils_InvokeWithVariantArgs(
- delegate_handle, p_arguments, p_argcount, &r_return_value);
+ delegate_handle, trampoline, p_arguments, p_argcount, &r_return_value);
r_call_error.error = Callable::CallError::CALL_OK;
}
@@ -106,7 +106,8 @@ void ManagedCallable::release_delegate_handle() {
// Why you do this clang-format...
/* clang-format off */
-ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id) : delegate_handle(p_delegate_handle), object_id(p_object_id) {
+ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle, void *p_trampoline, ObjectID p_object_id) :
+ delegate_handle(p_delegate_handle), trampoline(p_trampoline), object_id(p_object_id) {
#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(instances_mutex);
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index 26cd164fb6..b3a137dedf 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -40,6 +40,7 @@
class ManagedCallable : public CallableCustom {
friend class CSharpLanguage;
GCHandleIntPtr delegate_handle;
+ void *trampoline = nullptr;
ObjectID object_id;
#ifdef GD_MONO_HOT_RELOAD
@@ -58,6 +59,7 @@ public:
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
_FORCE_INLINE_ GCHandleIntPtr get_delegate() const { return delegate_handle; }
+ _FORCE_INLINE_ void *get_trampoline() const { return trampoline; }
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
@@ -67,7 +69,7 @@ public:
void release_delegate_handle();
- ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id);
+ ManagedCallable(GCHandleIntPtr p_delegate_handle, void *p_trampoline, ObjectID p_object_id);
~ManagedCallable();
};
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 13b599fe55..9c26fa2b0a 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -74,7 +74,7 @@ struct ManagedCallbacks {
using Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, void *p_def_vals, int32_t p_count);
using FuncSignalAwaiter_SignalCallback = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, int32_t, bool *);
- using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, uint32_t, const Variant *);
+ using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, void *, const Variant **, int32_t, const Variant *);
using FuncDelegateUtils_DelegateEquals = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr);
using FuncDelegateUtils_TrySerializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const Array *);
using FuncDelegateUtils_TryDeserializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(const Array *, GCHandleIntPtr *);
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index da425076a4..659ce7316a 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -72,7 +72,7 @@ void SceneReplicationInterface::_free_remotes(const PeerInfo &p_info) {
for (const KeyValue<uint32_t, ObjectID> &E : p_info.recv_nodes) {
Node *node = tracked_nodes.has(E.value) ? get_id_as<Node>(E.value) : nullptr;
ERR_CONTINUE(!node);
- node->queue_delete();
+ node->queue_free();
}
}
@@ -581,7 +581,7 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p
if (node->get_parent() != nullptr) {
node->get_parent()->remove_child(node);
}
- node->queue_delete();
+ node->queue_free();
spawner->emit_signal(SNAME("despawned"), node);
return OK;
diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp
index 51c402d746..b03402e80d 100644
--- a/modules/openxr/editor/openxr_action_map_editor.cpp
+++ b/modules/openxr/editor/openxr_action_map_editor.cpp
@@ -128,7 +128,7 @@ void OpenXRActionMapEditor::_update_interaction_profiles() {
interaction_profiles.remove_at(0);
tabs->remove_child(interaction_profile);
- interaction_profile->queue_delete();
+ interaction_profile->queue_free();
}
// in with the new...
@@ -205,7 +205,7 @@ void OpenXRActionMapEditor::_on_remove_action_set(Object *p_action_set_editor) {
action_map->remove_action_set(action_set);
actionsets_vb->remove_child(action_set_editor);
- action_set_editor->queue_delete();
+ action_set_editor->queue_free();
}
void OpenXRActionMapEditor::_on_action_removed() {
@@ -290,7 +290,7 @@ void OpenXRActionMapEditor::_on_tab_button_pressed(int p_tab) {
action_map->remove_interaction_profile(interaction_profile);
tabs->remove_child(profile_editor);
- profile_editor->queue_delete();
+ profile_editor->queue_free();
}
void OpenXRActionMapEditor::open_action_map(String p_path) {
diff --git a/modules/openxr/editor/openxr_action_set_editor.cpp b/modules/openxr/editor/openxr_action_set_editor.cpp
index 804808a6b9..3869146e8e 100644
--- a/modules/openxr/editor/openxr_action_set_editor.cpp
+++ b/modules/openxr/editor/openxr_action_set_editor.cpp
@@ -140,7 +140,7 @@ void OpenXRActionSetEditor::_on_remove_action(Object *p_action_editor) {
// And remove it....
action_map->remove_action(action->get_name_with_set()); // remove it from the set and any interaction profile it relates to
actions_vb->remove_child(action_editor);
- action_editor->queue_delete();
+ action_editor->queue_free();
// Let action map editor know so we can update our interaction profiles
emit_signal("action_removed");
diff --git a/modules/webp/resource_saver_webp.cpp b/modules/webp/resource_saver_webp.cpp
index bd71c2869a..56ff8097b4 100644
--- a/modules/webp/resource_saver_webp.cpp
+++ b/modules/webp/resource_saver_webp.cpp
@@ -38,8 +38,8 @@
Error ResourceSaverWebP::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Ref<ImageTexture> texture = p_resource;
- ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as WEBP.");
- ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as WEBP.");
+ ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as WebP.");
+ ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as WebP.");
Ref<Image> img = texture->get_image();
@@ -52,7 +52,7 @@ Error ResourceSaverWebP::save_image(const String &p_path, const Ref<Image> &p_im
Vector<uint8_t> buffer = save_image_to_buffer(p_img, p_lossy, p_quality);
Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
- ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WEBP at path: '%s'.", p_path));
+ ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WebP at path: '%s'.", p_path));
const uint8_t *reader = buffer.ptr();
diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp
index 049c1c3a32..c6440f1796 100644
--- a/modules/webp/webp_common.cpp
+++ b/modules/webp/webp_common.cpp
@@ -139,7 +139,7 @@ Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
ERR_FAIL_COND_V(r[0] != 'R' || r[1] != 'I' || r[2] != 'F' || r[3] != 'F' || r[8] != 'W' || r[9] != 'E' || r[10] != 'B' || r[11] != 'P', Ref<Image>());
WebPBitstreamFeatures features;
if (WebPGetFeatures(r, size, &features) != VP8_STATUS_OK) {
- ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WEBP image.");
+ ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WebP image.");
}
Vector<uint8_t> dst_image;
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index 49ff454f07..49dd9f7318 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -18,16 +18,16 @@
func _ready():
# We assume this node has a button as a child.
# This button is for the user to consent to entering immersive VR mode.
- $Button.connect("pressed", self, "_on_Button_pressed")
+ $Button.pressed.connect(self._on_Button_pressed)
webxr_interface = XRServer.find_interface("WebXR")
if webxr_interface:
# WebXR uses a lot of asynchronous callbacks, so we connect to various
# signals in order to receive them.
- webxr_interface.connect("session_supported", self, "_webxr_session_supported")
- webxr_interface.connect("session_started", self, "_webxr_session_started")
- webxr_interface.connect("session_ended", self, "_webxr_session_ended")
- webxr_interface.connect("session_failed", self, "_webxr_session_failed")
+ webxr_interface.session_supported.connect(self._webxr_session_supported)
+ webxr_interface.session_started.connect(self._webxr_session_started)
+ webxr_interface.session_ended.connect(self._webxr_session_ended)
+ webxr_interface.session_failed.connect(self._webxr_session_failed)
# This returns immediately - our _webxr_session_supported() method
# (which we connected to the "session_supported" signal above) will
diff --git a/platform/android/jni_utils.cpp b/platform/android/jni_utils.cpp
index d46b4f39de..2b0ee50570 100644
--- a/platform/android/jni_utils.cpp
+++ b/platform/android/jni_utils.cpp
@@ -265,33 +265,33 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) {
if (name == "[D") {
jdoubleArray arr = (jdoubleArray)obj;
int fCount = env->GetArrayLength(arr);
- PackedFloat32Array sarr;
- sarr.resize(fCount);
+ PackedFloat64Array packed_array;
+ packed_array.resize(fCount);
- real_t *w = sarr.ptrw();
+ double *w = packed_array.ptrw();
for (int i = 0; i < fCount; i++) {
double n;
env->GetDoubleArrayRegion(arr, i, 1, &n);
w[i] = n;
}
- return sarr;
+ return packed_array;
}
if (name == "[F") {
jfloatArray arr = (jfloatArray)obj;
int fCount = env->GetArrayLength(arr);
- PackedFloat32Array sarr;
- sarr.resize(fCount);
+ PackedFloat32Array packed_array;
+ packed_array.resize(fCount);
- real_t *w = sarr.ptrw();
+ float *w = packed_array.ptrw();
for (int i = 0; i < fCount; i++) {
float n;
env->GetFloatArrayRegion(arr, i, 1, &n);
w[i] = n;
}
- return sarr;
+ return packed_array;
}
if (name == "[Ljava.lang.Object;") {
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index e865806a7f..f7a66215b2 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -1297,7 +1297,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
return;
}
- // Draw a placeholder for scenes needing one.
+ // Draw a placeholder for tiles needing one.
RenderingServer *rs = RenderingServer::get_singleton();
Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
for (const Vector2i &E_cell : p_quadrant->cells) {
@@ -1330,9 +1330,9 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
0.8);
// Draw a placeholder tile.
- Transform2D xform;
- xform.set_origin(map_to_local(E_cell) - quadrant_pos);
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ Transform2D cell_to_quadrant;
+ cell_to_quadrant.set_origin(map_to_local(E_cell) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, cell_to_quadrant);
rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
}
}
@@ -1632,13 +1632,13 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
color.push_back(debug_collision_color);
Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
- Transform2D qudrant_xform;
- qudrant_xform.set_origin(quadrant_pos);
- Transform2D global_transform_inv = (get_global_transform() * qudrant_xform).affine_inverse();
+ Transform2D quadrant_to_local;
+ quadrant_to_local.set_origin(quadrant_pos);
+ Transform2D global_to_quadrant = (get_global_transform() * quadrant_to_local).affine_inverse();
for (RID body : p_quadrant->bodies) {
- Transform2D xform = Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)) * global_transform_inv;
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ Transform2D body_to_quadrant = global_to_quadrant * Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM));
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, body_to_quadrant);
for (int shape_index = 0; shape_index < ps->body_get_shape_count(body); shape_index++) {
const RID &shape = ps->body_get_shape(body, shape_index);
PhysicsServer2D::ShapeType type = ps->shape_get_type(shape);
@@ -1815,9 +1815,9 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
- Transform2D xform;
- xform.set_origin(map_to_local(E_cell) - quadrant_pos);
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ Transform2D cell_to_quadrant;
+ cell_to_quadrant.set_origin(map_to_local(E_cell) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, cell_to_quadrant);
for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index);
@@ -1863,7 +1863,7 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
for (const KeyValue<Vector2i, String> &E : q.scenes) {
Node *node = get_node_or_null(E.value);
if (node) {
- node->queue_delete();
+ node->queue_free();
}
}
@@ -1911,7 +1911,7 @@ void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) {
Node *node = get_node_or_null(E.value);
if (node) {
- node->queue_delete();
+ node->queue_free();
}
}
@@ -1956,9 +1956,9 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
0.8);
// Draw a placeholder tile.
- Transform2D xform;
- xform.set_origin(map_to_local(E_cell) - quadrant_pos);
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ Transform2D cell_to_quadrant;
+ cell_to_quadrant.set_origin(map_to_local(E_cell) - quadrant_pos);
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, cell_to_quadrant);
rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color);
}
}
@@ -4056,9 +4056,5 @@ TileMap::TileMap() {
}
TileMap::~TileMap() {
- if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed));
- }
-
_clear_internals();
}
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index a45ef52452..45ff0a4b45 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -516,7 +516,7 @@ void RayCast3D::_clear_debug_shape() {
MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape);
if (mi->is_inside_tree()) {
- mi->queue_delete();
+ mi->queue_free();
} else {
memdelete(mi);
}
diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp
index e7d1a8ec7d..03cbd984cd 100644
--- a/scene/3d/shape_cast_3d.cpp
+++ b/scene/3d/shape_cast_3d.cpp
@@ -619,7 +619,7 @@ void ShapeCast3D::_clear_debug_shape() {
MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape);
if (mi->is_inside_tree()) {
- mi->queue_delete();
+ mi->queue_free();
} else {
memdelete(mi);
}
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index e306d00a51..59930a3fbb 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -2066,7 +2066,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
// Forcing the use of the original root because the scene where original player belongs may be not the active one
Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values(get_node(get_root()));
aux_player->seek(0.0f, true);
- aux_player->queue_delete();
+ aux_player->queue_free();
if (p_user_initiated) {
Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values();
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index bfc3c25fe6..b4bdda9ecb 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -548,14 +548,36 @@ SceneDebuggerTree::SceneDebuggerTree(Node *p_root) {
// Flatten tree into list, depth first, use stack to avoid recursion.
List<Node *> stack;
stack.push_back(p_root);
+ bool is_root = true;
+ const StringName &is_visible_sn = SNAME("is_visible");
+ const StringName &is_visible_in_tree_sn = SNAME("is_visible_in_tree");
while (stack.size()) {
Node *n = stack[0];
stack.pop_front();
+
int count = n->get_child_count();
- nodes.push_back(RemoteNode(count, n->get_name(), n->get_class(), n->get_instance_id()));
for (int i = 0; i < count; i++) {
stack.push_front(n->get_child(count - i - 1));
}
+
+ int view_flags = 0;
+ if (is_root) {
+ // Prevent root window visibility from being changed.
+ is_root = false;
+ } else if (n->has_method(is_visible_sn)) {
+ const Variant visible = n->call(is_visible_sn);
+ if (visible.get_type() == Variant::BOOL) {
+ view_flags = RemoteNode::VIEW_HAS_VISIBLE_METHOD;
+ view_flags |= uint8_t(visible) * RemoteNode::VIEW_VISIBLE;
+ }
+ if (n->has_method(is_visible_in_tree_sn)) {
+ const Variant visible_in_tree = n->call(is_visible_in_tree_sn);
+ if (visible_in_tree.get_type() == Variant::BOOL) {
+ view_flags |= uint8_t(visible_in_tree) * RemoteNode::VIEW_VISIBLE_IN_TREE;
+ }
+ }
+ }
+ nodes.push_back(RemoteNode(count, n->get_name(), n->get_class(), n->get_instance_id(), n->get_scene_file_path(), view_flags));
}
}
@@ -565,19 +587,23 @@ void SceneDebuggerTree::serialize(Array &p_arr) {
p_arr.push_back(n.name);
p_arr.push_back(n.type_name);
p_arr.push_back(n.id);
+ p_arr.push_back(n.scene_file_path);
+ p_arr.push_back(n.view_flags);
}
}
void SceneDebuggerTree::deserialize(const Array &p_arr) {
int idx = 0;
while (p_arr.size() > idx) {
- ERR_FAIL_COND(p_arr.size() < 4);
- CHECK_TYPE(p_arr[idx], INT);
- CHECK_TYPE(p_arr[idx + 1], STRING);
- CHECK_TYPE(p_arr[idx + 2], STRING);
- CHECK_TYPE(p_arr[idx + 3], INT);
- nodes.push_back(RemoteNode(p_arr[idx], p_arr[idx + 1], p_arr[idx + 2], p_arr[idx + 3]));
- idx += 4;
+ ERR_FAIL_COND(p_arr.size() < 6);
+ CHECK_TYPE(p_arr[idx], INT); // child_count.
+ CHECK_TYPE(p_arr[idx + 1], STRING); // name.
+ CHECK_TYPE(p_arr[idx + 2], STRING); // type_name.
+ CHECK_TYPE(p_arr[idx + 3], INT); // id.
+ CHECK_TYPE(p_arr[idx + 4], STRING); // scene_file_path.
+ CHECK_TYPE(p_arr[idx + 5], INT); // view_flags.
+ nodes.push_back(RemoteNode(p_arr[idx], p_arr[idx + 1], p_arr[idx + 2], p_arr[idx + 3], p_arr[idx + 4], p_arr[idx + 5]));
+ idx += 6;
}
}
diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h
index 911363f45d..fe35446aae 100644
--- a/scene/debugger/scene_debugger.h
+++ b/scene/debugger/scene_debugger.h
@@ -110,12 +110,23 @@ public:
String name;
String type_name;
ObjectID id;
+ String scene_file_path;
+ uint8_t view_flags = 0;
- RemoteNode(int p_child, const String &p_name, const String &p_type, ObjectID p_id) {
+ enum ViewFlags {
+ VIEW_HAS_VISIBLE_METHOD = 1 << 1,
+ VIEW_VISIBLE = 1 << 2,
+ VIEW_VISIBLE_IN_TREE = 1 << 3,
+ };
+
+ RemoteNode(int p_child, const String &p_name, const String &p_type, ObjectID p_id, const String p_scene_file_path, int p_view_flags) {
child_count = p_child;
name = p_name;
type_name = p_type;
id = p_id;
+
+ scene_file_path = p_scene_file_path;
+ view_flags = p_view_flags;
}
RemoteNode() {}
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 8a5d04f49c..f61fa29a33 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -2859,7 +2859,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
offset = line_height;
}
- max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
+ if (font.is_valid()) {
+ max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
+ }
code_completion_options.push_back(option);
}
@@ -2970,7 +2972,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
if (string_to_complete.length() == 0) {
code_completion_options.push_back(option);
- max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
+ if (font.is_valid()) {
+ max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
+ }
continue;
}
@@ -3076,7 +3080,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
option.matches.append_array(ssq_matches);
completion_options_subseq.push_back(option);
}
- max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
+ if (font.is_valid()) {
+ max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
+ }
} else if (!*ssq_lower) { // Matched the whole subsequence in s_lower.
option.matches.clear();
@@ -3093,7 +3099,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
option.matches.append_array(ssq_lower_matches);
completion_options_subseq_casei.push_back(option);
}
- max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
+ if (font.is_valid()) {
+ max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
+ }
}
}
diff --git a/scene/gui/color_mode.cpp b/scene/gui/color_mode.cpp
index 3a5013dabe..a063cd344a 100644
--- a/scene/gui/color_mode.cpp
+++ b/scene/gui/color_mode.cpp
@@ -284,46 +284,68 @@ Color ColorModeOKHSL::get_color() const {
}
void ColorModeOKHSL::slider_draw(int p_which) {
- Vector<Vector2> pos;
- pos.resize(4);
- Vector<Color> col;
- col.resize(4);
HSlider *slider = color_picker->get_slider(p_which);
Size2 size = slider->get_size();
- Color left_color;
- Color right_color;
- Color color = color_picker->get_pick_color();
const real_t margin = 16 * color_picker->get_theme_default_base_scale();
- if (p_which == ColorPicker::SLIDER_COUNT) {
- slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, 0), Size2(size.x, margin)), true);
-
- left_color = color;
- left_color.a = 0;
- right_color = color;
- right_color.a = 1;
- } else if (p_which == 0) {
+ if (p_which == 0) { // H
Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0));
slider->draw_texture_rect(hue, Rect2(Vector2(margin * -1, 0), Vector2(margin, size.x)), false);
return;
- } else {
- Color s_col;
- Color v_col;
- s_col.set_ok_hsl(color.get_h(), 0, color.get_v());
- left_color = (p_which == 1) ? s_col : Color(0, 0, 0);
- s_col.set_ok_hsl(color.get_h(), 1, color.get_v());
- v_col.set_ok_hsl(color.get_h(), color.get_s(), 1);
- right_color = (p_which == 1) ? s_col : v_col;
}
- col.set(0, left_color);
- col.set(1, right_color);
- col.set(2, right_color);
- col.set(3, left_color);
- pos.set(0, Vector2(0, 0));
- pos.set(1, Vector2(size.x, 0));
- pos.set(2, Vector2(size.x, margin));
- pos.set(3, Vector2(0, margin));
+
+ Vector<Vector2> pos;
+ Vector<Color> col;
+ Color left_color;
+ Color right_color;
+ Color color = color_picker->get_pick_color();
+
+ if (p_which == 2) { // L
+ pos.resize(6);
+ col.resize(6);
+ left_color = Color(0, 0, 0);
+ Color middle_color;
+ middle_color.set_ok_hsl(color.get_ok_hsl_h(), color.get_ok_hsl_s(), 0.5);
+ right_color.set_ok_hsl(color.get_ok_hsl_h(), color.get_ok_hsl_s(), 1);
+
+ col.set(0, left_color);
+ col.set(1, middle_color);
+ col.set(2, right_color);
+ col.set(3, right_color);
+ col.set(4, middle_color);
+ col.set(5, left_color);
+ pos.set(0, Vector2(0, 0));
+ pos.set(1, Vector2(size.x * 0.5, 0));
+ pos.set(2, Vector2(size.x, 0));
+ pos.set(3, Vector2(size.x, margin));
+ pos.set(4, Vector2(size.x * 0.5, margin));
+ pos.set(5, Vector2(0, margin));
+ } else { // A / S
+ pos.resize(4);
+ col.resize(4);
+
+ if (p_which == ColorPicker::SLIDER_COUNT) {
+ slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, 0), Size2(size.x, margin)), true);
+
+ left_color = color;
+ left_color.a = 0;
+ right_color = color;
+ right_color.a = 1;
+ } else {
+ left_color.set_ok_hsl(color.get_ok_hsl_h(), 0, color.get_ok_hsl_l());
+ right_color.set_ok_hsl(color.get_ok_hsl_h(), 1, color.get_ok_hsl_l());
+ }
+
+ col.set(0, left_color);
+ col.set(1, right_color);
+ col.set(2, right_color);
+ col.set(3, left_color);
+ pos.set(0, Vector2(0, 0));
+ pos.set(1, Vector2(size.x, 0));
+ pos.set(2, Vector2(size.x, margin));
+ pos.set(3, Vector2(0, margin));
+ }
slider->draw_polygon(pos, col);
}
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 929bf27be6..1009b36584 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -744,7 +744,7 @@ void ColorPicker::add_recent_preset(const Color &p_color) {
if (recent_preset_hbc->get_child_count() >= PRESET_COLUMN_COUNT) {
recent_preset_cache.pop_front();
recent_presets.pop_front();
- recent_preset_hbc->get_child(PRESET_COLUMN_COUNT - 1)->queue_delete();
+ recent_preset_hbc->get_child(PRESET_COLUMN_COUNT - 1)->queue_free();
}
recent_presets.push_back(p_color);
recent_preset_cache.push_back(p_color);
@@ -770,7 +770,7 @@ void ColorPicker::erase_preset(const Color &p_color) {
for (int i = 1; i < preset_container->get_child_count(); i++) {
ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(preset_container->get_child(i));
if (current_btn && p_color == current_btn->get_preset_color()) {
- current_btn->queue_delete();
+ current_btn->queue_free();
break;
}
}
@@ -794,7 +794,7 @@ void ColorPicker::erase_recent_preset(const Color &p_color) {
for (int i = 1; i < recent_preset_hbc->get_child_count(); i++) {
ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(recent_preset_hbc->get_child(i));
if (current_btn && p_color == current_btn->get_preset_color()) {
- current_btn->queue_delete();
+ current_btn->queue_free();
break;
}
}
@@ -1087,16 +1087,24 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
Vector<Color> colors;
Color col;
col.set_ok_hsl(h, s, 1);
- points.resize(4);
- colors.resize(4);
- points.set(0, Vector2());
- points.set(1, Vector2(c->get_size().x, 0));
+ Color col2;
+ col2.set_ok_hsl(h, s, 0.5);
+ Color col3;
+ col3.set_ok_hsl(h, s, 0);
+ points.resize(6);
+ colors.resize(6);
+ points.set(0, Vector2(c->get_size().x, 0));
+ points.set(1, Vector2(c->get_size().x, c->get_size().y * 0.5));
points.set(2, c->get_size());
points.set(3, Vector2(0, c->get_size().y));
+ points.set(4, Vector2(0, c->get_size().y * 0.5));
+ points.set(5, Vector2());
colors.set(0, col);
- colors.set(1, col);
- colors.set(2, Color(0, 0, 0));
- colors.set(3, Color(0, 0, 0));
+ colors.set(1, col2);
+ colors.set(2, col3);
+ colors.set(3, col3);
+ colors.set(4, col2);
+ colors.set(5, col);
c->draw_polygon(points, colors);
int y = c->get_size().y - c->get_size().y * CLAMP(v, 0, 1);
col.set_ok_hsl(h, 1, v);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 48853cde9c..e52cc07777 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -2094,6 +2094,17 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
accept_event();
return;
}
+
+ if (k->is_action("ui_text_caret_add_below", true)) {
+ add_caret_at_carets(true);
+ accept_event();
+ return;
+ }
+ if (k->is_action("ui_text_caret_add_above", true)) {
+ add_caret_at_carets(false);
+ accept_event();
+ return;
+ }
}
// MISC.
@@ -2808,6 +2819,51 @@ void TextEdit::_move_caret_document_end(bool p_select) {
}
}
+void TextEdit::_get_above_below_caret_line_column(int p_old_line, int p_old_wrap_index, int p_old_column, bool p_below, int &p_new_line, int &p_new_column, int p_last_fit_x) const {
+ if (p_last_fit_x == -1) {
+ p_last_fit_x = _get_column_x_offset_for_line(p_old_column, p_old_line, p_old_column);
+ }
+
+ // Calculate the new line and wrap index
+ p_new_line = p_old_line;
+ int caret_wrap_index = p_old_wrap_index;
+ if (p_below) {
+ if (caret_wrap_index < get_line_wrap_count(p_new_line)) {
+ caret_wrap_index++;
+ } else {
+ p_new_line++;
+ caret_wrap_index = 0;
+ }
+ } else {
+ if (caret_wrap_index == 0) {
+ p_new_line--;
+ caret_wrap_index = get_line_wrap_count(p_new_line);
+ } else {
+ caret_wrap_index--;
+ }
+ }
+
+ // Boundary checks
+ if (p_new_line < 0) {
+ p_new_line = 0;
+ }
+ if (p_new_line >= text.size()) {
+ p_new_line = text.size() - 1;
+ }
+
+ p_new_column = _get_char_pos_for_line(p_last_fit_x, p_new_line, caret_wrap_index);
+ if (p_new_column != 0 && get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && caret_wrap_index < get_line_wrap_count(p_new_line)) {
+ Vector<String> rows = get_line_wrapped_text(p_new_line);
+ int row_end_col = 0;
+ for (int i = 0; i < caret_wrap_index + 1; i++) {
+ row_end_col += rows[i].length();
+ }
+ if (p_new_column >= row_end_col) {
+ p_new_column -= 1;
+ }
+ }
+}
+
void TextEdit::_update_placeholder() {
if (font.is_null() || font_size <= 0) {
return; // Not in tree?
@@ -3815,6 +3871,9 @@ void TextEdit::undo() {
return;
}
+ if (in_action) {
+ pending_action_end = true;
+ }
_push_current_op();
if (undo_stack_pos == nullptr) {
@@ -3876,6 +3935,9 @@ void TextEdit::redo() {
return;
}
+ if (in_action) {
+ pending_action_end = true;
+ }
_push_current_op();
if (undo_stack_pos == nullptr) {
@@ -4509,6 +4571,68 @@ int TextEdit::get_caret_count() const {
return carets.size();
}
+void TextEdit::add_caret_at_carets(bool p_below) {
+ Vector<int> caret_edit_order = get_caret_index_edit_order();
+ for (const int &caret_index : caret_edit_order) {
+ const int caret_line = get_caret_line(caret_index);
+ const int caret_column = get_caret_column(caret_index);
+
+ // The last fit x will be cleared if the caret has a selection,
+ // but if it does not have a selection the last fit x will be
+ // transferred to the new caret
+ int caret_from_column = 0, caret_to_column = 0, caret_last_fit_x = carets[caret_index].last_fit_x;
+ if (has_selection(caret_index)) {
+ // If the selection goes over multiple lines, deselect it.
+ if (get_selection_from_line(caret_index) != get_selection_to_line(caret_index)) {
+ deselect(caret_index);
+ } else {
+ caret_from_column = get_selection_from_column(caret_index);
+ caret_to_column = get_selection_to_column(caret_index);
+ caret_last_fit_x = -1;
+ carets.write[caret_index].last_fit_x = _get_column_x_offset_for_line(caret_column, caret_line, caret_column);
+ }
+ }
+
+ // Get the line and column of the new caret as if you would move the caret by pressing the arrow keys
+ int new_caret_line, new_caret_column, new_caret_from_column = 0, new_caret_to_column = 0;
+ _get_above_below_caret_line_column(caret_line, get_caret_wrap_index(caret_index), caret_column, p_below, new_caret_line, new_caret_column, caret_last_fit_x);
+
+ // If the caret does have a selection calculate the new from and to columns
+ if (caret_from_column != caret_to_column) {
+ // We only need to calculate the selection columns if the column of the caret changed
+ if (caret_column != new_caret_column) {
+ int _; // unused placeholder for p_new_line
+ _get_above_below_caret_line_column(caret_line, get_caret_wrap_index(caret_index), caret_from_column, p_below, _, new_caret_from_column);
+ _get_above_below_caret_line_column(caret_line, get_caret_wrap_index(caret_index), caret_to_column, p_below, _, new_caret_to_column);
+ } else {
+ new_caret_from_column = caret_from_column;
+ new_caret_to_column = caret_to_column;
+ }
+ }
+
+ // Add the new caret
+ const int new_caret_index = add_caret(new_caret_line, new_caret_column);
+
+ if (new_caret_index == -1) {
+ continue;
+ }
+ // Also add the selection if there should be one
+ if (new_caret_from_column != new_caret_to_column) {
+ select(new_caret_line, new_caret_from_column, new_caret_line, new_caret_to_column, new_caret_index);
+ // Necessary to properly modify the selection after adding the new caret
+ carets.write[new_caret_index].selection.selecting_line = new_caret_line;
+ carets.write[new_caret_index].selection.selecting_column = new_caret_column == new_caret_from_column ? new_caret_to_column : new_caret_from_column;
+ continue;
+ }
+
+ // Copy the last fit x over
+ carets.write[new_caret_index].last_fit_x = carets[caret_index].last_fit_x;
+ }
+
+ merge_overlapping_carets();
+ queue_redraw();
+}
+
Vector<int> TextEdit::get_caret_index_edit_order() {
if (!caret_index_edit_dirty) {
return caret_index_edit_order;
@@ -6003,6 +6127,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_secondary_carets"), &TextEdit::remove_secondary_carets);
ClassDB::bind_method(D_METHOD("merge_overlapping_carets"), &TextEdit::merge_overlapping_carets);
ClassDB::bind_method(D_METHOD("get_caret_count"), &TextEdit::get_caret_count);
+ ClassDB::bind_method(D_METHOD("add_caret_at_carets", "below"), &TextEdit::add_caret_at_carets);
ClassDB::bind_method(D_METHOD("get_caret_index_edit_order"), &TextEdit::get_caret_index_edit_order);
ClassDB::bind_method(D_METHOD("adjust_carets_after_edit", "caret", "from_line", "from_col", "to_line", "to_col"), &TextEdit::adjust_carets_after_edit);
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 86b838e387..e57f321887 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -598,6 +598,9 @@ private:
void _move_caret_document_start(bool p_select);
void _move_caret_document_end(bool p_select);
+ // Used in add_caret_at_carets
+ void _get_above_below_caret_line_column(int p_old_line, int p_old_wrap_index, int p_old_column, bool p_below, int &p_new_line, int &p_new_column, int p_last_fit_x = -1) const;
+
protected:
void _notification(int p_what);
@@ -816,6 +819,7 @@ public:
void remove_secondary_carets();
void merge_overlapping_carets();
int get_caret_count() const;
+ void add_caret_at_carets(bool p_below);
Vector<int> get_caret_index_edit_order();
void adjust_carets_after_edit(int p_caret, int p_from_line, int p_from_col, int p_to_line, int p_to_col);
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index a9982b3ece..48c6dc5cfc 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -510,18 +510,38 @@ void TextureProgressBar::_notification(int p_what) {
}
pts.append(to);
+ Ref<AtlasTexture> atlas_progress = progress;
+ bool valid_atlas_progress = atlas_progress.is_valid() && atlas_progress->get_atlas().is_valid();
+ Rect2 region_rect;
+ Size2 atlas_size;
+ if (valid_atlas_progress) {
+ region_rect = atlas_progress->get_region();
+ atlas_size = atlas_progress->get_atlas()->get_size();
+ }
+
Vector<Point2> uvs;
Vector<Point2> points;
- uvs.push_back(get_relative_center());
- points.push_back(progress_offset + s * get_relative_center());
for (int i = 0; i < pts.size(); i++) {
Point2 uv = unit_val_to_uv(pts[i]);
if (uvs.find(uv) >= 0) {
continue;
}
- uvs.push_back(uv);
points.push_back(progress_offset + Point2(uv.x * s.x, uv.y * s.y));
+ if (valid_atlas_progress) {
+ uv.x = Math::remap(uv.x, 0, 1, region_rect.position.x / atlas_size.x, (region_rect.position.x + region_rect.size.x) / atlas_size.x);
+ uv.y = Math::remap(uv.y, 0, 1, region_rect.position.y / atlas_size.y, (region_rect.position.y + region_rect.size.y) / atlas_size.y);
+ }
+ uvs.push_back(uv);
+ }
+
+ Point2 center_point = get_relative_center();
+ points.push_back(progress_offset + s * center_point);
+ if (valid_atlas_progress) {
+ center_point.x = Math::remap(center_point.x, 0, 1, region_rect.position.x / atlas_size.x, (region_rect.position.x + region_rect.size.x) / atlas_size.x);
+ center_point.y = Math::remap(center_point.y, 0, 1, region_rect.position.y / atlas_size.y, (region_rect.position.y + region_rect.size.y) / atlas_size.y);
}
+ uvs.push_back(center_point);
+
Vector<Color> colors;
colors.push_back(tint_progress);
draw_polygon(points, colors, uvs, progress);
diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp
index 6dd83e4636..56e719968b 100644
--- a/scene/main/instance_placeholder.cpp
+++ b/scene/main/instance_placeholder.cpp
@@ -100,7 +100,7 @@ Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene
}
if (p_replace) {
- queue_delete();
+ queue_free();
base->remove_child(this);
}
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 2ea45df309..038f182e8b 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1341,12 +1341,23 @@ Node *Node::get_node(const NodePath &p_path) const {
Node *node = get_node_or_null(p_path);
if (unlikely(!node)) {
+ // Try to get a clear description of this node in the error message.
+ String desc;
+ if (is_inside_tree()) {
+ desc = get_path();
+ } else {
+ desc = get_name();
+ if (desc.is_empty()) {
+ desc = get_class();
+ }
+ }
+
if (p_path.is_absolute()) {
ERR_FAIL_V_MSG(nullptr,
- vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path()));
+ vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, desc));
} else {
ERR_FAIL_V_MSG(nullptr,
- vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path()));
+ vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, desc));
}
}
@@ -2569,7 +2580,7 @@ void Node::print_orphan_nodes() {
#endif
}
-void Node::queue_delete() {
+void Node::queue_free() {
if (is_inside_tree()) {
get_tree()->queue_delete(this);
} else {
@@ -2811,7 +2822,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_viewport"), &Node::get_viewport);
- ClassDB::bind_method(D_METHOD("queue_free"), &Node::queue_delete);
+ ClassDB::bind_method(D_METHOD("queue_free"), &Node::queue_free);
ClassDB::bind_method(D_METHOD("request_ready"), &Node::request_ready);
diff --git a/scene/main/node.h b/scene/main/node.h
index c8c8c395ce..4a3ec253b1 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -460,7 +460,7 @@ public:
#endif
static String adjust_name_casing(const String &p_name);
- void queue_delete();
+ void queue_free();
//hacks for speed
static void init_node_hrcr();
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index ad40346c36..02bde19201 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1161,7 +1161,7 @@ void Viewport::_gui_cancel_tooltip() {
gui.tooltip_timer = Ref<SceneTreeTimer>();
}
if (gui.tooltip_popup) {
- gui.tooltip_popup->queue_delete();
+ gui.tooltip_popup->queue_free();
}
}
diff --git a/scene/property_utils.cpp b/scene/property_utils.cpp
index a9b7e9acbe..f445634a45 100644
--- a/scene/property_utils.cpp
+++ b/scene/property_utils.cpp
@@ -169,8 +169,10 @@ static bool _collect_inheritance_chain(const Ref<SceneState> &p_state, const Nod
state = state->get_base_scene_state();
}
- for (int i = inheritance_states.size() - 1; i >= 0; --i) {
- r_states_stack.push_back(inheritance_states[i]);
+ if (inheritance_states.size() > 0) {
+ for (int i = inheritance_states.size() - 1; i >= 0; --i) {
+ r_states_stack.push_back(inheritance_states[i]);
+ }
}
return found;
@@ -214,10 +216,12 @@ Vector<SceneState::PackState> PropertyUtils::get_node_states_stack(const Node *p
{
states_stack_ret.resize(states_stack.size());
_FastPackState *ps = states_stack.ptr();
- for (int i = states_stack.size() - 1; i >= 0; --i) {
- states_stack_ret.write[i].state.reference_ptr(ps->state);
- states_stack_ret.write[i].node = ps->node;
- ++ps;
+ if (states_stack.size() > 0) {
+ for (int i = states_stack.size() - 1; i >= 0; --i) {
+ states_stack_ret.write[i].state.reference_ptr(ps->state);
+ states_stack_ret.write[i].node = ps->node;
+ ++ps;
+ }
}
}
return states_stack_ret;
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index fbcf140925..b994498dbf 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -653,7 +653,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si
Image::Format format = Image::Format(f->get_32());
if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP || data_format == DATA_FORMAT_BASIS_UNIVERSAL) {
- //look for a PNG or WEBP file inside
+ //look for a PNG or WebP file inside
int sw = w;
int sh = h;
diff --git a/servers/rendering/renderer_rd/shaders/cluster_render.glsl b/servers/rendering/renderer_rd/shaders/cluster_render.glsl
index 2fe230f0bf..932312de82 100644
--- a/servers/rendering/renderer_rd/shaders/cluster_render.glsl
+++ b/servers/rendering/renderer_rd/shaders/cluster_render.glsl
@@ -64,7 +64,7 @@ void main() {
#version 450
#VERSION_DEFINES
-
+#ifndef MOLTENVK_USED // Metal will corrupt GPU state otherwise
#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic) && defined(has_GL_KHR_shader_subgroup_vote)
#extension GL_KHR_shader_subgroup_ballot : enable
@@ -73,6 +73,7 @@ void main() {
#define USE_SUBGROUPS
#endif
+#endif
layout(location = 0) in float depth_interp;
layout(location = 1) in flat uint element_index;
diff --git a/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl b/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl
index b0a0839836..02566d8e35 100644
--- a/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/taa_resolve.glsl
@@ -32,7 +32,9 @@
// Based on Spartan Engine's TAA implementation (without TAA upscale).
// <https://github.com/PanosK92/SpartanEngine/blob/a8338d0609b85dc32f3732a5c27fb4463816a3b9/Data/shaders/temporal_antialiasing.hlsl>
+#ifndef MOLTENVK_USED
#define USE_SUBGROUPS
+#endif // MOLTENVK_USED
#define GROUP_SIZE 8
#define FLT_MIN 0.00000001
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 293532bf2d..0bdf0e50aa 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
@@ -97,9 +97,7 @@ layout(location = 8) out vec4 prev_screen_position;
#ifdef MATERIAL_UNIFORMS_USED
layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms{
-
#MATERIAL_UNIFORMS
-
} material;
#endif
@@ -691,7 +689,7 @@ vec4 fog_process(vec3 vertex) {
void cluster_get_item_range(uint p_offset, out uint item_min, out uint item_max, out uint item_from, out uint item_to) {
uint item_min_max = cluster_buffer.data[p_offset];
- item_min = item_min_max & 0xFFFF;
+ item_min = item_min_max & 0xFFFFu;
item_max = item_min_max >> 16;
item_from = item_min >> 5;
@@ -958,9 +956,9 @@ void fragment_shader(in SceneData scene_data) {
while (merged_mask != 0) {
uint bit = findMSB(merged_mask);
- merged_mask &= ~(1 << bit);
+ merged_mask &= ~(1u << bit);
#ifdef USE_SUBGROUPS
- if (((1 << bit) & mask) == 0) { //do not process if not originally here
+ if (((1u << bit) & mask) == 0) { //do not process if not originally here
continue;
}
#endif
@@ -1419,9 +1417,9 @@ void fragment_shader(in SceneData scene_data) {
while (merged_mask != 0) {
uint bit = findMSB(merged_mask);
- merged_mask &= ~(1 << bit);
+ merged_mask &= ~(1u << bit);
#ifdef USE_SUBGROUPS
- if (((1 << bit) & mask) == 0) { //do not process if not originally here
+ if (((1u << bit) & mask) == 0) { //do not process if not originally here
continue;
}
#endif
@@ -1781,9 +1779,9 @@ void fragment_shader(in SceneData scene_data) {
float shadow = 1.0;
#ifndef SHADOWS_DISABLED
if (i < 4) {
- shadow = float(shadow0 >> (i * 8) & 0xFF) / 255.0;
+ shadow = float(shadow0 >> (i * 8u) & 0xFFu) / 255.0;
} else {
- shadow = float(shadow1 >> ((i - 4) * 8) & 0xFF) / 255.0;
+ shadow = float(shadow1 >> ((i - 4u) * 8u) & 0xFFu) / 255.0;
}
shadow = shadow * directional_lights.data[i].shadow_opacity + 1.0 - directional_lights.data[i].shadow_opacity;
@@ -1845,9 +1843,9 @@ void fragment_shader(in SceneData scene_data) {
while (merged_mask != 0) {
uint bit = findMSB(merged_mask);
- merged_mask &= ~(1 << bit);
+ merged_mask &= ~(1u << bit);
#ifdef USE_SUBGROUPS
- if (((1 << bit) & mask) == 0) { //do not process if not originally here
+ if (((1u << bit) & mask) == 0) { //do not process if not originally here
continue;
}
#endif
@@ -1916,9 +1914,9 @@ void fragment_shader(in SceneData scene_data) {
while (merged_mask != 0) {
uint bit = findMSB(merged_mask);
- merged_mask &= ~(1 << bit);
+ merged_mask &= ~(1u << bit);
#ifdef USE_SUBGROUPS
- if (((1 << bit) & mask) == 0) { //do not process if not originally here
+ if (((1u << bit) & mask) == 0) { //do not process if not originally here
continue;
}
#endif
@@ -2071,7 +2069,7 @@ void fragment_shader(in SceneData scene_data) {
float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
//store as 8985 to have 2 extra neighbour bits
- uint light_rgbe = ((uint(sRed) & 0x1FF) >> 1) | ((uint(sGreen) & 0x1FF) << 8) | (((uint(sBlue) & 0x1FF) >> 1) << 17) | ((uint(exps) & 0x1F) << 25);
+ uint light_rgbe = ((uint(sRed) & 0x1FFu) >> 1) | ((uint(sGreen) & 0x1FFu) << 8) | (((uint(sBlue) & 0x1FFu) >> 1) << 17) | ((uint(exps) & 0x1Fu) << 25);
imageStore(emission_grid, grid_pos, uvec4(light_rgbe));
imageStore(emission_aniso_grid, grid_pos, uvec4(light_aniso));
@@ -2105,8 +2103,8 @@ void fragment_shader(in SceneData scene_data) {
if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_VOXEL_GI)) { // process voxel_gi_instances
uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
uint index2 = instances.data[instance_index].gi_offset >> 16;
- voxel_gi_buffer.x = index1 & 0xFF;
- voxel_gi_buffer.y = index2 & 0xFF;
+ voxel_gi_buffer.x = index1 & 0xFFu;
+ voxel_gi_buffer.y = index2 & 0xFFu;
} else {
voxel_gi_buffer.x = 0xFF;
voxel_gi_buffer.y = 0xFF;
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
index 0d36b98645..3a45ab0059 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
@@ -4,14 +4,15 @@
#define MAX_VOXEL_GI_INSTANCES 8
#define MAX_VIEWS 2
+#ifndef MOLTENVK_USED
#if defined(has_GL_KHR_shader_subgroup_ballot) && defined(has_GL_KHR_shader_subgroup_arithmetic)
#extension GL_KHR_shader_subgroup_ballot : enable
#extension GL_KHR_shader_subgroup_arithmetic : enable
#define USE_SUBGROUPS
-
#endif
+#endif // MOLTENVK_USED
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
#extension GL_EXT_multiview : enable
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h
index 38b616cda0..181d9a8a54 100644
--- a/tests/core/io/test_image.h
+++ b/tests/core/io/test_image.h
@@ -124,7 +124,7 @@ TEST_CASE("[Image] Saving and loading") {
image_jpg->load_jpg_from_buffer(data_jpg) == OK,
"The JPG image should load successfully.");
- // Load WEBP
+ // Load WebP
Ref<Image> image_webp = memnew(Image());
Ref<FileAccess> f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err);
PackedByteArray data_webp;
@@ -132,7 +132,7 @@ TEST_CASE("[Image] Saving and loading") {
f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length());
CHECK_MESSAGE(
image_webp->load_webp_from_buffer(data_webp) == OK,
- "The WEBP image should load successfully.");
+ "The WebP image should load successfully.");
// Load PNG
Ref<Image> image_png = memnew(Image());
diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h
index 90ca9e439f..f52b715cd7 100644
--- a/tests/core/math/test_basis.h
+++ b/tests/core/math/test_basis.h
@@ -38,15 +38,6 @@
namespace TestBasis {
-enum RotOrder {
- EulerXYZ,
- EulerXZY,
- EulerYZX,
- EulerYXZ,
- EulerZXY,
- EulerZYX
-};
-
Vector3 deg_to_rad(const Vector3 &p_rotation) {
return p_rotation / 180.0 * Math_PI;
}
@@ -55,88 +46,26 @@ Vector3 rad2deg(const Vector3 &p_rotation) {
return p_rotation / Math_PI * 180.0;
}
-Basis EulerToBasis(RotOrder mode, const Vector3 &p_rotation) {
- Basis ret;
- switch (mode) {
- case EulerXYZ:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_XYZ);
- break;
-
- case EulerXZY:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY);
- break;
-
- case EulerYZX:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX);
- break;
-
- case EulerYXZ:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ);
- break;
-
- case EulerZXY:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY);
- break;
-
- case EulerZYX:
- ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX);
- break;
-
- default:
- // If you land here, Please integrate all rotation orders.
- FAIL("This is not unreachable.");
- }
-
- return ret;
-}
-
-Vector3 BasisToEuler(RotOrder mode, const Basis &p_rotation) {
- switch (mode) {
- case EulerXYZ:
- return p_rotation.get_euler(Basis::EULER_ORDER_XYZ);
-
- case EulerXZY:
- return p_rotation.get_euler(Basis::EULER_ORDER_XZY);
-
- case EulerYZX:
- return p_rotation.get_euler(Basis::EULER_ORDER_YZX);
-
- case EulerYXZ:
- return p_rotation.get_euler(Basis::EULER_ORDER_YXZ);
-
- case EulerZXY:
- return p_rotation.get_euler(Basis::EULER_ORDER_ZXY);
-
- case EulerZYX:
- return p_rotation.get_euler(Basis::EULER_ORDER_ZYX);
-
- default:
- // If you land here, Please integrate all rotation orders.
- FAIL("This is not unreachable.");
- return Vector3();
- }
-}
-
-String get_rot_order_name(RotOrder ro) {
+String get_rot_order_name(Basis::EulerOrder ro) {
switch (ro) {
- case EulerXYZ:
+ case Basis::EULER_ORDER_XYZ:
return "XYZ";
- case EulerXZY:
+ case Basis::EULER_ORDER_XZY:
return "XZY";
- case EulerYZX:
+ case Basis::EULER_ORDER_YZX:
return "YZX";
- case EulerYXZ:
+ case Basis::EULER_ORDER_YXZ:
return "YXZ";
- case EulerZXY:
+ case Basis::EULER_ORDER_ZXY:
return "ZXY";
- case EulerZYX:
+ case Basis::EULER_ORDER_ZYX:
return "ZYX";
default:
return "[Not supported]";
}
}
-void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
+void test_rotation(Vector3 deg_original_euler, Basis::EulerOrder rot_order) {
// This test:
// 1. Converts the rotation vector from deg to rad.
// 2. Converts euler to basis.
@@ -156,11 +85,11 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
// Euler to rotation
const Vector3 original_euler = deg_to_rad(deg_original_euler);
- const Basis to_rotation = EulerToBasis(rot_order, original_euler);
+ const Basis to_rotation = Basis::from_euler(original_euler, rot_order);
// Euler from rotation
- const Vector3 euler_from_rotation = BasisToEuler(rot_order, to_rotation);
- const Basis rotation_from_computed_euler = EulerToBasis(rot_order, euler_from_rotation);
+ const Vector3 euler_from_rotation = to_rotation.get_euler(rot_order);
+ const Basis rotation_from_computed_euler = Basis::from_euler(euler_from_rotation, rot_order);
Basis res = to_rotation.inverse() * rotation_from_computed_euler;
@@ -184,13 +113,13 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
}
TEST_CASE("[Basis] Euler conversions") {
- Vector<RotOrder> rotorder_to_test;
- rotorder_to_test.push_back(EulerXYZ);
- rotorder_to_test.push_back(EulerXZY);
- rotorder_to_test.push_back(EulerYZX);
- rotorder_to_test.push_back(EulerYXZ);
- rotorder_to_test.push_back(EulerZXY);
- rotorder_to_test.push_back(EulerZYX);
+ Vector<Basis::EulerOrder> euler_order_to_test;
+ euler_order_to_test.push_back(Basis::EULER_ORDER_XYZ);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_XZY);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_YZX);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_YXZ);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_ZXY);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_ZYX);
Vector<Vector3> vectors_to_test;
@@ -248,21 +177,21 @@ TEST_CASE("[Basis] Euler conversions") {
vectors_to_test.push_back(Vector3(120.0, 150.0, -130.0));
vectors_to_test.push_back(Vector3(120.0, 150.0, 130.0));
- for (int h = 0; h < rotorder_to_test.size(); h += 1) {
+ for (int h = 0; h < euler_order_to_test.size(); h += 1) {
for (int i = 0; i < vectors_to_test.size(); i += 1) {
- test_rotation(vectors_to_test[i], rotorder_to_test[h]);
+ test_rotation(vectors_to_test[i], euler_order_to_test[h]);
}
}
}
TEST_CASE("[Stress][Basis] Euler conversions") {
- Vector<RotOrder> rotorder_to_test;
- rotorder_to_test.push_back(EulerXYZ);
- rotorder_to_test.push_back(EulerXZY);
- rotorder_to_test.push_back(EulerYZX);
- rotorder_to_test.push_back(EulerYXZ);
- rotorder_to_test.push_back(EulerZXY);
- rotorder_to_test.push_back(EulerZYX);
+ Vector<Basis::EulerOrder> euler_order_to_test;
+ euler_order_to_test.push_back(Basis::EULER_ORDER_XYZ);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_XZY);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_YZX);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_YXZ);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_ZXY);
+ euler_order_to_test.push_back(Basis::EULER_ORDER_ZYX);
Vector<Vector3> vectors_to_test;
// Add 1000 random vectors with weirds numbers.
@@ -274,9 +203,9 @@ TEST_CASE("[Stress][Basis] Euler conversions") {
rng.randf_range(-1800, 1800)));
}
- for (int h = 0; h < rotorder_to_test.size(); h += 1) {
+ for (int h = 0; h < euler_order_to_test.size(); h += 1) {
for (int i = 0; i < vectors_to_test.size(); i += 1) {
- test_rotation(vectors_to_test[i], rotorder_to_test[h]);
+ test_rotation(vectors_to_test[i], euler_order_to_test[h]);
}
}
}
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index 4b92b9daff..652cbed6e8 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -3353,7 +3353,7 @@ TEST_CASE("[SceneTree][TextEdit] caret") {
memdelete(text_edit);
}
-TEST_CASE("[SceneTree][TextEdit] muiticaret") {
+TEST_CASE("[SceneTree][TextEdit] multicaret") {
TextEdit *text_edit = memnew(TextEdit);
SceneTree::get_singleton()->get_root()->add_child(text_edit);
text_edit->set_multiple_carets_enabled(true);
@@ -3443,6 +3443,43 @@ TEST_CASE("[SceneTree][TextEdit] muiticaret") {
CHECK(text_edit->get_caret_index_edit_order() == caret_index_get_order);
}
+ SUBCASE("[TextEdit] add caret at carets") {
+ text_edit->remove_secondary_carets();
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(9);
+
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 4);
+
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 2);
+
+ text_edit->add_caret_at_carets(false);
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 7);
+
+ text_edit->remove_secondary_carets();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ text_edit->select(0, 0, 0, 4);
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_selection_from_line(1) == 1);
+ CHECK(text_edit->get_selection_to_line(1) == 1);
+ CHECK(text_edit->get_selection_from_column(1) == 0);
+ CHECK(text_edit->get_selection_to_column(1) == 3);
+
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->get_selection_from_line(2) == 2);
+ CHECK(text_edit->get_selection_to_line(2) == 2);
+ CHECK(text_edit->get_selection_from_column(2) == 0);
+ CHECK(text_edit->get_selection_to_column(2) == 4);
+ }
+
memdelete(text_edit);
}