diff options
208 files changed, 1566 insertions, 742 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7ac048b24a..7dfc3bccc3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -37,6 +37,7 @@ doc_classes/* @godotengine/documentation ## Rendering /drivers/dummy/ @godotengine/rendering +/drivers/gles3/ @godotengine/rendering /drivers/spirv-reflect/ @godotengine/rendering /drivers/vulkan/ @godotengine/rendering diff --git a/core/input/input.cpp b/core/input/input.cpp index 1321e40795..cb65d2569c 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -1310,7 +1310,7 @@ void Input::parse_mapping(String p_mapping) { JoyButton output_button = _get_output_button(output); JoyAxis output_axis = _get_output_axis(output); ERR_CONTINUE_MSG(output_button == JoyButton::INVALID && output_axis == JoyAxis::INVALID, - vformat("Unrecognised output string \"%s\" in mapping:\n%s", output, p_mapping)); + vformat("Unrecognized output string \"%s\" in mapping:\n%s", output, p_mapping)); ERR_CONTINUE_MSG(output_button != JoyButton::INVALID && output_axis != JoyAxis::INVALID, vformat("Output string \"%s\" matched both button and axis in mapping:\n%s", output, p_mapping)); diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index aebb9caa2f..1c7d71e309 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -342,7 +342,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = { { "ui_text_select_all", TTRC("Select All") }, { "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") }, { "ui_text_add_selection_for_next_occurrence", TTRC("Add Selection for Next Occurrence") }, - { "ui_text_remove_secondary_carets", TTRC("Remove Secondary Carets") }, + { "ui_text_clear_carets_and_selection", TTRC("Clear Carets and Selection") }, { "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") }, { "ui_text_submit", TTRC("Text Submitted") }, { "ui_graph_duplicate", TTRC("Duplicate Nodes") }, @@ -674,7 +674,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(Key::ESCAPE)); - default_builtin_cache.insert("ui_text_remove_secondary_carets", inputs); + default_builtin_cache.insert("ui_text_clear_carets_and_selection", inputs); inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(Key::INSERT)); diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index 1a80faaa12..d06f9bef1e 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -34,7 +34,7 @@ #include "core/math/vector2.h" #include "core/typedefs.h" -static inline float undenormalise(volatile float f) { +static inline float undenormalize(volatile float f) { union { uint32_t i; float f; @@ -101,9 +101,9 @@ struct AudioFrame { r /= p_sample; } - _ALWAYS_INLINE_ void undenormalise() { - l = ::undenormalise(l); - r = ::undenormalise(r); + _ALWAYS_INLINE_ void undenormalize() { + l = ::undenormalize(l); + r = ::undenormalize(r); } _FORCE_INLINE_ AudioFrame lerp(const AudioFrame &p_b, float p_t) const { diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 9b8188eed8..5f0d7ba96f 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -815,7 +815,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { return; } // As we have reached here there are no singularities so we can handle normally. - double s = Math::sqrt((rows[2][1] - rows[1][2]) * (rows[2][1] - rows[1][2]) + (rows[0][2] - rows[2][0]) * (rows[0][2] - rows[2][0]) + (rows[1][0] - rows[0][1]) * (rows[1][0] - rows[0][1])); // Used to normalise. + double s = Math::sqrt((rows[2][1] - rows[1][2]) * (rows[2][1] - rows[1][2]) + (rows[0][2] - rows[2][0]) * (rows[0][2] - rows[2][0]) + (rows[1][0] - rows[0][1]) * (rows[1][0] - rows[0][1])); // Used to normalize. if (Math::abs(s) < CMP_EPSILON) { // Prevent divide by zero, should not happen if matrix is orthogonal and should be caught by singularity test above. diff --git a/core/math/quaternion.cpp b/core/math/quaternion.cpp index 6a5f29f3d8..79a4e62b56 100644 --- a/core/math/quaternion.cpp +++ b/core/math/quaternion.cpp @@ -330,7 +330,7 @@ Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) { // (ax, ay, az), where ax is the angle of rotation around x axis, // and similar for other axes. // This implementation uses YXZ convention (Z is the first rotation). -Quaternion::Quaternion(const Vector3 &p_euler) { +Quaternion Quaternion::from_euler(const Vector3 &p_euler) { real_t half_a1 = p_euler.y * 0.5f; real_t half_a2 = p_euler.x * 0.5f; real_t half_a3 = p_euler.z * 0.5f; @@ -346,8 +346,9 @@ Quaternion::Quaternion(const Vector3 &p_euler) { real_t cos_a3 = Math::cos(half_a3); real_t sin_a3 = Math::sin(half_a3); - x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3; - y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3; - z = -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3; - w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3; + return Quaternion( + sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3, + sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3, + -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3, + sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); } diff --git a/core/math/quaternion.h b/core/math/quaternion.h index 7aa400aa8c..7497f1643e 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -69,6 +69,7 @@ struct _NO_DISCARD_ Quaternion { Vector3 get_euler_xyz() const; Vector3 get_euler_yxz() const; Vector3 get_euler() const { return get_euler_yxz(); }; + static Quaternion from_euler(const Vector3 &p_euler); Quaternion slerp(const Quaternion &p_to, const real_t &p_weight) const; Quaternion slerpni(const Quaternion &p_to, const real_t &p_weight) const; @@ -128,8 +129,6 @@ struct _NO_DISCARD_ Quaternion { Quaternion(const Vector3 &p_axis, real_t p_angle); - Quaternion(const Vector3 &p_euler); - Quaternion(const Quaternion &p_q) : x(p_q.x), y(p_q.y), diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 688650f532..087ce09215 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1806,6 +1806,7 @@ static void _register_variant_builtin_methods() { bind_method(Quaternion, spherical_cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray()); bind_method(Quaternion, spherical_cubic_interpolate_in_time, sarray("b", "pre_a", "post_b", "weight", "b_t", "pre_a_t", "post_b_t"), varray()); bind_method(Quaternion, get_euler, sarray(), varray()); + bind_static_method(Quaternion, from_euler, sarray("euler"), varray()); bind_method(Quaternion, get_axis, sarray(), varray()); bind_method(Quaternion, get_angle, sarray(), varray()); @@ -1835,10 +1836,6 @@ static void _register_variant_builtin_methods() { bind_static_method(Color, hex64, sarray("hex"), varray()); bind_static_method(Color, html, sarray("rgba"), varray()); bind_static_method(Color, html_is_valid, sarray("color"), varray()); - bind_static_method(Color, find_named_color, sarray("name"), varray()); - bind_static_method(Color, get_named_color_count, sarray(), varray()); - bind_static_method(Color, get_named_color_name, sarray("idx"), varray()); - bind_static_method(Color, get_named_color, sarray("idx"), varray()); bind_static_method(Color, from_string, sarray("str", "default"), varray()); bind_static_method(Color, from_hsv, sarray("h", "s", "v", "alpha"), varray(1.0)); bind_static_method(Color, from_ok_hsl, sarray("h", "s", "l", "alpha"), varray(1.0)); diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp index 3b88dc11ca..ae9727fe79 100644 --- a/core/variant/variant_construct.cpp +++ b/core/variant/variant_construct.cpp @@ -141,7 +141,6 @@ void Variant::_register_variant_constructors() { add_constructor<VariantConstructor<Quaternion, Vector3, double>>(sarray("axis", "angle")); add_constructor<VariantConstructor<Quaternion, Vector3, Vector3>>(sarray("arc_from", "arc_to")); add_constructor<VariantConstructor<Quaternion, double, double, double, double>>(sarray("x", "y", "z", "w")); - add_constructor<VariantConstructor<Quaternion, Vector3>>(sarray("euler_yxz")); add_constructor<VariantConstructNoArgs<::AABB>>(sarray()); add_constructor<VariantConstructor<::AABB, ::AABB>>(sarray("from")); diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index 4c6ee8df1b..25bc241e9b 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -240,8 +240,6 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorAdd<double, double, int64_t>>(Variant::OP_ADD, Variant::FLOAT, Variant::INT); register_op<OperatorEvaluatorAdd<double, double, double>>(Variant::OP_ADD, Variant::FLOAT, Variant::FLOAT); register_op<OperatorEvaluatorAdd<String, String, String>>(Variant::OP_ADD, Variant::STRING, Variant::STRING); - register_op<OperatorEvaluatorAdd<String, char32_t, String>>(Variant::OP_ADD, Variant::INT, Variant::STRING); - register_op<OperatorEvaluatorAdd<String, String, char32_t>>(Variant::OP_ADD, Variant::STRING, Variant::INT); register_op<OperatorEvaluatorAdd<Vector2, Vector2, Vector2>>(Variant::OP_ADD, Variant::VECTOR2, Variant::VECTOR2); register_op<OperatorEvaluatorAdd<Vector2i, Vector2i, Vector2i>>(Variant::OP_ADD, Variant::VECTOR2I, Variant::VECTOR2I); register_op<OperatorEvaluatorAdd<Vector3, Vector3, Vector3>>(Variant::OP_ADD, Variant::VECTOR3, Variant::VECTOR3); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index e30034495c..ae39f7ce28 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -572,7 +572,8 @@ <return type="float" /> <param index="0" name="lin" type="float" /> <description> - Converts from linear energy to decibels (audio). This can be used to implement volume sliders that behave as expected (since volume isn't linear). Example: + Converts from linear energy to decibels (audio). This can be used to implement volume sliders that behave as expected (since volume isn't linear). + [b]Example:[/b] [codeblock] # "Slider" refers to a node that inherits Range such as HSlider or VSlider. # Its range must be configured to go from 0 to 1. @@ -841,7 +842,7 @@ <return type="PackedInt64Array" /> <param index="0" name="seed" type="int" /> <description> - Given a [param seed], returns a [PackedInt64Array] of size [code]2[/code], where its first element is the randomised [int] value, and the second element is the same as [param seed]. Passing the same [param seed] consistently returns the same array. + Given a [param seed], returns a [PackedInt64Array] of size [code]2[/code], where its first element is the randomized [int] value, and the second element is the same as [param seed]. Passing the same [param seed] consistently returns the same array. [b]Note:[/b] "Seed" here refers to the internal state of the pseudo random number generator, currently implemented as a 64 bit integer. [codeblock] var a = rand_from_seed(4) @@ -2487,7 +2488,8 @@ </constant> <constant name="OK" value="0" enum="Error"> Methods that return [enum Error] return [constant OK] when no error occurred. - Since [constant OK] has value 0, and all other error constants are positive integers, it can also be used in boolean checks. For example: + Since [constant OK] has value 0, and all other error constants are positive integers, it can also be used in boolean checks. + [b]Example:[/b] [codeblock] var error = method_that_returns_error() if error != OK: @@ -2648,7 +2650,8 @@ The property has no hint for the editor. </constant> <constant name="PROPERTY_HINT_RANGE" value="1" enum="PropertyHint"> - Hints that an [int] or [float] property should be within a range specified via the hint string [code]"min,max"[/code] or [code]"min,max,step"[/code]. The hint string can optionally include [code]"or_greater"[/code] and/or [code]"or_less"[/code] to allow manual input going respectively above the max or below the min values. Example: [code]"-360,360,1,or_greater,or_less"[/code]. + Hints that an [int] or [float] property should be within a range specified via the hint string [code]"min,max"[/code] or [code]"min,max,step"[/code]. The hint string can optionally include [code]"or_greater"[/code] and/or [code]"or_less"[/code] to allow manual input going respectively above the max or below the min values. + [b]Example:[/b] [code]"-360,360,1,or_greater,or_less"[/code]. Additionally, other keywords can be included: [code]"exp"[/code] for exponential range editing, [code]"radians"[/code] for editing radian angles in degrees, [code]"degrees"[/code] to hint at an angle and [code]"hide_slider"[/code] to hide the slider. </constant> <constant name="PROPERTY_HINT_ENUM" value="2" enum="PropertyHint"> @@ -2722,7 +2725,8 @@ <constant name="PROPERTY_HINT_OBJECT_ID" value="24" enum="PropertyHint"> </constant> <constant name="PROPERTY_HINT_TYPE_STRING" value="25" enum="PropertyHint"> - Hints that a property represents a particular type. If a property is [constant TYPE_STRING], allows to set a type from the create dialog. If you need to create an [Array] to contain elements of a specific type, the [code]hint_string[/code] must encode nested types using [code]":"[/code] and [code]"/"[/code] for specifying [Resource] types. For example: + Hints that a property represents a particular type. If a property is [constant TYPE_STRING], allows to set a type from the create dialog. If you need to create an [Array] to contain elements of a specific type, the [code]hint_string[/code] must encode nested types using [code]":"[/code] and [code]"/"[/code] for specifying [Resource] types. + [b]Example:[/b] [codeblock] hint_string = "%s:" % [TYPE_INT] # Array of integers. hint_string = "%s:%s:" % [TYPE_ARRAY, TYPE_REAL] # Two-dimensional array of floats. diff --git a/doc/classes/AcceptDialog.xml b/doc/classes/AcceptDialog.xml index b5a1461876..4da9e41ca8 100644 --- a/doc/classes/AcceptDialog.xml +++ b/doc/classes/AcceptDialog.xml @@ -62,7 +62,7 @@ Sets autowrapping for the text in the dialog. </member> <member name="dialog_close_on_escape" type="bool" setter="set_close_on_escape" getter="get_close_on_escape" default="true"> - If [code]true[/code], the dialog will be hidden when the escape key ([constant @GlobalScope.KEY_ESCAPE]) is pressed. + If [code]true[/code], the dialog will be hidden when the escape key ([constant KEY_ESCAPE]) is pressed. </member> <member name="dialog_hide_on_ok" type="bool" setter="set_hide_on_ok" getter="get_hide_on_ok" default="true"> If [code]true[/code], the dialog is hidden when the OK button is pressed. You can set it to [code]false[/code] if you want to do e.g. input validation when receiving the [signal confirmed] signal, and handle hiding the dialog in your own logic. diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index b8e0ac0c2a..2ec37651f7 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -317,13 +317,13 @@ <method name="get_typed_builtin" qualifiers="const"> <return type="int" /> <description> - Returns the [code]TYPE[/code] constant for a typed array. If the [Array] is not typed, returns [constant @GlobalScope.TYPE_NIL]. + Returns the [enum Variant.Type] constant for a typed array. If the [Array] is not typed, returns [constant TYPE_NIL]. </description> </method> <method name="get_typed_class_name" qualifiers="const"> <return type="StringName" /> <description> - Returns a class name of a typed [Array] of type [constant @GlobalScope.TYPE_OBJECT]. + Returns a class name of a typed [Array] of type [constant TYPE_OBJECT]. </description> </method> <method name="get_typed_script" qualifiers="const"> @@ -353,7 +353,6 @@ GD.Print(arr.Contains("7")); // False [/csharp] [/codeblocks] - [b]Note:[/b] This is equivalent to using the [code]in[/code] operator as follows: [codeblocks] [gdscript] @@ -543,7 +542,7 @@ <param index="1" name="class_name" type="StringName" /> <param index="2" name="script" type="Variant" /> <description> - Makes the [Array] typed. The [param type] should be one of the [@GlobalScope] [code]TYPE[/code] constants. [param class_name] is optional and can only be provided for [constant @GlobalScope.TYPE_OBJECT]. [param script] can only be provided if [param class_name] is not empty. + Makes the [Array] typed. The [param type] should be one of the [enum Variant.Type] constants. [param class_name] is optional and can only be provided for [constant TYPE_OBJECT]. [param script] can only be provided if [param class_name] is not empty. The method fails if an array is already typed. </description> </method> diff --git a/doc/classes/BitMap.xml b/doc/classes/BitMap.xml index b3fa55f154..e9774ef485 100644 --- a/doc/classes/BitMap.xml +++ b/doc/classes/BitMap.xml @@ -12,7 +12,7 @@ <method name="convert_to_image" qualifiers="const"> <return type="Image" /> <description> - Returns an image of the same size as the bitmap and with a [enum Image.Format] of type [code]FORMAT_L8[/code]. [code]true[/code] bits of the bitmap are being converted into white pixels, and [code]false[/code] bits into black. + Returns an image of the same size as the bitmap and with a [enum Image.Format] of type [constant Image.FORMAT_L8]. [code]true[/code] bits of the bitmap are being converted into white pixels, and [code]false[/code] bits into black. </description> </method> <method name="create"> diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index 1ff51b48be..d2c9a77486 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -42,7 +42,7 @@ </tutorials> <members> <member name="alignment" type="int" setter="set_text_alignment" getter="get_text_alignment" enum="HorizontalAlignment" default="1"> - Text alignment policy for the button's text, use one of the [enum @GlobalScope.HorizontalAlignment] constants. + Text alignment policy for the button's text, use one of the [enum HorizontalAlignment] constants. </member> <member name="clip_text" type="bool" setter="set_clip_text" getter="get_clip_text" default="false"> When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text. @@ -58,7 +58,7 @@ To edit margin and spacing of the icon, use [theme_item h_separation] theme property and [code]content_margin_*[/code] properties of the used [StyleBox]es. </member> <member name="icon_alignment" type="int" setter="set_icon_alignment" getter="get_icon_alignment" enum="HorizontalAlignment" default="0"> - Specifies if the icon should be aligned to the left, right, or center of a button. Uses the same [enum @GlobalScope.HorizontalAlignment] constants as the text alignment. If centered, text will draw on top of the icon. + Specifies if the icon should be aligned to the left, right, or center of a button. Uses the same [enum HorizontalAlignment] constants as the text alignment. If centered, text will draw on top of the icon. </member> <member name="language" type="String" setter="set_language" getter="get_language" default=""""> Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead. diff --git a/doc/classes/CallbackTweener.xml b/doc/classes/CallbackTweener.xml index 6e4ee0380e..626405bc8e 100644 --- a/doc/classes/CallbackTweener.xml +++ b/doc/classes/CallbackTweener.xml @@ -14,7 +14,8 @@ <return type="CallbackTweener" /> <param index="0" name="delay" type="float" /> <description> - Makes the callback call delayed by given time in seconds. Example: + Makes the callback call delayed by given time in seconds. + [b]Example:[/b] [codeblock] var tween = get_tree().create_tween() tween.tween_callback(queue_free).set_delay(2) #this will call queue_free() after 2 seconds diff --git a/doc/classes/CharacterBody2D.xml b/doc/classes/CharacterBody2D.xml index 2f8e1a7bb8..0111ef804e 100644 --- a/doc/classes/CharacterBody2D.xml +++ b/doc/classes/CharacterBody2D.xml @@ -152,7 +152,7 @@ </member> <member name="floor_snap_length" type="float" setter="set_floor_snap_length" getter="get_floor_snap_length" default="1.0"> Sets a snapping distance. When set to a value different from [code]0.0[/code], the body is kept attached to slopes when calling [method move_and_slide]. The snapping vector is determined by the given distance along the opposite direction of the [member up_direction]. - As long as the snapping vector is in contact with the ground and the body moves against `up_direction`, the body will remain attached to the surface. Snapping is not applied if the body moves along `up_direction`, so it will be able to detach from the ground when jumping. + As long as the snapping vector is in contact with the ground and the body moves against [member up_direction], the body will remain attached to the surface. Snapping is not applied if the body moves along [member up_direction], so it will be able to detach from the ground when jumping. </member> <member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="true"> If [code]true[/code], the body will not slide on slopes when calling [method move_and_slide] when the body is standing still. diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml index 6a1975d40f..309e2231a4 100644 --- a/doc/classes/CharacterBody3D.xml +++ b/doc/classes/CharacterBody3D.xml @@ -137,7 +137,7 @@ </member> <member name="floor_snap_length" type="float" setter="set_floor_snap_length" getter="get_floor_snap_length" default="0.1"> Sets a snapping distance. When set to a value different from [code]0.0[/code], the body is kept attached to slopes when calling [method move_and_slide]. The snapping vector is determined by the given distance along the opposite direction of the [member up_direction]. - As long as the snapping vector is in contact with the ground and the body moves against `up_direction`, the body will remain attached to the surface. Snapping is not applied if the body moves along `up_direction`, so it will be able to detach from the ground when jumping. + As long as the snapping vector is in contact with the ground and the body moves against [member up_direction], the body will remain attached to the surface. Snapping is not applied if the body moves along [member up_direction], so it will be able to detach from the ground when jumping. </member> <member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="true"> If [code]true[/code], the body will not slide on slopes when calling [method move_and_slide] when the body is standing still. diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml index 4c62ab2a7f..e7ef5fb289 100644 --- a/doc/classes/CodeEdit.xml +++ b/doc/classes/CodeEdit.xml @@ -4,7 +4,7 @@ Multiline text control intended for editing code. </brief_description> <description> - CodeEdit is a specialised [TextEdit] designed for editing plain text code files. It contains a bunch of features commonly found in code editors such as line numbers, line folding, code completion, indent management and string / comment management. + CodeEdit is a specialized [TextEdit] designed for editing plain text code files. It contains a bunch of features commonly found in code editors such as line numbers, line folding, code completion, indent management and string / comment management. [b]Note:[/b] By default [CodeEdit] always use left-to-right text direction to correctly display source code. </description> <tutorials> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index a11d7157f1..90fbdcc622 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -141,17 +141,6 @@ [/codeblocks] </description> </method> - <method name="find_named_color" qualifiers="static"> - <return type="int" /> - <param index="0" name="name" type="String" /> - <description> - Returns the index of a named color. Use [method get_named_color] to get the actual color. - [codeblock] - var idx = Color.find_named_color("khaki") - modulate = Color.get_named_color(idx) - [/codeblock] - </description> - </method> <method name="from_hsv" qualifiers="static"> <return type="Color" /> <param index="0" name="h" type="float" /> @@ -208,27 +197,7 @@ <description> Returns the luminance of the color in the [code][0.0, 1.0][/code] range. This is useful when determining light or dark color. Colors with a luminance smaller than 0.5 can be generally considered dark. - [b]Note:[/b] [method get_luminance] relies on the colour being in the linear color space to return an accurate relative luminance value. If the color is in the sRGB color space, use [method srgb_to_linear] to convert it to the linear color space first. - </description> - </method> - <method name="get_named_color" qualifiers="static"> - <return type="Color" /> - <param index="0" name="idx" type="int" /> - <description> - Returns a named color with the given index. You can get the index from [method find_named_color] or iteratively from [method get_named_color_count]. - </description> - </method> - <method name="get_named_color_count" qualifiers="static"> - <return type="int" /> - <description> - Returns the number of available named colors. - </description> - </method> - <method name="get_named_color_name" qualifiers="static"> - <return type="String" /> - <param index="0" name="idx" type="int" /> - <description> - Returns the name of a color with the given index. You can get the index from [method find_named_color] or iteratively from [method get_named_color_count]. + [b]Note:[/b] [method get_luminance] relies on the color being in the linear color space to return an accurate relative luminance value. If the color is in the sRGB color space, use [method srgb_to_linear] to convert it to the linear color space first. </description> </method> <method name="hex" qualifiers="static"> diff --git a/doc/classes/ConfigFile.xml b/doc/classes/ConfigFile.xml index 7ba53f852b..4e60df6b96 100644 --- a/doc/classes/ConfigFile.xml +++ b/doc/classes/ConfigFile.xml @@ -163,7 +163,7 @@ <param index="0" name="path" type="String" /> <description> Loads the config file specified as a parameter. The file's contents are parsed and loaded in the [ConfigFile] object which the method was called on. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="load_encrypted"> @@ -172,7 +172,7 @@ <param index="1" name="key" type="PackedByteArray" /> <description> Loads the encrypted config file specified as a parameter, using the provided [param key] to decrypt it. The file's contents are parsed and loaded in the [ConfigFile] object which the method was called on. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="load_encrypted_pass"> @@ -181,7 +181,7 @@ <param index="1" name="password" type="String" /> <description> Loads the encrypted config file specified as a parameter, using the provided [param password] to decrypt it. The file's contents are parsed and loaded in the [ConfigFile] object which the method was called on. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="parse"> @@ -189,7 +189,7 @@ <param index="0" name="data" type="String" /> <description> Parses the passed string as the contents of a config file. The string is parsed and loaded in the ConfigFile object which the method was called on. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="save"> @@ -197,7 +197,7 @@ <param index="0" name="path" type="String" /> <description> Saves the contents of the [ConfigFile] object to the file specified as a parameter. The output file uses an INI-style structure. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="save_encrypted"> @@ -206,7 +206,7 @@ <param index="1" name="key" type="PackedByteArray" /> <description> Saves the contents of the [ConfigFile] object to the AES-256 encrypted file specified as a parameter, using the provided [param key] to encrypt it. The output file uses an INI-style structure. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="save_encrypted_pass"> @@ -215,7 +215,7 @@ <param index="1" name="password" type="String" /> <description> Saves the contents of the [ConfigFile] object to the AES-256 encrypted file specified as a parameter, using the provided [param password] to encrypt it. The output file uses an INI-style structure. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="set_value"> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index c1fa1a2bf1..e1dd2819b2 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -110,7 +110,7 @@ <param index="0" name="event" type="InputEvent" /> <description> Virtual method to be implemented by the user. Use this method to process and accept inputs on UI elements. See [method accept_event]. - Example: clicking a control. + [b]Example usage for clicking a control:[/b] [codeblocks] [gdscript] func _gui_input(event): @@ -159,7 +159,7 @@ The returned node will be added as child to a [PopupPanel], so you should only provide the contents of that panel. That [PopupPanel] can be themed using [method Theme.set_stylebox] for the type [code]"TooltipPanel"[/code] (see [member tooltip_text] for an example). [b]Note:[/b] The tooltip is shrunk to minimal size. If you want to ensure it's fully visible, you might want to set its [member custom_minimum_size] to some non-zero value. [b]Note:[/b] The node (and any relevant children) should be [member CanvasItem.visible] when returned, otherwise, the viewport that instantiates it will not be able to calculate its minimum size reliably. - Example of usage with a custom-constructed node: + [b]Example of usage with a custom-constructed node:[/b] [codeblocks] [gdscript] func _make_custom_tooltip(for_text): @@ -176,7 +176,7 @@ } [/csharp] [/codeblocks] - Example of usage with a custom scene instance: + [b]Example of usage with a custom scene instance:[/b] [codeblocks] [gdscript] func _make_custom_tooltip(for_text): @@ -201,7 +201,7 @@ <param index="1" name="text" type="String" /> <description> User defined BiDi algorithm override function. - Returns [code]Array[/code] of [code]Vector2i[/code] text ranges, in the left-to-right order. Ranges should cover full source [param text] without overlaps. BiDi algorithm will be used on each range separately. + Returns an [Array] of [Vector2i] text ranges, in the left-to-right order. Ranges should cover full source [param text] without overlaps. BiDi algorithm will be used on each range separately. </description> </method> <method name="accept_event"> @@ -422,7 +422,7 @@ <description> Returns the position of this [Control] in global screen coordinates (i.e. taking window position into account). Mostly useful for editor plugins. Equals to [member global_position] if the window is embedded (see [member Viewport.gui_embed_subwindows]). - Example usage for showing a popup: + [b]Example usage for showing a popup:[/b] [codeblock] popup_menu.position = get_screen_position() + get_local_mouse_position() popup_menu.reset_size() @@ -528,7 +528,7 @@ <param index="0" name="at_position" type="Vector2" default="Vector2(0, 0)" /> <description> Returns the tooltip text [param at_position] in local coordinates, which will typically appear when the cursor is resting over this control. By default, it returns [member tooltip_text]. - [b]Note:[/b] This method can be overridden to customise its behaviour. If this method returns an empty [String], no tooltip is displayed. + [b]Note:[/b] This method can be overridden to customize its behavior. If this method returns an empty [String], no tooltip is displayed. </description> </method> <method name="grab_click_focus"> @@ -916,7 +916,7 @@ <param index="2" name="margin" type="int" default="0" /> <description> Sets the offsets to a [param preset] from [enum Control.LayoutPreset] enum. This is the code equivalent to using the Layout menu in the 2D editor. - Use parameter [param resize_mode] with constants from [enum Control.LayoutPresetMode] to better determine the resulting size of the [Control]. Constant size will be ignored if used with presets that change size, e.g. [code]PRESET_LEFT_WIDE[/code]. + Use parameter [param resize_mode] with constants from [enum Control.LayoutPresetMode] to better determine the resulting size of the [Control]. Constant size will be ignored if used with presets that change size, e.g. [constant PRESET_LEFT_WIDE]. Use parameter [param margin] to determine the gap between the [Control] and the edges. </description> </method> @@ -979,23 +979,23 @@ The focus access mode for the control (None, Click or All). Only one Control can be focused at the same time, and it will receive keyboard, gamepad, and mouse signals. </member> <member name="focus_neighbor_bottom" type="NodePath" setter="set_focus_neighbor" getter="get_focus_neighbor" default="NodePath("")"> - Tells Godot which node it should give focus to if the user presses the down arrow on the keyboard or down on a gamepad by default. You can change the key by editing the [code]ui_down[/code] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the bottom of this one. + Tells Godot which node it should give focus to if the user presses the down arrow on the keyboard or down on a gamepad by default. You can change the key by editing the [member ProjectSettings.input/ui_down] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the bottom of this one. </member> <member name="focus_neighbor_left" type="NodePath" setter="set_focus_neighbor" getter="get_focus_neighbor" default="NodePath("")"> - Tells Godot which node it should give focus to if the user presses the left arrow on the keyboard or left on a gamepad by default. You can change the key by editing the [code]ui_left[/code] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the left of this one. + Tells Godot which node it should give focus to if the user presses the left arrow on the keyboard or left on a gamepad by default. You can change the key by editing the [member ProjectSettings.input/ui_left] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the left of this one. </member> <member name="focus_neighbor_right" type="NodePath" setter="set_focus_neighbor" getter="get_focus_neighbor" default="NodePath("")"> - Tells Godot which node it should give focus to if the user presses the right arrow on the keyboard or right on a gamepad by default. You can change the key by editing the [code]ui_right[/code] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the bottom of this one. + Tells Godot which node it should give focus to if the user presses the right arrow on the keyboard or right on a gamepad by default. You can change the key by editing the [member ProjectSettings.input/ui_right] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the bottom of this one. </member> <member name="focus_neighbor_top" type="NodePath" setter="set_focus_neighbor" getter="get_focus_neighbor" default="NodePath("")"> - Tells Godot which node it should give focus to if the user presses the top arrow on the keyboard or top on a gamepad by default. You can change the key by editing the [code]ui_top[/code] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the bottom of this one. + Tells Godot which node it should give focus to if the user presses the top arrow on the keyboard or top on a gamepad by default. You can change the key by editing the [member ProjectSettings.input/ui_up] input action. The node must be a [Control]. If this property is not set, Godot will give focus to the closest [Control] to the bottom of this one. </member> <member name="focus_next" type="NodePath" setter="set_focus_next" getter="get_focus_next" default="NodePath("")"> - Tells Godot which node it should give focus to if the user presses [kbd]Tab[/kbd] on a keyboard by default. You can change the key by editing the [code]ui_focus_next[/code] input action. + Tells Godot which node it should give focus to if the user presses [kbd]Tab[/kbd] on a keyboard by default. You can change the key by editing the [member ProjectSettings.input/ui_focus_next] input action. If this property is not set, Godot will select a "best guess" based on surrounding nodes in the scene tree. </member> <member name="focus_previous" type="NodePath" setter="set_focus_previous" getter="get_focus_previous" default="NodePath("")"> - Tells Godot which node it should give focus to if the user presses [kbd]Shift + Tab[/kbd] on a keyboard by default. You can change the key by editing the [code]ui_focus_prev[/code] input action. + Tells Godot which node it should give focus to if the user presses [kbd]Shift + Tab[/kbd] on a keyboard by default. You can change the key by editing the [member ProjectSettings.input/ui_focus_prev] input action. If this property is not set, Godot will select a "best guess" based on surrounding nodes in the scene tree. </member> <member name="global_position" type="Vector2" setter="_set_global_position" getter="get_global_position"> @@ -1019,7 +1019,7 @@ </member> <member name="mouse_force_pass_scroll_events" type="bool" setter="set_force_pass_scroll_events" getter="is_force_pass_scroll_events" default="true"> When enabled, scroll wheel events processed by [method _gui_input] will be passed to the parent control even if [member mouse_filter] is set to [constant MOUSE_FILTER_STOP]. As it defaults to true, this allows nested scrollable containers to work out of the box. - You should disable it on the root of your UI if you do not want scroll events to go to the [code]_unhandled_input[/code] processing. + You should disable it on the root of your UI if you do not want scroll events to go to the [method Node._unhandled_input] processing. </member> <member name="offset_bottom" type="float" setter="set_offset" getter="get_offset" default="0.0"> Distance between the node's bottom edge and its parent control, based on [member anchor_bottom]. @@ -1076,7 +1076,7 @@ [b]Note:[/b] Theme items are looked for in the tree order, from branch to root, where each [Control] node is checked for its [member theme] property. The earliest match against any type/class name is returned. The project-level Theme and the default Theme are checked last. </member> <member name="tooltip_text" type="String" setter="set_tooltip_text" getter="get_tooltip_text" default=""""> - The default tooltip text. The tooltip appears when the user's mouse cursor stays idle over this control for a few moments, provided that the [member mouse_filter] property is not [constant MOUSE_FILTER_IGNORE]. The time required for the tooltip to appear can be changed with the [code]gui/timers/tooltip_delay_sec[/code] option in Project Settings. See also [method get_tooltip]. + The default tooltip text. The tooltip appears when the user's mouse cursor stays idle over this control for a few moments, provided that the [member mouse_filter] property is not [constant MOUSE_FILTER_IGNORE]. The time required for the tooltip to appear can be changed with the [member ProjectSettings.gui/timers/tooltip_delay_sec] option. See also [method get_tooltip]. The tooltip popup will use either a default implementation, or a custom one that you can provide by overriding [method _make_custom_tooltip]. The default tooltip includes a [PopupPanel] and [Label] whose theme properties can be customized using [Theme] methods with the [code]"TooltipPanel"[/code] and [code]"TooltipLabel"[/code] respectively. For example: [codeblocks] [gdscript] @@ -1330,7 +1330,7 @@ The control will receive mouse button input events through [method _gui_input] if clicked on. And the control will receive the [signal mouse_entered] and [signal mouse_exited] signals. These events are automatically marked as handled, and they will not propagate further to other controls. This also results in blocking signals in other controls. </constant> <constant name="MOUSE_FILTER_PASS" value="1" enum="MouseFilter"> - The control will receive mouse button input events through [method _gui_input] if clicked on. And the control will receive the [signal mouse_entered] and [signal mouse_exited] signals. If this control does not handle the event, the parent control (if any) will be considered, and so on until there is no more parent control to potentially handle it. This also allows signals to fire in other controls. If no control handled it, the event will be passed to `_unhandled_input` for further processing. + The control will receive mouse button input events through [method _gui_input] if clicked on. And the control will receive the [signal mouse_entered] and [signal mouse_exited] signals. If this control does not handle the event, the parent control (if any) will be considered, and so on until there is no more parent control to potentially handle it. This also allows signals to fire in other controls. If no control handled it, the event will be passed to [method Node._unhandled_input] for further processing. </constant> <constant name="MOUSE_FILTER_IGNORE" value="2" enum="MouseFilter"> The control will not receive mouse button input events through [method _gui_input]. The control will also not receive the [signal mouse_entered] nor [signal mouse_exited] signals. This will not block other controls from receiving these events or firing the signals. Ignored events will not be handled automatically. diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml index 20b9dafd0d..d5864f30fa 100644 --- a/doc/classes/Curve.xml +++ b/doc/classes/Curve.xml @@ -30,7 +30,7 @@ <method name="clean_dupes"> <return type="void" /> <description> - Removes points that are closer than [code]CMP_EPSILON[/code] (0.00001) units to their neighbor on the curve. + Removes duplicate points, i.e. points that are less than 0.00001 units (engine epsilon value) away from their neighbor on the curve. </description> </method> <method name="clear_points"> diff --git a/doc/classes/Curve2D.xml b/doc/classes/Curve2D.xml index cc4124d084..ccdf085319 100644 --- a/doc/classes/Curve2D.xml +++ b/doc/classes/Curve2D.xml @@ -102,6 +102,22 @@ Cubic interpolation tends to follow the curves better, but linear is faster (and often, precise enough). </description> </method> + <method name="sample_baked_with_rotation" qualifiers="const"> + <return type="Transform2D" /> + <param index="0" name="offset" type="float" /> + <param index="1" name="cubic" type="bool" default="false" /> + <param index="2" name="loop" type="bool" default="true" /> + <param index="3" name="lookahead" type="float" default="4.0" /> + <description> + Similar to [method sample_baked], but returns [Transform2D] that includes a rotation along the curve. Returns empty transform if length of the curve is [code]0[/code]. + Use [param loop] to smooth the tangent at the end of the curve. [param lookahead] defines the distance to a nearby point for calculating the tangent vector. + [codeblock] + var transform = curve.sample_baked_with_rotation(offset) + position = transform.get_origin() + rotation = transform.get_rotation() + [/codeblock] + </description> + </method> <method name="samplef" qualifiers="const"> <return type="Vector2" /> <param index="0" name="fofs" type="float" /> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index ac1f63af56..9842288d35 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -339,7 +339,7 @@ <return type="Variant" /> <param index="0" name="key" type="Variant" /> <description> - Retunrs a value at the given [param key] or [code]null[/code] and error if the key does not exist. For safe access, use [method get] or [method has]. + Returns a value at the given [param key] or [code]null[/code] and error if the key does not exist. For safe access, use [method get] or [method has]. </description> </operator> </operators> diff --git a/doc/classes/DirAccess.xml b/doc/classes/DirAccess.xml index 7d1612e59c..181d2eb485 100644 --- a/doc/classes/DirAccess.xml +++ b/doc/classes/DirAccess.xml @@ -70,7 +70,7 @@ <param index="0" name="to_dir" type="String" /> <description> Changes the currently opened directory to the one passed as an argument. The argument can be relative to the current directory (e.g. [code]newdir[/code] or [code]../newdir[/code]), or an absolute path (e.g. [code]/tmp/newdir[/code] or [code]res://somedir/newdir[/code]). - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="copy"> @@ -81,7 +81,7 @@ <description> Copies the [param from] file to the [param to] destination. Both arguments should be paths to files, either relative or absolute. If the destination file exists and is not access-protected, it will be overwritten. If [param chmod_flags] is different than [code]-1[/code], the Unix permissions for the destination path will be set to the provided value, if available on the current operating system. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="copy_absolute" qualifiers="static"> @@ -221,7 +221,7 @@ <param index="0" name="path" type="String" /> <description> Creates a directory. The argument can be relative to the current directory, or an absolute path. The target directory should be placed in an already existing directory (to create the full path recursively, see [method make_dir_recursive]). - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="make_dir_absolute" qualifiers="static"> @@ -236,7 +236,7 @@ <param index="0" name="path" type="String" /> <description> Creates a target directory and all necessary intermediate directories in its path, by calling [method make_dir] recursively. The argument can be relative to the current directory, or an absolute path. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="make_dir_recursive_absolute" qualifiers="static"> @@ -260,7 +260,7 @@ <description> Permanently deletes the target file or an empty directory. The argument can be relative to the current directory, or an absolute path. If the target directory is not empty, the operation will fail. If you don't want to delete the file/directory permanently, use [method OS.move_to_trash] instead. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="remove_absolute" qualifiers="static"> @@ -276,7 +276,7 @@ <param index="1" name="to" type="String" /> <description> Renames (move) the [param from] file or directory to the [param to] destination. Both arguments should be paths to files or directories, either relative or absolute. If the destination file or directory exists and is not access-protected, it will be overwritten. - Returns one of the [enum Error] code constants ([code]OK[/code] on success). + Returns one of the [enum Error] code constants ([constant OK] on success). </description> </method> <method name="rename_absolute" qualifiers="static"> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 4bb3657cf7..113987bb52 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -1008,8 +1008,8 @@ <param index="1" name="callable" type="Callable" /> <description> Adds a callback, which is called when the utterance has started, finished, canceled or reached a text boundary. - - [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. + - [constant TTS_UTTERANCE_STARTED], [constant TTS_UTTERANCE_ENDED], and [constant TTS_UTTERANCE_CANCELED] callable's method should take one [int] parameter, the utterance id. + - [constant TTS_UTTERANCE_BOUNDARY] 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 (X11), macOS, and Windows. </description> @@ -1170,7 +1170,7 @@ <return type="Vector3i" /> <param index="0" name="window_id" type="int" default="0" /> <description> - Returns left margins ([code]x[/code]), right margins ([code]y[/code]) adn height ([code]z[/code]) of the title that are safe to use (contains no buttons or other elements) when [constant WINDOW_FLAG_EXTEND_TO_TITLE] flag is set. + Returns left margins ([code]x[/code]), right margins ([code]y[/code]) and height ([code]z[/code]) of the title that are safe to use (contains no buttons or other elements) when [constant WINDOW_FLAG_EXTEND_TO_TITLE] flag is set. </description> </method> <method name="window_get_size" qualifiers="const"> diff --git a/doc/classes/EditorCommandPalette.xml b/doc/classes/EditorCommandPalette.xml index 53a3fe5d19..380c79fc1a 100644 --- a/doc/classes/EditorCommandPalette.xml +++ b/doc/classes/EditorCommandPalette.xml @@ -5,7 +5,7 @@ </brief_description> <description> Object that holds all the available Commands and their shortcuts text. These Commands can be accessed through [b]Editor > Command Palette[/b] menu. - Command key names use slash delimiters to distinguish sections Example: [code]"example/command1"[/code] then [code]example[/code] will be the section name. + Command key names use slash delimiters to distinguish sections, for example: [code]"example/command1"[/code] then [code]example[/code] will be the section name. [codeblocks] [gdscript] var command_palette = get_editor_interface().get_command_palette() diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index 51e3706981..29a8f470a1 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -4,7 +4,7 @@ A modified version of [FileDialog] used by the editor. </brief_description> <description> - [EditorFileDialog] is an enhanced version of [FileDialog] avaiable only to editor plugins. Additional features include list of favorited/recent files and ability to see files as thumbnails grid instead of list. + [EditorFileDialog] is an enhanced version of [FileDialog] available only to editor plugins. Additional features include list of favorited/recent files and ability to see files as thumbnails grid instead of list. </description> <tutorials> </tutorials> diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml index 108c670f59..0cda49f1bf 100644 --- a/doc/classes/EditorInspector.xml +++ b/doc/classes/EditorInspector.xml @@ -7,8 +7,8 @@ This is the control that implements property editing in the editor's Settings dialogs, the Inspector dock, etc. To get the [EditorInspector] used in the editor's Inspector dock, use [method EditorInterface.get_inspector]. [EditorInspector] will show properties in the same order as the array returned by [method Object.get_property_list]. If a property's name is path-like (i.e. if it contains forward slashes), [EditorInspector] will create nested sections for "directories" along the path. For example, if a property is named [code]highlighting/gdscript/node_path_color[/code], it will be shown as "Node Path Color" inside the "GDScript" section nested inside the "Highlighting" section. - If a property has [constant @GlobalScope.PROPERTY_USAGE_GROUP] usage, it will group subsequent properties whose name starts with the property's hint string. The group ends when a property does not start with that hint string or when a new group starts. An empty group name effectively ends the current group. [EditorInspector] will create a top-level section for each group. For example, if a property with group usage is named [code]Collide With[/code] and its hint string is [code]collide_with_[/code], a subsequent [code]collide_with_area[/code] property will be shown as "Area" inside the "Collide With" section. - If a property has [constant @GlobalScope.PROPERTY_USAGE_SUBGROUP] usage, a subgroup will be created in the same way as a group, and a second-level section will be created for each subgroup. + If a property has [constant PROPERTY_USAGE_GROUP] usage, it will group subsequent properties whose name starts with the property's hint string. The group ends when a property does not start with that hint string or when a new group starts. An empty group name effectively ends the current group. [EditorInspector] will create a top-level section for each group. For example, if a property with group usage is named [code]Collide With[/code] and its hint string is [code]collide_with_[/code], a subsequent [code]collide_with_area[/code] property will be shown as "Area" inside the "Collide With" section. + If a property has [constant PROPERTY_USAGE_SUBGROUP] usage, a subgroup will be created in the same way as a group, and a second-level section will be created for each subgroup. [b]Note:[/b] Unlike sections created from path-like property names, [EditorInspector] won't capitalize the name for sections created from groups. So properties with group usage usually use capitalized names instead of snake_cased names. </description> <tutorials> diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 594c180894..bb319cb5a3 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -222,7 +222,7 @@ <method name="save_scene"> <return type="int" enum="Error" /> <description> - Saves the scene. Returns either [code]OK[/code] or [code]ERR_CANT_CREATE[/code] (see [@GlobalScope] constants). + Saves the scene. Returns either [constant OK] or [constant ERR_CANT_CREATE]. </description> </method> <method name="save_scene_as"> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index e413c526f4..2289a373bf 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -100,7 +100,8 @@ <param index="0" name="viewport_camera" type="Camera3D" /> <param index="1" name="event" type="InputEvent" /> <description> - Called when there is a root node in the current edited scene, [method _handles] is implemented, and an [InputEvent] happens in the 3D viewport. The return value decides whether the [InputEvent] is consumed or forwarded to other [EditorPlugin]s. See [enum AfterGUIInput] for options. Example: + Called when there is a root node in the current edited scene, [method _handles] is implemented, and an [InputEvent] happens in the 3D viewport. The return value decides whether the [InputEvent] is consumed or forwarded to other [EditorPlugin]s. See [enum AfterGUIInput] for options. + [b]Example:[/b] [codeblocks] [gdscript] # Prevents the InputEvent from reaching other Editor classes. @@ -115,7 +116,8 @@ } [/csharp] [/codeblocks] - Must [code]return EditorPlugin.AFTER_GUI_INPUT_PASS[/code] in order to forward the [InputEvent] to other Editor classes. Example: + Must [code]return EditorPlugin.AFTER_GUI_INPUT_PASS[/code] in order to forward the [InputEvent] to other Editor classes. + [b]Example:[/b] [codeblocks] [gdscript] # Consumes InputEventMouseMotion and forwards other InputEvent types. @@ -182,7 +184,8 @@ <return type="bool" /> <param index="0" name="event" type="InputEvent" /> <description> - Called when there is a root node in the current edited scene, [method _handles] is implemented and an [InputEvent] happens in the 2D viewport. Intercepts the [InputEvent], if [code]return true[/code] [EditorPlugin] consumes the [param event], otherwise forwards [param event] to other Editor classes. Example: + Called when there is a root node in the current edited scene, [method _handles] is implemented and an [InputEvent] happens in the 2D viewport. Intercepts the [InputEvent], if [code]return true[/code] [EditorPlugin] consumes the [param event], otherwise forwards [param event] to other Editor classes. + [b]Example:[/b] [codeblocks] [gdscript] # Prevents the InputEvent from reaching other Editor classes. @@ -197,7 +200,8 @@ } [/csharp] [/codeblocks] - Must [code]return false[/code] in order to forward the [InputEvent] to other Editor classes. Example: + Must [code]return false[/code] in order to forward the [InputEvent] to other Editor classes. + [b]Example:[/b] [codeblocks] [gdscript] # Consumes InputEventMouseMotion and forwards other InputEvent types. diff --git a/doc/classes/EditorUndoRedoManager.xml b/doc/classes/EditorUndoRedoManager.xml index 1350e4487c..c1e771d653 100644 --- a/doc/classes/EditorUndoRedoManager.xml +++ b/doc/classes/EditorUndoRedoManager.xml @@ -107,6 +107,18 @@ </description> </method> </methods> + <signals> + <signal name="history_changed"> + <description> + Emitted when the list of actions in any history has changed, either when an action is commited or a history is cleared. + </description> + </signal> + <signal name="version_changed"> + <description> + Emitted when the version of any history has changed as a result of undo or redo call. + </description> + </signal> + </signals> <constants> <constant name="GLOBAL_HISTORY" value="0" enum="SpecialHistory"> Global history not associated with any scene, but with external resources etc. diff --git a/doc/classes/FlowContainer.xml b/doc/classes/FlowContainer.xml index d449049ef1..7a324160c9 100644 --- a/doc/classes/FlowContainer.xml +++ b/doc/classes/FlowContainer.xml @@ -18,11 +18,25 @@ </method> </methods> <members> + <member name="alignment" type="int" setter="set_alignment" getter="get_alignment" enum="FlowContainer.AlignmentMode" default="0"> + The alignment of the container's children (must be one of [constant ALIGNMENT_BEGIN], [constant ALIGNMENT_CENTER], or [constant ALIGNMENT_END]). + </member> <member name="vertical" type="bool" setter="set_vertical" getter="is_vertical" default="false"> If [code]true[/code], the [FlowContainer] will arrange its children vertically, rather than horizontally. Can't be changed when using [HFlowContainer] and [VFlowContainer]. </member> </members> + <constants> + <constant name="ALIGNMENT_BEGIN" value="0" enum="AlignmentMode"> + The child controls will be arranged at the beginning of the container, i.e. top if orientation is vertical, left if orientation is horizontal (right for RTL layout). + </constant> + <constant name="ALIGNMENT_CENTER" value="1" enum="AlignmentMode"> + The child controls will be centered in the container. + </constant> + <constant name="ALIGNMENT_END" value="2" enum="AlignmentMode"> + The child controls will be arranged at the end of the container, i.e. bottom if orientation is vertical, right if orientation is horizontal (left for RTL layout). + </constant> + </constants> <theme_items> <theme_item name="h_separation" data_type="constant" type="int" default="4"> The horizontal separation of children nodes. diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml index dac822ae2f..1528045a6a 100644 --- a/doc/classes/FontFile.xml +++ b/doc/classes/FontFile.xml @@ -14,7 +14,6 @@ [b]Note:[/b] A character is a symbol that represents an item (letter, digit etc.) in an abstract way. [b]Note:[/b] A glyph is a bitmap or shape used to draw a one or more characters in a context-dependent manner. Glyph indices are bound to the specific font data source. [b]Note:[/b] If a none of the font data sources contain glyphs for a character used in a string, the character in question will be replaced with a box displaying its hexadecimal code. - [codeblocks] [gdscript] var f = load("res://BarlowCondensed-Bold.ttf") diff --git a/doc/classes/FontVariation.xml b/doc/classes/FontVariation.xml index 6aa381c2de..e0fad126b9 100644 --- a/doc/classes/FontVariation.xml +++ b/doc/classes/FontVariation.xml @@ -5,7 +5,6 @@ </brief_description> <description> OpenType variations, simulated bold / slant, and additional font settings like OpenType features and extra spacing. - To use simulated bold font variant: [codeblocks] [gdscript] diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index 64a3315308..c504e26d58 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -30,7 +30,6 @@ if error != OK: push_error("An error occurred in the HTTP request.") - # Called when the HTTP request is completed. func _http_request_completed(result, response_code, headers, body): var json = JSON.new() @@ -95,7 +94,6 @@ if error != OK: push_error("An error occurred in the HTTP request.") - # Called when the HTTP request is completed. func _http_request_completed(result, response_code, headers, body): if result != HTTPRequest.RESULT_SUCCESS: diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 88cea6820e..ed398d3b58 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -471,7 +471,8 @@ <param index="1" name="y" type="int" /> <param index="2" name="color" type="Color" /> <description> - Sets the [Color] of the pixel at [code](x, y)[/code] to [param color]. Example: + Sets the [Color] of the pixel at [code](x, y)[/code] to [param color]. + [b]Example:[/b] [codeblocks] [gdscript] var img_width = 10 @@ -498,7 +499,8 @@ <param index="0" name="point" type="Vector2i" /> <param index="1" name="color" type="Color" /> <description> - Sets the [Color] of the pixel at [param point] to [param color]. Example: + Sets the [Color] of the pixel at [param point] to [param color]. + [b]Example:[/b] [codeblocks] [gdscript] var img_width = 10 diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml index 1c9655c266..b80857a7bf 100644 --- a/doc/classes/ImporterMesh.xml +++ b/doc/classes/ImporterMesh.xml @@ -5,7 +5,6 @@ </brief_description> <description> ImporterMesh is a type of [Resource] analogous to [ArrayMesh]. It contains vertex array-based geometry, divided in [i]surfaces[/i]. Each surface contains a completely separate array and a material used to draw it. Design wise, a mesh with multiple surfaces is preferred to a single surface, because objects created in 3D editing software commonly contain multiple materials. - Unlike its runtime counterpart, [ImporterMesh] contains mesh data before various import steps, such as lod and shadow mesh generation, have taken place. Modify surface data by calling [method clear], followed by [method add_surface] for each surface. </description> <tutorials> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 89d52caa02..d246e64251 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -254,7 +254,7 @@ <param index="0" name="event" type="InputEvent" /> <description> Feeds an [InputEvent] to the game. Can be used to artificially trigger input events from code. Also generates [method Node._input] calls. - Example: + [b]Example:[/b] [codeblocks] [gdscript] var cancel_event = InputEventAction.new() @@ -366,7 +366,7 @@ <param index="0" name="position" type="Vector2" /> <description> Sets the mouse position to the specified vector, provided in pixels and relative to an origin at the upper left corner of the currently focused Window Manager game window. - Mouse position is clipped to the limits of the screen resolution, or to the limits of the game window if [enum MouseMode] is set to [code]MOUSE_MODE_CONFINED[/code] or [code]MOUSE_MODE_CONFINED_HIDDEN[/code]. + Mouse position is clipped to the limits of the screen resolution, or to the limits of the game window if [enum MouseMode] is set to [constant MOUSE_MODE_CONFINED] or [constant MOUSE_MODE_CONFINED_HIDDEN]. </description> </method> </methods> diff --git a/doc/classes/InputEventMIDI.xml b/doc/classes/InputEventMIDI.xml index 04d8cab065..67e7ced2e8 100644 --- a/doc/classes/InputEventMIDI.xml +++ b/doc/classes/InputEventMIDI.xml @@ -69,18 +69,18 @@ The MIDI channel of this input event. There are 16 channels, so this value ranges from 0 to 15. MIDI channel 9 is reserved for the use with percussion instruments, the rest of the channels are for non-percussion instruments. </member> <member name="controller_number" type="int" setter="set_controller_number" getter="get_controller_number" default="0"> - If the message is [code]MIDI_MESSAGE_CONTROL_CHANGE[/code], this indicates the controller number, otherwise this is zero. Controllers include devices such as pedals and levers. + If the message is [constant MIDI_MESSAGE_CONTROL_CHANGE], this indicates the controller number, otherwise this is zero. Controllers include devices such as pedals and levers. </member> <member name="controller_value" type="int" setter="set_controller_value" getter="get_controller_value" default="0"> - If the message is [code]MIDI_MESSAGE_CONTROL_CHANGE[/code], this indicates the controller value, otherwise this is zero. Controllers include devices such as pedals and levers. + If the message is [constant MIDI_MESSAGE_CONTROL_CHANGE], this indicates the controller value, otherwise this is zero. Controllers include devices such as pedals and levers. </member> <member name="instrument" type="int" setter="set_instrument" getter="get_instrument" default="0"> The instrument of this input event. This value ranges from 0 to 127. Refer to the instrument list on the General MIDI wikipedia article to see a list of instruments, except that this value is 0-index, so subtract one from every number on that chart. A standard piano will have an instrument number of 0. </member> <member name="message" type="int" setter="set_message" getter="get_message" enum="MIDIMessage" default="0"> - Returns a value indicating the type of message for this MIDI signal. This is a member of the [enum @GlobalScope.MIDIMessage] enum. + Returns a value indicating the type of message for this MIDI signal. This is a member of the [enum MIDIMessage] enum. For MIDI messages between 0x80 and 0xEF, only the left half of the bits are returned as this value, as the other part is the channel (ex: 0x94 becomes 0x9). For MIDI messages from 0xF0 to 0xFF, the value is returned as-is. - Notes will return [code]MIDI_MESSAGE_NOTE_ON[/code] when activated, but they might not always return [code]MIDI_MESSAGE_NOTE_OFF[/code] when deactivated, therefore your code should treat the input as stopped if some period of time has passed. + Notes will return [constant MIDI_MESSAGE_NOTE_ON] when activated, but they might not always return [constant MIDI_MESSAGE_NOTE_OFF] when deactivated, therefore your code should treat the input as stopped if some period of time has passed. For more information, see the MIDI message status byte list chart linked above. </member> <member name="pitch" type="int" setter="set_pitch" getter="get_pitch" default="0"> diff --git a/doc/classes/JSON.xml b/doc/classes/JSON.xml index 125d016632..93731cf553 100644 --- a/doc/classes/JSON.xml +++ b/doc/classes/JSON.xml @@ -54,7 +54,7 @@ <param index="0" name="json_string" type="String" /> <description> Attempts to parse the [param json_string] provided. - Returns an [enum Error]. If the parse was successful, it returns [code]OK[/code] and the result can be retrieved using [member data]. If unsuccessful, use [method get_error_line] and [method get_error_message] for identifying the source of the failure. + Returns an [enum Error]. If the parse was successful, it returns [constant OK] and the result can be retrieved using [member data]. If unsuccessful, use [method get_error_line] and [method get_error_message] for identifying the source of the failure. Non-static variant of [method parse_string], if you want custom error handling. </description> </method> diff --git a/doc/classes/JavaScriptObject.xml b/doc/classes/JavaScriptObject.xml index 26792bd19e..7132cc5cad 100644 --- a/doc/classes/JavaScriptObject.xml +++ b/doc/classes/JavaScriptObject.xml @@ -5,7 +5,7 @@ </brief_description> <description> JavaScriptObject is used to interact with JavaScript objects retrieved or created via [method JavaScriptBridge.get_interface], [method JavaScriptBridge.create_object], or [method JavaScriptBridge.create_callback]. - Example: + [b]Example:[/b] [codeblock] extends Node diff --git a/doc/classes/MultiplayerPeer.xml b/doc/classes/MultiplayerPeer.xml index 6661fbfe0b..dd7dac3f23 100644 --- a/doc/classes/MultiplayerPeer.xml +++ b/doc/classes/MultiplayerPeer.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="MultiplayerPeer" inherits="PacketPeer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - A high-level network interface to simplify multiplayer interactions. + Abstract class for specialized [PacketPeer]s used by the [MultiplayerAPI]. </brief_description> <description> - Manages the connection to multiplayer peers. Assigns unique IDs to each client connected to the server. See also [MultiplayerAPI]. - [b]Note:[/b] The high-level multiplayer API protocol is an implementation detail and isn't meant to be used by non-Godot servers. It may change without notice. + Manages the connection with one or more remote peers acting as server or client and assigning unique IDs to each of them. See also [MultiplayerAPI]. + [b]Note:[/b] The [MultiplayerAPI] protocol is an implementation detail and isn't meant to be used by non-Godot servers. It may change without notice. [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> @@ -97,49 +97,34 @@ </member> </members> <signals> - <signal name="connection_failed"> - <description> - Emitted when a connection attempt fails. - </description> - </signal> - <signal name="connection_succeeded"> - <description> - Emitted when a connection attempt succeeds. - </description> - </signal> <signal name="peer_connected"> <param index="0" name="id" type="int" /> <description> - Emitted by the server when a client connects. + Emitted when a remote peer connects. </description> </signal> <signal name="peer_disconnected"> <param index="0" name="id" type="int" /> <description> - Emitted by the server when a client disconnects. - </description> - </signal> - <signal name="server_disconnected"> - <description> - Emitted by clients when the server disconnects. + Emitted when a remote peer has disconnected. </description> </signal> </signals> <constants> <constant name="CONNECTION_DISCONNECTED" value="0" enum="ConnectionStatus"> - The ongoing connection disconnected. + The MultiplayerPeer is disconnected. </constant> <constant name="CONNECTION_CONNECTING" value="1" enum="ConnectionStatus"> - A connection attempt is ongoing. + The MultiplayerPeer is currently connecting to a server. </constant> <constant name="CONNECTION_CONNECTED" value="2" enum="ConnectionStatus"> - The connection attempt succeeded. + This MultiplayerPeer is connected. </constant> <constant name="TARGET_PEER_BROADCAST" value="0"> - Packets are sent to the server and then redistributed to other peers. + Packets are sent to all connected peers. </constant> <constant name="TARGET_PEER_SERVER" value="1"> - Packets are sent to the server alone. + Packets are sent to the remote peer acting as server. </constant> <constant name="TRANSFER_MODE_UNRELIABLE" value="0" enum="TransferMode"> Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [constant TRANSFER_MODE_UNRELIABLE_ORDERED]. Use for non-critical data, and always consider whether the order matters. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index bf52c55d67..b83bff5d5e 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -630,7 +630,7 @@ <return type="int" enum="Error" /> <param index="0" name="method" type="StringName" /> <description> - Sends a remote procedure call request for the given [param method] to peers on the network (and locally), optionally sending all additional arguments as arguments to the method called by the RPC. The call request will only be received by nodes with the same [NodePath], including the exact same node name. Behaviour depends on the RPC configuration for the given method, see [method rpc_config]. Methods are not exposed to RPCs by default. Returns [code]null[/code]. + Sends a remote procedure call request for the given [param method] to peers on the network (and locally), optionally sending all additional arguments as arguments to the method called by the RPC. The call request will only be received by nodes with the same [NodePath], including the exact same node name. Behavior depends on the RPC configuration for the given method, see [method rpc_config]. Methods are not exposed to RPCs by default. Returns [code]null[/code]. [b]Note:[/b] You can only safely use RPCs on clients after you received the [code]connected_to_server[/code] signal from the [MultiplayerAPI]. You also need to keep track of the connection state, either by the [MultiplayerAPI] signals like [code]server_disconnected[/code] or by checking [code]get_multiplayer().peer.get_connection_status() == CONNECTION_CONNECTED[/code]. </description> </method> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 0c74690924..f4a5288481 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -134,7 +134,7 @@ <method name="get_cache_dir" qualifiers="const"> <return type="String" /> <description> - Returns the [i]global[/i] cache data directory according to the operating system's standards. On desktop platforms, this path can be overridden by setting the [code]XDG_CACHE_HOME[/code] environment variable before starting the project. See [url=$DOCS_URL/tutorials/io/data_paths.html]File paths in Godot projects[/url] in the documentation for more information. See also [method get_config_dir] and [method get_data_dir]. + Returns the [i]global[/i] cache data directory according to the operating system's standards. On the Linux/BSD platform, this path can be overridden by setting the [code]XDG_CACHE_HOME[/code] environment variable before starting the project. See [url=$DOCS_URL/tutorials/io/data_paths.html]File paths in Godot projects[/url] in the documentation for more information. See also [method get_config_dir] and [method get_data_dir]. Not to be confused with [method get_user_data_dir], which returns the [i]project-specific[/i] user data path. </description> </method> @@ -192,7 +192,7 @@ <method name="get_config_dir" qualifiers="const"> <return type="String" /> <description> - Returns the [i]global[/i] user configuration directory according to the operating system's standards. On desktop platforms, this path can be overridden by setting the [code]XDG_CONFIG_HOME[/code] environment variable before starting the project. See [url=$DOCS_URL/tutorials/io/data_paths.html]File paths in Godot projects[/url] in the documentation for more information. See also [method get_cache_dir] and [method get_data_dir]. + Returns the [i]global[/i] user configuration directory according to the operating system's standards. On the Linux/BSD platform, this path can be overridden by setting the [code]XDG_CONFIG_HOME[/code] environment variable before starting the project. See [url=$DOCS_URL/tutorials/io/data_paths.html]File paths in Godot projects[/url] in the documentation for more information. See also [method get_cache_dir] and [method get_data_dir]. Not to be confused with [method get_user_data_dir], which returns the [i]project-specific[/i] user data path. </description> </method> @@ -200,14 +200,14 @@ <return type="PackedStringArray" /> <description> Returns an array of MIDI device names. - The returned array will be empty if the system MIDI driver has not previously been initialised with [method open_midi_inputs]. + The returned array will be empty if the system MIDI driver has not previously been initialized with [method open_midi_inputs]. [b]Note:[/b] This method is implemented on Linux, macOS and Windows. </description> </method> <method name="get_data_dir" qualifiers="const"> <return type="String" /> <description> - Returns the [i]global[/i] user data directory according to the operating system's standards. On desktop platforms, this path can be overridden by setting the [code]XDG_DATA_HOME[/code] environment variable before starting the project. See [url=$DOCS_URL/tutorials/io/data_paths.html]File paths in Godot projects[/url] in the documentation for more information. See also [method get_cache_dir] and [method get_config_dir]. + Returns the [i]global[/i] user data directory according to the operating system's standards. On the Linux/BSD platform, this path can be overridden by setting the [code]XDG_DATA_HOME[/code] environment variable before starting the project. See [url=$DOCS_URL/tutorials/io/data_paths.html]File paths in Godot projects[/url] in the documentation for more information. See also [method get_cache_dir] and [method get_config_dir]. Not to be confused with [method get_user_data_dir], which returns the [i]project-specific[/i] user data path. </description> </method> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index 34cf259f63..318f2cfade 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -108,7 +108,8 @@ <return type="Variant" /> <param index="0" name="method" type="StringName" /> <description> - Calls the [param method] on the object and returns the result. This method supports a variable number of arguments, so parameters are passed as a comma separated list. Example: + Calls the [param method] on the object and returns the result. This method supports a variable number of arguments, so parameters are passed as a comma separated list. + [b]Example:[/b] [codeblocks] [gdscript] var node = Node3D.new() @@ -126,7 +127,8 @@ <return type="Variant" /> <param index="0" name="method" type="StringName" /> <description> - Calls the [param method] on the object during idle time. This method supports a variable number of arguments, so parameters are passed as a comma separated list. Example: + Calls the [param method] on the object during idle time. This method supports a variable number of arguments, so parameters are passed as a comma separated list. + [b]Example:[/b] [codeblocks] [gdscript] var node = Node3D.new() @@ -318,7 +320,8 @@ <return type="int" enum="Error" /> <param index="0" name="signal" type="StringName" /> <description> - Emits the given [param signal]. The signal must exist, so it should be a built-in signal of this class or one of its parent classes, or a user-defined signal. This method supports a variable number of arguments, so parameters are passed as a comma separated list. Example: + Emits the given [param signal]. The signal must exist, so it should be a built-in signal of this class or one of its parent classes, or a user-defined signal. This method supports a variable number of arguments, so parameters are passed as a comma separated list. + [b]Example:[/b] [codeblocks] [gdscript] emit_signal("hit", "sword", 100) @@ -366,7 +369,8 @@ <return type="Variant" /> <param index="0" name="property_path" type="NodePath" /> <description> - Gets the object's property indexed by the given [param property_path]. The path should be a [NodePath] relative to the current object and can use the colon character ([code]:[/code]) to access nested properties. Examples: [code]"position:x"[/code] or [code]"material:next_pass:blend_mode"[/code]. + Gets the object's property indexed by the given [param property_path]. The path should be a [NodePath] relative to the current object and can use the colon character ([code]:[/code]) to access nested properties. + [b]Examples:[/b] [code]"position:x"[/code] or [code]"material:next_pass:blend_mode"[/code]. [b]Note:[/b] Even though the method takes [NodePath] argument, it doesn't support actual paths to [Node]s in the scene tree, only colon-separated sub-property paths. For the purpose of nodes, use [method Node.get_node_and_resource] instead. </description> </method> @@ -535,7 +539,8 @@ <param index="0" name="property_path" type="NodePath" /> <param index="1" name="value" type="Variant" /> <description> - Assigns a new value to the property identified by the [param property_path]. The path should be a [NodePath] relative to the current object and can use the colon character ([code]:[/code]) to access nested properties. Example: + Assigns a new value to the property identified by the [param property_path]. The path should be a [NodePath] relative to the current object and can use the colon character ([code]:[/code]) to access nested properties. + [b]Example:[/b] [codeblocks] [gdscript] var node = Node2D.new() diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index 199535298c..e63e822648 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -216,7 +216,7 @@ <signal name="item_focused"> <param index="0" name="index" type="int" /> <description> - Emitted when the user navigates to an item using the [code]ui_up[/code] or [code]ui_down[/code] actions. The index of the item selected is passed as argument. + Emitted when the user navigates to an item using the [member ProjectSettings.input/ui_up] or [member ProjectSettings.input/ui_down] input actions. The index of the item selected is passed as argument. </description> </signal> <signal name="item_selected"> diff --git a/doc/classes/PhysicsTestMotionParameters2D.xml b/doc/classes/PhysicsTestMotionParameters2D.xml index d8f96511dd..5b969ea869 100644 --- a/doc/classes/PhysicsTestMotionParameters2D.xml +++ b/doc/classes/PhysicsTestMotionParameters2D.xml @@ -30,7 +30,7 @@ </member> <member name="recovery_as_collision" type="bool" setter="set_recovery_as_collision_enabled" getter="is_recovery_as_collision_enabled" default="false"> If set to [code]true[/code], any depenetration from the recovery phase is reported as a collision; this is used e.g. by [CharacterBody2D] for improving floor detection during floor snapping. - If set to [code]false[/code], only collisions resulting from the motion are reported, which is generally the desired behaviour. + If set to [code]false[/code], only collisions resulting from the motion are reported, which is generally the desired behavior. </member> </members> </class> diff --git a/doc/classes/PhysicsTestMotionParameters3D.xml b/doc/classes/PhysicsTestMotionParameters3D.xml index e8f80ae131..ea9bca6a35 100644 --- a/doc/classes/PhysicsTestMotionParameters3D.xml +++ b/doc/classes/PhysicsTestMotionParameters3D.xml @@ -33,7 +33,7 @@ </member> <member name="recovery_as_collision" type="bool" setter="set_recovery_as_collision_enabled" getter="is_recovery_as_collision_enabled" default="false"> If set to [code]true[/code], any depenetration from the recovery phase is reported as a collision; this is used e.g. by [CharacterBody3D] for improving floor detection during floor snapping. - If set to [code]false[/code], only collisions resulting from the motion are reported, which is generally the desired behaviour. + If set to [code]false[/code], only collisions resulting from the motion are reported, which is generally the desired behavior. </member> </members> </class> diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index c99760996f..0dcb33ac4c 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -526,15 +526,14 @@ <signal name="id_focused"> <param index="0" name="id" type="int" /> <description> - Emitted when user navigated to an item of some [param id] using [code]ui_up[/code] or [code]ui_down[/code] action. + Emitted when the user navigated to an item of some [param id] using the [member ProjectSettings.input/ui_up] or [member ProjectSettings.input/ui_down] input action. </description> </signal> <signal name="id_pressed"> <param index="0" name="id" type="int" /> <description> Emitted when an item of some [param id] is pressed or its accelerator is activated. - - [b]Note:[/b] If [param id] is negative (either explicitly or due to overflow), this will return the correponding index instead. + [b]Note:[/b] If [param id] is negative (either explicitly or due to overflow), this will return the corresponding index instead. </description> </signal> <signal name="index_pressed"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 91d9bdd4a2..376994f0b5 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -729,7 +729,7 @@ Default font glyph sub-pixel positioning mode. See [member FontFile.subpixel_positioning]. </member> <member name="gui/theme/default_theme_scale" type="float" setter="" getter="" default="1.0"> - The default scale factor for [Control]s, when not overriden by a [Theme]. + The default scale factor for [Control]s, when not overridden by a [Theme]. [b]Note:[/b] This property is only read when the project starts. To change the default scale at runtime, set [member ThemeDB.fallback_base_scale] instead. </member> <member name="gui/theme/lcd_subpixel_layout" type="int" setter="" getter="" default="1"> @@ -937,6 +937,11 @@ <member name="input/ui_text_caret_word_right.macos" type="Dictionary" setter="" getter=""> macOS specific override for the shortcut to move the text cursor forward one word. </member> + <member name="input/ui_text_clear_carets_and_selection" type="Dictionary" setter="" getter=""> + If there's only one caret active and with a selection, clears the selection. + In case there's more than one caret active, removes the secondary carets and clears their selections. + [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. + </member> <member name="input/ui_text_completion_accept" type="Dictionary" setter="" getter=""> Default [InputEventAction] to accept an autocompetion hint. [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. @@ -987,10 +992,6 @@ Default [InputEventAction] to insert a new line after the current one. [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. </member> - <member name="input/ui_text_remove_secondary_carets" type="Dictionary" setter="" getter=""> - If multiple carets are currently active, clear additional carets and keep just one caret. - [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. - </member> <member name="input/ui_text_scroll_down" type="Dictionary" setter="" getter=""> Default [InputEventAction] to scroll down one line of 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. @@ -1018,7 +1019,7 @@ [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. </member> <member name="input/ui_text_toggle_insert_mode" type="Dictionary" setter="" getter=""> - Default [InputEventAction] to toggle [i]instert mode[/i] in a text field. While in insert mode, inserting new text overrides the character after the cursor, unless the next character is a new line. + Default [InputEventAction] to toggle [i]insert mode[/i] in a text field. While in insert mode, inserting new text overrides the character after the cursor, unless the next character is a new line. [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. </member> <member name="input/ui_undo" type="Dictionary" setter="" getter=""> @@ -1890,7 +1891,7 @@ Sets the quality for rough screen-space reflections. Turning off will make all screen space reflections sharp, while higher values make rough reflections look better. </member> <member name="rendering/environment/ssao/adaptive_target" type="float" setter="" getter="" default="0.5"> - Quality target to use when [member rendering/environment/ssao/quality] is set to [code]ULTRA[/code]. A value of [code]0.0[/code] provides a quality and speed similar to [code]MEDIUM[/code] while a value of [code]1.0[/code] provides much higher quality than any of the other settings at the cost of performance. + Quality target to use when [member rendering/environment/ssao/quality] is set to [code]Ultra[/code]. A value of [code]0.0[/code] provides a quality and speed similar to [code]Medium[/code] while a value of [code]1.0[/code] provides much higher quality than any of the other settings at the cost of performance. </member> <member name="rendering/environment/ssao/blur_passes" type="int" setter="" getter="" default="2"> Number of blur passes to use when computing screen-space ambient occlusion. A higher number will result in a smoother look, but will be slower to compute and will have less high-frequency detail. @@ -1905,10 +1906,10 @@ If [code]true[/code], screen-space ambient occlusion will be rendered at half size and then upscaled before being added to the scene. This is significantly faster but may miss small details. If [code]false[/code], screen-space ambient occlusion will be rendered at full size. </member> <member name="rendering/environment/ssao/quality" type="int" setter="" getter="" default="2"> - Sets the quality of the screen-space ambient occlusion effect. Higher values take more samples and so will result in better quality, at the cost of performance. Setting to [code]ULTRA[/code] will use the [member rendering/environment/ssao/adaptive_target] setting. + Sets the quality of the screen-space ambient occlusion effect. Higher values take more samples and so will result in better quality, at the cost of performance. Setting to [code]Ultra[/code] will use the [member rendering/environment/ssao/adaptive_target] setting. </member> <member name="rendering/environment/ssil/adaptive_target" type="float" setter="" getter="" default="0.5"> - Quality target to use when [member rendering/environment/ssil/quality] is set to [code]ULTRA[/code]. A value of [code]0.0[/code] provides a quality and speed similar to [code]MEDIUM[/code] while a value of [code]1.0[/code] provides much higher quality than any of the other settings at the cost of performance. When using the adaptive target, the performance cost scales with the complexity of the scene. + Quality target to use when [member rendering/environment/ssil/quality] is set to [code]Ultra[/code]. A value of [code]0.0[/code] provides a quality and speed similar to [code]Medium[/code] while a value of [code]1.0[/code] provides much higher quality than any of the other settings at the cost of performance. When using the adaptive target, the performance cost scales with the complexity of the scene. </member> <member name="rendering/environment/ssil/blur_passes" type="int" setter="" getter="" default="4"> Number of blur passes to use when computing screen-space indirect lighting. A higher number will result in a smoother look, but will be slower to compute and will have less high-frequency detail. @@ -1923,7 +1924,7 @@ If [code]true[/code], screen-space indirect lighting will be rendered at half size and then upscaled before being added to the scene. This is significantly faster but may miss small details and may result in some objects appearing to glow at their edges. </member> <member name="rendering/environment/ssil/quality" type="int" setter="" getter="" default="2"> - Sets the quality of the screen-space indirect lighting effect. Higher values take more samples and so will result in better quality, at the cost of performance. Setting to [code]ULTRA[/code] will use the [member rendering/environment/ssil/adaptive_target] setting. + Sets the quality of the screen-space indirect lighting effect. Higher values take more samples and so will result in better quality, at the cost of performance. Setting to [code]Ultra[/code] will use the [member rendering/environment/ssil/adaptive_target] setting. </member> <member name="rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale" type="float" setter="" getter="" default="0.01"> Scales the depth over which the subsurface scattering effect is applied. A high value may allow light to scatter into a part of the mesh or another mesh that is close in screen space but far in depth. @@ -2260,7 +2261,7 @@ Action map configuration to load by default. </member> <member name="xr/openxr/enabled" type="bool" setter="" getter="" default="false"> - If [code]true[/code] Godot will setup and initialise OpenXR on startup. + If [code]true[/code] Godot will setup and initialize OpenXR on startup. </member> <member name="xr/openxr/form_factor" type="int" setter="" getter="" default=""0""> Specify whether OpenXR should be configured for an HMD or a hand held device. diff --git a/doc/classes/PropertyTweener.xml b/doc/classes/PropertyTweener.xml index 78ead65cdd..61e8357809 100644 --- a/doc/classes/PropertyTweener.xml +++ b/doc/classes/PropertyTweener.xml @@ -13,7 +13,8 @@ <method name="as_relative"> <return type="PropertyTweener" /> <description> - When called, the final value will be used as a relative value instead. Example: + When called, the final value will be used as a relative value instead. + [b]Example:[/b] [codeblock] var tween = get_tree().create_tween() tween.tween_property(self, "position", Vector2.RIGHT * 100, 1).as_relative() #the node will move by 100 pixels to the right @@ -24,7 +25,8 @@ <return type="PropertyTweener" /> <param index="0" name="value" type="Variant" /> <description> - Sets a custom initial value to the [PropertyTweener]. Example: + Sets a custom initial value to the [PropertyTweener]. + [b]Example:[/b] [codeblock] var tween = get_tree().create_tween() tween.tween_property(self, "position", Vector2(200, 100), 1).from(Vector2(100, 100)) #this will move the node from position (100, 100) to (200, 100) diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml index 99dffeff9d..08a9231751 100644 --- a/doc/classes/Quaternion.xml +++ b/doc/classes/Quaternion.xml @@ -43,12 +43,6 @@ </constructor> <constructor name="Quaternion"> <return type="Quaternion" /> - <param index="0" name="euler_yxz" type="Vector3" /> - <description> - </description> - </constructor> - <constructor name="Quaternion"> - <return type="Quaternion" /> <param index="0" name="from" type="Basis" /> <description> Constructs a quaternion from the given [Basis]. @@ -86,6 +80,13 @@ <description> </description> </method> + <method name="from_euler" qualifiers="static"> + <return type="Quaternion" /> + <param index="0" name="euler" type="Vector3" /> + <description> + Constructs a Quaternion from Euler angles in YXZ rotation order. + </description> + </method> <method name="get_angle" qualifiers="const"> <return type="float" /> <description> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 51c2498ea7..cb13697cc2 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -232,7 +232,7 @@ <param index="0" name="draw_list" type="int" /> <param index="1" name="color" type="Color" /> <description> - Sets blend constants for draw list, blend constants are used only if the graphics pipeline is created with [code]DYNAMIC_STATE_BLEND_CONSTANTS[/code] flag set. + Sets blend constants for draw list, blend constants are used only if the graphics pipeline is created with [constant DYNAMIC_STATE_BLEND_CONSTANTS] flag set. </description> </method> <method name="draw_list_set_push_constant"> diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml index b2b8a37e39..e533fc1e32 100644 --- a/doc/classes/Resource.xml +++ b/doc/classes/Resource.xml @@ -24,7 +24,7 @@ <param index="0" name="subresources" type="bool" default="false" /> <description> Duplicates this resource, returning a new resource with its [code]export[/code]ed or [constant PROPERTY_USAGE_STORAGE] properties copied from the original. - If [param subresources] is [code]false[/code], a shallow copy is returned. Nested resources within subresources are not duplicated and are shared from the original resource. This behavior can be overriden by the [constant PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE] flag. + If [param subresources] is [code]false[/code], a shallow copy is returned. Nested resources within subresources are not duplicated and are shared from the original resource. This behavior can be overridden by the [constant PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE] flag. [b]Note:[/b] For custom resources, this method will fail if [method Object._init] has been defined with required parameters. </description> </method> @@ -51,7 +51,7 @@ <method name="get_rid" qualifiers="const"> <return type="RID" /> <description> - Returns the [RID] of this resource (or an empty RID). Many resources (such as [Texture2D], [Mesh], and so on) are high-level abstractions of resources stored in a specialised server ([DisplayServer], [RenderingServer], etc.), so this function will return the original [RID]. + Returns the [RID] of this resource (or an empty RID). Many resources (such as [Texture2D], [Mesh], and so on) are high-level abstractions of resources stored in a specialized server ([DisplayServer], [RenderingServer], etc.), so this function will return the original [RID]. </description> </method> <method name="setup_local_to_scene"> diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml index f5018c25ff..b96b7187e4 100644 --- a/doc/classes/ScrollContainer.xml +++ b/doc/classes/ScrollContainer.xml @@ -6,7 +6,7 @@ <description> A ScrollContainer node meant to contain a [Control] child. ScrollContainers will automatically create a scrollbar child ([HScrollBar], [VScrollBar], or both) when needed and will only draw the Control within the ScrollContainer area. Scrollbars will automatically be drawn at the right (for vertical) or bottom (for horizontal) and will enable dragging to move the viewable Control (and its children) within the ScrollContainer. Scrollbars will also automatically resize the grabber based on the [member Control.custom_minimum_size] of the Control relative to the ScrollContainer. - Works great with a [Panel] control. You can set [code]EXPAND[/code] on the children's size flags, so they will upscale to the ScrollContainer's size if it's larger (scroll is invisible for the chosen dimension). + Works great with a [Panel] control. You can set [constant Control.SIZE_EXPAND] on the children's size flags, so they will upscale to the ScrollContainer's size if it's larger (scroll is invisible for the chosen dimension). </description> <tutorials> <link title="GUI containers">$DOCS_URL/tutorials/ui/gui_containers.html</link> diff --git a/doc/classes/ShapeCast2D.xml b/doc/classes/ShapeCast2D.xml index 36c3beecb1..5a8eec3921 100644 --- a/doc/classes/ShapeCast2D.xml +++ b/doc/classes/ShapeCast2D.xml @@ -58,6 +58,13 @@ Returns the collided [Object] of one of the multiple collisions at [param index], or [code]null[/code] if no object is intersecting the shape (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> + <method name="get_collider_rid" qualifiers="const"> + <return type="RID" /> + <param index="0" name="index" type="int" /> + <description> + Returns the [RID] of the collided object of one of the multiple collisions at [param index]. + </description> + </method> <method name="get_collider_shape" qualifiers="const"> <return type="int" /> <param index="0" name="index" type="int" /> diff --git a/doc/classes/ShapeCast3D.xml b/doc/classes/ShapeCast3D.xml index cbdf660133..735b91cee9 100644 --- a/doc/classes/ShapeCast3D.xml +++ b/doc/classes/ShapeCast3D.xml @@ -58,6 +58,13 @@ Returns the collided [Object] of one of the multiple collisions at [param index], or [code]null[/code] if no object is intersecting the shape (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> + <method name="get_collider_rid" qualifiers="const"> + <return type="RID" /> + <param index="0" name="index" type="int" /> + <description> + Returns the [RID] of the collided object of one of the multiple collisions at [param index]. + </description> + </method> <method name="get_collider_shape" qualifiers="const"> <return type="int" /> <param index="0" name="index" type="int" /> diff --git a/doc/classes/Sprite2D.xml b/doc/classes/Sprite2D.xml index 83532721b2..235fef0bdd 100644 --- a/doc/classes/Sprite2D.xml +++ b/doc/classes/Sprite2D.xml @@ -13,7 +13,8 @@ <method name="get_rect" qualifiers="const"> <return type="Rect2" /> <description> - Returns a [Rect2] representing the Sprite2D's boundary in local coordinates. Can be used to detect if the Sprite2D was clicked. Example: + Returns a [Rect2] representing the Sprite2D's boundary in local coordinates. Can be used to detect if the Sprite2D was clicked. + [b]Example:[/b] [codeblocks] [gdscript] func _input(event): diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml index 4188563695..f05b5f7dbf 100644 --- a/doc/classes/StreamPeer.xml +++ b/doc/classes/StreamPeer.xml @@ -44,7 +44,7 @@ <return type="Array" /> <param index="0" name="bytes" type="int" /> <description> - Returns a chunk data with the received bytes. The number of bytes to be received can be requested in the [param bytes] argument. If not enough bytes are available, the function will block until the desired amount is received. This function returns two values, an [enum @GlobalScope.Error] code and a data array. + Returns a chunk data with the received bytes. The number of bytes to be received can be requested in the [param bytes] argument. If not enough bytes are available, the function will block until the desired amount is received. This function returns two values, an [enum Error] code and a data array. </description> </method> <method name="get_double"> @@ -63,7 +63,7 @@ <return type="Array" /> <param index="0" name="bytes" type="int" /> <description> - Returns a chunk data with the received bytes. The number of bytes to be received can be requested in the "bytes" argument. If not enough bytes are available, the function will return how many were actually received. This function returns two values, an [enum @GlobalScope.Error] code, and a data array. + Returns a chunk data with the received bytes. The number of bytes to be received can be requested in the "bytes" argument. If not enough bytes are available, the function will return how many were actually received. This function returns two values, an [enum Error] code, and a data array. </description> </method> <method name="get_string"> @@ -144,7 +144,7 @@ <return type="int" enum="Error" /> <param index="0" name="data" type="PackedByteArray" /> <description> - Sends a chunk of data through the connection, blocking if necessary until the data is done sending. This function returns an [enum @GlobalScope.Error] code. + Sends a chunk of data through the connection, blocking if necessary until the data is done sending. This function returns an [enum Error] code. </description> </method> <method name="put_double"> @@ -165,7 +165,7 @@ <return type="Array" /> <param index="0" name="data" type="PackedByteArray" /> <description> - Sends a chunk of data through the connection. If all the data could not be sent at once, only part of it will. This function returns two values, an [enum @GlobalScope.Error] code and an integer, describing how much data was actually sent. + Sends a chunk of data through the connection. If all the data could not be sent at once, only part of it will. This function returns two values, an [enum Error] code and an integer, describing how much data was actually sent. </description> </method> <method name="put_string"> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 320b9fd737..1ee91b91d2 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -239,7 +239,7 @@ <description> Splits a string using a [param delimiter] and returns a substring at index [param slice]. Returns an empty string if the index doesn't exist. This is a more performant alternative to [method split] for cases when you need only one element from the array at a fixed index. - Example: + [b]Example:[/b] [codeblock] print("i/am/example/string".get_slice("/", 2)) # Prints 'example'. [/codeblock] @@ -408,7 +408,7 @@ <param index="0" name="parts" type="PackedStringArray" /> <description> Returns a [String] which is the concatenation of the [param parts]. The separator between elements is the string providing this method. - Example: + [b]Example:[/b] [codeblocks] [gdscript] print(", ".join(["One", "Two", "Three", "Four"])) @@ -430,7 +430,7 @@ <param index="0" name="length" type="int" /> <description> Returns a number of characters from the left of the string. If negative [param length] is used, the characters are counted downwards from [String]'s length. - Examples: + [b]Example:[/b] [codeblock] print("sample text".left(3)) #prints "sam" print("sample text".left(-3)) #prints "sample t" @@ -514,7 +514,7 @@ Converts a [float] to a string representation of a decimal number. The number of decimal places can be specified with [param decimals]. If [param decimals] is [code]-1[/code] (default), decimal places will be automatically adjusted so that the string representation has 14 significant digits (counting both digits to the left and the right of the decimal point). Trailing zeros are not included in the string. The last digit will be rounded and not truncated. - Some examples: + [b]Example:[/b] [codeblock] String.num(3.141593) # "3.141593" String.num(3.141593, 3) # "3.142" @@ -617,7 +617,7 @@ <param index="0" name="length" type="int" /> <description> Returns a number of characters from the right of the string. If negative [param length] is used, the characters are counted downwards from [String]'s length. - Examples: + [b]Example:[/b] [codeblock] print("sample text".right(3)) #prints "ext" print("sample text".right(-3)) #prints "ple text" @@ -642,7 +642,7 @@ The splits in the returned array are sorted in the same order as the original string, from left to right. If [param allow_empty] is [code]true[/code], and there are two adjacent delimiters in the string, it will add an empty string to the array of substrings at this position. If [param maxsplit] is specified, it defines the number of splits to do from the right up to [param maxsplit]. The default value of 0 means that all items are split, thus giving the same result as [method split]. - Example: + [b]Example:[/b] [codeblocks] [gdscript] var some_string = "One,Two,Three,Four" @@ -718,7 +718,7 @@ If [param allow_empty] is [code]true[/code], and there are two adjacent delimiters in the string, it will add an empty string to the array of substrings at this position. If [param maxsplit] is specified, it defines the number of splits to do from the left up to [param maxsplit]. The default value of [code]0[/code] means that all items are split. If you need only one element from the array at a specific index, [method get_slice] is a more performant option. - Example: + [b]Example:[/b] [codeblocks] [gdscript] var some_string = "One,Two,Three,Four" @@ -940,12 +940,6 @@ <description> </description> </operator> - <operator name="operator +"> - <return type="String" /> - <param index="0" name="right" type="int" /> - <description> - </description> - </operator> <operator name="operator <"> <return type="bool" /> <param index="0" name="right" type="String" /> diff --git a/doc/classes/StyleBoxFlat.xml b/doc/classes/StyleBoxFlat.xml index 7f6628f8ee..d6900f935c 100644 --- a/doc/classes/StyleBoxFlat.xml +++ b/doc/classes/StyleBoxFlat.xml @@ -9,7 +9,8 @@ - Border width (individual width for each border) - Rounded corners (individual radius for each corner) - Shadow (with blur and offset) - Setting corner radius to high values is allowed. As soon as corners overlap, the stylebox will switch to a relative system. Example: + Setting corner radius to high values is allowed. As soon as corners overlap, the stylebox will switch to a relative system. + [b]Example:[/b] [codeblock] height = 30 corner_radius_top_left = 50 diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index d56cc9a31b..9d73e9fb39 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -275,28 +275,28 @@ </methods> <constants> <constant name="CUSTOM_RGBA8_UNORM" value="0" enum="CustomFormat"> - Limits range of data passed to `set_custom` to unsigned normalized 0 to 1 stored in 8 bits per channel. See [constant Mesh.ARRAY_CUSTOM_RGBA8_UNORM]. + Limits range of data passed to [method set_custom] to unsigned normalized 0 to 1 stored in 8 bits per channel. See [constant Mesh.ARRAY_CUSTOM_RGBA8_UNORM]. </constant> <constant name="CUSTOM_RGBA8_SNORM" value="1" enum="CustomFormat"> - Limits range of data passed to `set_custom` to signed normalized -1 to 1 stored in 8 bits per channel. See [constant Mesh.ARRAY_CUSTOM_RGBA8_SNORM]. + Limits range of data passed to [method set_custom] to signed normalized -1 to 1 stored in 8 bits per channel. See [constant Mesh.ARRAY_CUSTOM_RGBA8_SNORM]. </constant> <constant name="CUSTOM_RG_HALF" value="2" enum="CustomFormat"> - Stores data passed to `set_custom` as half precision floats, and uses only red and green color channels. See [constant Mesh.ARRAY_CUSTOM_RG_HALF]. + Stores data passed to [method set_custom] as half precision floats, and uses only red and green color channels. See [constant Mesh.ARRAY_CUSTOM_RG_HALF]. </constant> <constant name="CUSTOM_RGBA_HALF" value="3" enum="CustomFormat"> - Stores data passed to `set_custom` as half precision floats and uses all color channels. See [constant Mesh.ARRAY_CUSTOM_RGBA_HALF]. + Stores data passed to [method set_custom] as half precision floats and uses all color channels. See [constant Mesh.ARRAY_CUSTOM_RGBA_HALF]. </constant> <constant name="CUSTOM_R_FLOAT" value="4" enum="CustomFormat"> - Stores data passed to `set_custom` as full precision floats, and uses only red color channel. See [constant Mesh.ARRAY_CUSTOM_R_FLOAT]. + Stores data passed to [method set_custom] as full precision floats, and uses only red color channel. See [constant Mesh.ARRAY_CUSTOM_R_FLOAT]. </constant> <constant name="CUSTOM_RG_FLOAT" value="5" enum="CustomFormat"> - Stores data passed to `set_custom` as full precision floats, and uses only red and green color channels. See [constant Mesh.ARRAY_CUSTOM_RG_FLOAT]. + Stores data passed to [method set_custom] as full precision floats, and uses only red and green color channels. See [constant Mesh.ARRAY_CUSTOM_RG_FLOAT]. </constant> <constant name="CUSTOM_RGB_FLOAT" value="6" enum="CustomFormat"> - Stores data passed to `set_custom` as full precision floats, and uses only red, green and blue color channels. See [constant Mesh.ARRAY_CUSTOM_RGB_FLOAT]. + Stores data passed to [method set_custom] as full precision floats, and uses only red, green and blue color channels. See [constant Mesh.ARRAY_CUSTOM_RGB_FLOAT]. </constant> <constant name="CUSTOM_RGBA_FLOAT" value="7" enum="CustomFormat"> - Stores data passed to `set_custom` as full precision floats, and uses all color channels. See [constant Mesh.ARRAY_CUSTOM_RGBA_FLOAT]. + Stores data passed to [method set_custom] as full precision floats, and uses all color channels. See [constant Mesh.ARRAY_CUSTOM_RGBA_FLOAT]. </constant> <constant name="CUSTOM_MAX" value="8" enum="CustomFormat"> Used to indicate a disabled custom channel. diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index 302f9b329b..e2e7a0c37e 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -217,7 +217,7 @@ </theme_item> <theme_item name="side_margin" data_type="constant" type="int" default="8"> The space at the left or right edges of the tab bar, accordingly with the current [member tab_alignment]. - The margin is ignored with [code]ALIGNMENT_RIGHT[/code] if the tabs are clipped (see [member clip_tabs]) or a popup has been set (see [method set_popup]). The margin is always ignored with [code]ALIGNMENT_CENTER[/code]. + The margin is ignored with [constant TabBar.ALIGNMENT_RIGHT] if the tabs are clipped (see [member clip_tabs]) or a popup has been set (see [method set_popup]). The margin is always ignored with [constant TabBar.ALIGNMENT_CENTER]. </theme_item> <theme_item name="font" data_type="font" type="Font"> The font used to draw tab names. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 0b822a4fe9..3682908124 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1416,7 +1416,7 @@ <param index="0" name="shaped" type="RID" /> <param index="1" name="direction" type="int" enum="TextServer.Direction" default="0" /> <description> - Sets desired text direction. If set to [code]TEXT_DIRECTION_AUTO[/code], direction will be detected based on the buffer contents and current locale. + Sets desired text direction. If set to [constant DIRECTION_AUTO], direction will be detected based on the buffer contents and current locale. [b]Note:[/b] Direction is ignored if server does not support [constant FEATURE_BIDI_LAYOUT] feature (supported by [TextServerAdvanced]). </description> </method> @@ -1823,7 +1823,7 @@ Font have fixed-width characters. </constant> <constant name="STRUCTURED_TEXT_DEFAULT" value="0" enum="StructuredTextParser"> - Use default behavior. Same as [code]STRUCTURED_TEXT_NONE[/code] unless specified otherwise in the control description. + Use default behavior. Same as [constant STRUCTURED_TEXT_NONE] unless specified otherwise in the control description. </constant> <constant name="STRUCTURED_TEXT_URI" value="1" enum="StructuredTextParser"> BiDi override for URI. diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 7ced16d1af..5a24483774 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -7,7 +7,6 @@ A TileSet is a library of tiles for a [TileMap]. A TileSet handles a list of [TileSetSource], each of them storing a set of tiles. Tiles can either be from a [TileSetAtlasSource], that render tiles out of a texture with support for physics, navigation, etc... or from a [TileSetScenesCollectionSource] which exposes scene-based tiles. Tiles are referenced by using three IDs: their source ID, their atlas coordinates ID and their alternative tile ID. - A TileSet can be configured so that its tiles expose more or less properties. To do so, the TileSet resources uses property layers, that you can add or remove depending on your needs. For example, adding a physics layer allows giving collision shapes to your tiles. Each layer having dedicated properties (physics layer an mask), you may add several TileSet physics layers for each type of collision you need. See the functions to add new layers for more information. diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml index df469cd030..5efc1da6f9 100644 --- a/doc/classes/TileSetAtlasSource.xml +++ b/doc/classes/TileSetAtlasSource.xml @@ -6,9 +6,7 @@ <description> An atlas is a grid of tiles laid out on a texture. Each tile in the grid must be exposed using [method create_tile]. Those tiles are then indexed using their coordinates in the grid. Each tile can also have a size in the grid coordinates, making it more or less cells in the atlas. - Alternatives version of a tile can be created using [method create_alternative_tile], which are then indexed using an alternative ID. The main tile (the one in the grid), is accessed with an alternative ID equal to 0. - Each tile alternate has a set of properties that is defined by the source's [TileSet] layers. Those properties are stored in a TileData object that can be accessed and modified using [method get_tile_data]. As TileData properties are stored directly in the TileSetAtlasSource resource, their properties might also be set using [code]TileSetAtlasSource.set("<coords_x>:<coords_y>/<alternative_id>/<tile_data_property>")[/code]. </description> diff --git a/doc/classes/TileSetSource.xml b/doc/classes/TileSetSource.xml index e88e725bf4..4ebd7735b0 100644 --- a/doc/classes/TileSetSource.xml +++ b/doc/classes/TileSetSource.xml @@ -7,7 +7,6 @@ Exposes a set of tiles for a [TileSet] resource. Tiles in a source are indexed with two IDs, coordinates ID (of type Vector2i) and an alternative ID (of type int), named according to their use in the [TileSetAtlasSource] class. Depending on the TileSet source type, those IDs might have restrictions on their values, this is why the base [TileSetSource] class only exposes getters for them. - You can iterate over all tiles exposed by a TileSetSource by first iterating over coordinates IDs using [method get_tiles_count] and [method get_tile_id], then over alternative IDs using [method get_alternative_tiles_count] and [method get_alternative_tile_id]. </description> <tutorials> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 539ca38190..532f6703b2 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -251,7 +251,7 @@ <param index="0" name="column" type="int" /> <param index="1" name="min_width" type="int" /> <description> - Overrides the calculated minimum width of a column. It can be set to `0` to restore the default behavior. Columns that have the "Expand" flag will use their "min_width" in a similar fashion to [member Control.size_flags_stretch_ratio]. + Overrides the calculated minimum width of a column. It can be set to [code]0[/code] to restore the default behavior. Columns that have the "Expand" flag will use their "min_width" in a similar fashion to [member Control.size_flags_stretch_ratio]. </description> </method> <method name="set_column_expand"> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index acf900ae55..eef35049e5 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -177,7 +177,8 @@ <method name="parallel"> <return type="Tween" /> <description> - Makes the next [Tweener] run parallelly to the previous one. Example: + Makes the next [Tweener] run parallelly to the previous one. + [b]Example:[/b] [codeblocks] [gdscript] var tween = create_tween() @@ -272,7 +273,7 @@ <param index="0" name="callback" type="Callable" /> <description> Creates and appends a [CallbackTweener]. This method can be used to call an arbitrary method in any object. Use [method Callable.bind] to bind additional arguments for the call. - Example: object that keeps shooting every 1 second. + [b]Example:[/b] Object that keeps shooting every 1 second: [codeblocks] [gdscript] var tween = get_tree().create_tween().set_loops() @@ -283,7 +284,7 @@ tween.TweenCallback(new Callable(Shoot)).SetDelay(1.0f); [/csharp] [/codeblocks] - Example: turning a sprite red and then blue, with 2 second delay. + [b]Example:[/b] Turning a sprite red and then blue, with 2 second delay: [codeblocks] [gdscript] var tween = get_tree().create_tween() @@ -304,7 +305,7 @@ <param index="0" name="time" type="float" /> <description> Creates and appends an [IntervalTweener]. This method can be used to create delays in the tween animation, as an alternative to using the delay in other [Tweener]s, or when there's no animation (in which case the [Tween] acts as a timer). [param time] is the length of the interval, in seconds. - Example: creating an interval in code execution. + [b]Example:[/b] Creating an interval in code execution: [codeblocks] [gdscript] # ... some code @@ -317,7 +318,7 @@ // ... more code [/csharp] [/codeblocks] - Example: creating an object that moves back and forth and jumps every few seconds. + [b]Example:[/b] Creating an object that moves back and forth and jumps every few seconds: [codeblocks] [gdscript] var tween = create_tween().set_loops() @@ -348,7 +349,7 @@ <param index="3" name="duration" type="float" /> <description> Creates and appends a [MethodTweener]. This method is similar to a combination of [method tween_callback] and [method tween_property]. It calls a method over time with a tweened value provided as an argument. The value is tweened between [param from] and [param to] over the time specified by [param duration], in seconds. Use [method Callable.bind] to bind additional arguments for the call. You can use [method MethodTweener.set_ease] and [method MethodTweener.set_trans] to tweak the easing and transition of the value or [method MethodTweener.set_delay] to delay the tweening. - Example: making a 3D object look from one point to another point. + [b]Example:[/b] Making a 3D object look from one point to another point: [codeblocks] [gdscript] var tween = create_tween() @@ -359,7 +360,7 @@ tween.TweenMethod(new Callable(() => LookAt(Vector3.Up)), new Vector3(-1.0f, 0.0f, -1.0f), new Vector3(1.0f, 0.0f, -1.0f), 1.0f); // The LookAt() method takes up vector as second argument. [/csharp] [/codeblocks] - Example: setting a text of a [Label], using an intermediate method and after a delay. + [b]Example:[/b] Setting the text of a [Label], using an intermediate method and after a delay: [codeblocks] [gdscript] func _ready(): @@ -393,7 +394,8 @@ <param index="2" name="final_val" type="Variant" /> <param index="3" name="duration" type="float" /> <description> - Creates and appends a [PropertyTweener]. This method tweens a [param property] of an [param object] between an initial value and [param final_val] in a span of time equal to [param duration], in seconds. The initial value by default is the property's value at the time the tweening of the [PropertyTweener] starts. For example: + Creates and appends a [PropertyTweener]. This method tweens a [param property] of an [param object] between an initial value and [param final_val] in a span of time equal to [param duration], in seconds. The initial value by default is the property's value at the time the tweening of the [PropertyTweener] starts. + [b]Example:[/b] [codeblocks] [gdscript] var tween = create_tween() @@ -408,7 +410,7 @@ [/codeblocks] will move the sprite to position (100, 200) and then to (200, 300). If you use [method PropertyTweener.from] or [method PropertyTweener.from_current], the starting position will be overwritten by the given value instead. See other methods in [PropertyTweener] to see how the tweening can be tweaked further. [b]Note:[/b] You can find the correct property name by hovering over the property in the Inspector. You can also provide the components of a property directly by using [code]"property:component"[/code] (eg. [code]position:x[/code]), where it would only apply to that particular component. - Example: moving object twice from the same position, with different transition types. + [b]Example:[/b] Moving an object twice from the same position, with different transition types: [codeblocks] [gdscript] var tween = create_tween() diff --git a/doc/classes/VisualShaderNodeInput.xml b/doc/classes/VisualShaderNodeInput.xml index 6268a3fe09..2ebffcfe76 100644 --- a/doc/classes/VisualShaderNodeInput.xml +++ b/doc/classes/VisualShaderNodeInput.xml @@ -19,7 +19,7 @@ </methods> <members> <member name="input_name" type="String" setter="set_input_name" getter="get_input_name" default=""[None]""> - One of the several input constants in lower-case style like: "vertex"([code]VERTEX[/code]) or "point_size"([code]POINT_SIZE[/code]). + One of the several input constants in lower-case style like: "vertex" ([code]VERTEX[/code]) or "point_size" ([code]POINT_SIZE[/code]). </member> </members> <signals> diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml index 3e48b8284a..cc483dbd02 100644 --- a/doc/classes/XRInterface.xml +++ b/doc/classes/XRInterface.xml @@ -32,7 +32,7 @@ <method name="get_play_area" qualifiers="const"> <return type="PackedVector3Array" /> <description> - Returns an array of vectors that denotes the physical play area mapped to the virtual space around the [XROrigin3D] point. The points form a convex polygon that can be used to react to or visualise the play area. This returns an empty array if this feature is not supported or if the information is not yet available. + Returns an array of vectors that denotes the physical play area mapped to the virtual space around the [XROrigin3D] point. The points form a convex polygon that can be used to react to or visualize the play area. This returns an empty array if this feature is not supported or if the information is not yet available. </description> </method> <method name="get_render_target_size"> @@ -66,7 +66,7 @@ <method name="is_initialized" qualifiers="const"> <return type="bool" /> <description> - Is [code]true[/code] if this interface has been initialised. + Is [code]true[/code] if this interface has been initialized. </description> </method> <method name="is_passthrough_enabled"> diff --git a/doc/classes/XRInterfaceExtension.xml b/doc/classes/XRInterfaceExtension.xml index 5958999037..c6b215daa3 100644 --- a/doc/classes/XRInterfaceExtension.xml +++ b/doc/classes/XRInterfaceExtension.xml @@ -138,7 +138,7 @@ <method name="_is_initialized" qualifiers="virtual const"> <return type="bool" /> <description> - Returns [code]true[/code] if this interface has been initialised. + Returns [code]true[/code] if this interface has been initialized. </description> </method> <method name="_notification" qualifiers="virtual"> diff --git a/doc/classes/int.xml b/doc/classes/int.xml index 689cb7fe8e..65ab5d4656 100644 --- a/doc/classes/int.xml +++ b/doc/classes/int.xml @@ -206,13 +206,6 @@ </description> </operator> <operator name="operator +"> - <return type="String" /> - <param index="0" name="right" type="String" /> - <description> - Adds Unicode character with code [int] to the [String]. - </description> - </operator> - <operator name="operator +"> <return type="float" /> <param index="0" name="right" type="float" /> <description> diff --git a/drivers/alsamidi/midi_driver_alsamidi.cpp b/drivers/alsamidi/midi_driver_alsamidi.cpp index 011758e5f7..61fecccb6b 100644 --- a/drivers/alsamidi/midi_driver_alsamidi.cpp +++ b/drivers/alsamidi/midi_driver_alsamidi.cpp @@ -43,7 +43,7 @@ MIDIDriverALSAMidi::MessageCategory MIDIDriverALSAMidi::msg_category(uint8_t msg } else if (msg_part >= 0xf0) { // System Exclusive begin/end are specified as System Common Category messages, // but we separate them here and give them their own categories as their - // behaviour is significantly different. + // behavior is significantly different. if (msg_part == 0xf0) { return MessageCategory::SysExBegin; } else if (msg_part == 0xf7) { diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 6e9d4e5963..c9492adb20 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -595,6 +595,7 @@ void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, cons sky->reflection_dirty = true; } + glBindBufferBase(GL_UNIFORM_BUFFER, SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, sky_globals.directional_light_buffer); if (shader_data->uses_light) { sky_globals.directional_light_count = 0; for (int i = 0; i < (int)p_lights.size(); i++) { @@ -678,7 +679,6 @@ void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, cons } if (light_data_dirty) { - glBindBufferBase(GL_UNIFORM_BUFFER, SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, sky_globals.directional_light_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(DirectionalLightData) * sky_globals.max_directional_lights, sky_globals.directional_lights, GL_STREAM_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index f5b4a8cb3c..3ab7642357 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -75,10 +75,10 @@ private: CharString general_defines; // A version is a high-level construct which is a combination of built-in and user-defined shader code, Each user-created Shader makes one version - // Variants use #ifdefs to toggle behaviour on and off to change behaviour of the shader + // Variants use #ifdefs to toggle behavior on and off to change behavior of the shader // All variants are compiled each time a new version is created - // Specializations use #ifdefs to toggle behaviour on and off for performance, on supporting hardware, they will compile a version with everything enabled, and then compile more copies to improve performance - // Use specializations to enable and disabled advanced features, use variants to toggle behaviour when different data may be used (e.g. using a samplerArray vs a sampler, or doing a depth prepass vs a color pass) + // Specializations use #ifdefs to toggle behavior on and off for performance, on supporting hardware, they will compile a version with everything enabled, and then compile more copies to improve performance + // Use specializations to enable and disabled advanced features, use variants to toggle behavior when different data may be used (e.g. using a samplerArray vs a sampler, or doing a depth prepass vs a color pass) struct Version { Vector<StringName> texture_uniforms; CharString uniforms; diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 5df123878e..464ab474e1 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -1336,7 +1336,7 @@ Error VulkanContext::_create_device() { vulkan11features.shaderDrawParameters = 0; nextptr = &vulkan11features; } else { - // On Vulkan 1.0 and 1.1 we use our older structs to initialise these features. + // On Vulkan 1.0 and 1.1 we use our older structs to initialize these features. storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; storage_feature.pNext = nextptr; storage_feature.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; @@ -1483,7 +1483,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { color_space = surfFormats[0].colorSpace; } else { // These should be ordered with the ones we want to use on top and fallback modes further down - // we want a 32bit RGBA unsigned normalised buffer or similar. + // we want a 32bit RGBA unsigned normalized buffer or similar. const VkFormat allowed_formats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index de7f5c8b88..ed2044c117 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -684,6 +684,7 @@ void ConnectionsDock::_connect(ConnectDialog::ConnectionData p_cd) { } Callable callable = p_cd.get_callable(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat(TTR("Connect '%s' to '%s'"), String(p_cd.signal), String(p_cd.method))); undo_redo->add_do_method(source, "connect", p_cd.signal, callable, p_cd.flags); undo_redo->add_undo_method(source, "disconnect", p_cd.signal, callable); @@ -704,6 +705,7 @@ void ConnectionsDock::_disconnect(TreeItem &p_item) { ERR_FAIL_COND(cd.source != selected_node); // Shouldn't happen but... Bugcheck. + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), cd.signal, cd.method)); Callable callable = cd.get_callable(); @@ -730,6 +732,7 @@ void ConnectionsDock::_disconnect_all() { TreeItem *child = item->get_first_child(); String signal_name = item->get_metadata(0).operator Dictionary()["name"]; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat(TTR("Disconnect all from signal: '%s'"), signal_name)); while (child) { @@ -991,10 +994,6 @@ void ConnectionsDock::_bind_methods() { ClassDB::bind_method("update_tree", &ConnectionsDock::update_tree); } -void ConnectionsDock::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { - undo_redo = p_undo_redo; -} - void ConnectionsDock::set_node(Node *p_node) { selected_node = p_node; update_tree(); diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 126a0ca828..fcfc501386 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -47,7 +47,6 @@ #include "scene/gui/tree.h" class ConnectDialogBinds; -class EditorUndoRedoManager; class ConnectDialog : public ConfirmationDialog { GDCLASS(ConnectDialog, ConfirmationDialog); @@ -197,7 +196,6 @@ class ConnectionsDock : public VBoxContainer { Button *connect_button = nullptr; PopupMenu *signal_menu = nullptr; PopupMenu *slot_menu = nullptr; - Ref<EditorUndoRedoManager> undo_redo; LineEdit *search_box = nullptr; HashMap<StringName, HashMap<StringName, String>> descr_cache; @@ -231,7 +229,6 @@ protected: static void _bind_methods(); public: - void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void set_node(Node *p_node); void update_tree(); diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 46df618d57..24116a8053 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -338,7 +338,7 @@ float EditorAudioBus::_normalized_volume_to_scaled_db(float normalized) { * values to relative decibal values. * One formula is an exponential graph which intends to counteract * the logarithmic nature of human hearing. This is an approximation - * of the behaviour of a 'logarithmic potentiometer' found on most + * of the behavior of a 'logarithmic potentiometer' found on most * musical instruments and also emulated in popular software. * The other two equations are hand-tuned linear tapers that intend to * try to ease the exponential equation in areas where it makes sense.*/ diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index 90310bb43c..598bcdc639 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -194,7 +194,7 @@ void EditorAutoloadSettings::_autoload_edited() { TreeItem *ti = tree->get_edited(); int column = tree->get_edited_column(); - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); if (column == 0) { String name = ti->get_text(0); @@ -289,7 +289,7 @@ void EditorAutoloadSettings::_autoload_button_pressed(Object *p_item, int p_colu String name = "autoload/" + ti->get_text(0); - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); switch (p_button) { case BUTTON_OPEN: { @@ -714,7 +714,7 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant & orders.sort(); - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Rearrange Autoloads")); @@ -757,7 +757,7 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_ name = "autoload/" + name; - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add Autoload")); // Singleton autoloads are represented with a leading "*" in their path. @@ -783,7 +783,7 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_ void EditorAutoloadSettings::autoload_remove(const String &p_name) { String name = "autoload/" + p_name; - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); int order = ProjectSettings::get_singleton()->get_order(name); diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 6d8f0df452..a3dd19bb67 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -457,12 +457,10 @@ Callable EditorData::get_move_array_element_function(const StringName &p_class) } void EditorData::remove_editor_plugin(EditorPlugin *p_plugin) { - p_plugin->undo_redo = Ref<EditorUndoRedoManager>(); editor_plugins.erase(p_plugin); } void EditorData::add_editor_plugin(EditorPlugin *p_plugin) { - p_plugin->undo_redo = undo_redo_manager; editor_plugins.push_back(p_plugin); } diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index 90f3f8d2f5..51ebc31df3 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -57,6 +57,24 @@ Ref<FontFile> load_external_font(const String &p_path, TextServer::Hinting p_hin return font; } +Ref<SystemFont> load_system_font(const PackedStringArray &p_names, TextServer::Hinting p_hinting, TextServer::FontAntialiasing p_aa, bool p_autohint, TextServer::SubpixelPositioning p_font_subpixel_positioning, bool p_msdf = false, TypedArray<Font> *r_fallbacks = nullptr) { + Ref<SystemFont> font; + font.instantiate(); + + font->set_font_names(p_names); + font->set_multichannel_signed_distance_field(p_msdf); + font->set_antialiasing(p_aa); + font->set_hinting(p_hinting); + font->set_force_autohinter(p_autohint); + font->set_subpixel_positioning(p_font_subpixel_positioning); + + if (r_fallbacks != nullptr) { + r_fallbacks->push_back(font); + } + + return font; +} + Ref<FontFile> load_internal_font(const uint8_t *p_data, size_t p_size, TextServer::Hinting p_hinting, TextServer::FontAntialiasing p_aa, bool p_autohint, TextServer::SubpixelPositioning p_font_subpixel_positioning, bool p_msdf = false, TypedArray<Font> *r_fallbacks = nullptr) { Ref<FontFile> font; font.instantiate(); @@ -166,6 +184,20 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<FontFile> thai_font_bold = load_internal_font(_font_NotoSansThaiUI_Bold, _font_NotoSansThaiUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); Ref<FontVariation> fallback_font_bold = make_bold_font(fallback_font, embolden_strength, &fallbacks_bold); Ref<FontVariation> japanese_font_bold = make_bold_font(japanese_font, embolden_strength, &fallbacks_bold); + + if (OS::get_singleton()->has_feature("system_fonts")) { + PackedStringArray emoji_font_names; + emoji_font_names.push_back("Apple Color Emoji"); + emoji_font_names.push_back("Segoe UI Emoji"); + emoji_font_names.push_back("Noto Color Emoji"); + emoji_font_names.push_back("Twitter Color Emoji"); + emoji_font_names.push_back("OpenMoji"); + emoji_font_names.push_back("EmojiOne Color"); + Ref<SystemFont> emoji_font = load_system_font(emoji_font_names, font_hinting, font_antialiasing, true, font_subpixel_positioning, false); + fallbacks.push_back(emoji_font); + fallbacks_bold.push_back(emoji_font); + } + default_font_bold->set_fallbacks(fallbacks_bold); default_font_bold_msdf->set_fallbacks(fallbacks_bold); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index ddb9bb30f1..89398409a2 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -375,7 +375,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview if (qualifier == "vararg") { hint = TTR("This method supports a variable number of arguments."); } else if (qualifier == "virtual") { - hint = TTR("This method is called by the engine.\nIt can be overriden to customise built-in behavior."); + hint = TTR("This method is called by the engine.\nIt can be overridden to customize built-in behavior."); } else if (qualifier == "const") { hint = TTR("This method has no side effects.\nIt does not modify the object in any way."); } else if (qualifier == "static") { diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 94dd4d0606..6e18bde303 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -39,6 +39,7 @@ #include "editor/editor_property_name_processor.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/script_editor_plugin.h" #include "multi_node_edit.h" #include "scene/gui/center_container.h" @@ -1698,6 +1699,7 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) { } else { action_name = vformat("Move element %d to position %d in property array with prefix %s.", p_element_index, p_to_pos, array_element_prefix); } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(action_name); if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) { // Call the function. @@ -1841,6 +1843,7 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) { } void EditorInspectorArray::_clear_array() { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat("Clear property array with prefix %s.", array_element_prefix)); if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) { for (int i = count - 1; i >= 0; i--) { @@ -1893,6 +1896,7 @@ void EditorInspectorArray::_resize_array(int p_size) { return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat("Resize property array with prefix %s.", array_element_prefix)); if (p_size > count) { if (mode == MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION) { @@ -2242,10 +2246,6 @@ void EditorInspectorArray::_bind_methods() { ADD_SIGNAL(MethodInfo("page_change_request")); } -void EditorInspectorArray::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { - undo_redo = p_undo_redo; -} - void EditorInspectorArray::setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable, bool p_numbered, int p_page_length, const String &p_add_item_text) { count_property = ""; mode = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION; @@ -2508,10 +2508,6 @@ Button *EditorInspector::create_inspector_action_button(const String &p_text) { return button; } -void EditorInspector::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { - undo_redo = p_undo_redo; -} - String EditorInspector::get_selected_path() const { return property_selected; } @@ -3077,7 +3073,6 @@ void EditorInspector::update_tree() { int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0; editor_inspector_array->setup_with_move_element_function(object, array_label, array_element_prefix, page, c, use_folding); editor_inspector_array->connect("page_change_request", callable_mp(this, &EditorInspector::_page_change_request).bind(array_element_prefix)); - editor_inspector_array->set_undo_redo(undo_redo); } else if (p.type == Variant::INT) { // Setup the array to use the count property and built-in functions to create/move/delete elements. if (class_name_components.size() >= 2) { @@ -3087,8 +3082,6 @@ void EditorInspector::update_tree() { editor_inspector_array->setup_with_count_property(object, class_name_components[0], p.name, array_element_prefix, page, c, foldable, movable, numbered, page_size, add_button_text, swap_method); editor_inspector_array->connect("page_change_request", callable_mp(this, &EditorInspector::_page_change_request).bind(array_element_prefix)); - - editor_inspector_array->set_undo_redo(undo_redo); } } @@ -3565,6 +3558,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo } } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); if (!undo_redo.is_valid() || bool(object->call("_dont_undo_redo"))) { object->set(p_name, p_value); if (p_refresh_all) { @@ -3685,6 +3679,7 @@ void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array } names += p_paths[i]; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Set Multiple:") + " " + names, UndoRedo::MERGE_ENDS); for (int i = 0; i < p_paths.size(); i++) { _edit_set(p_paths[i], p_values[i], false, ""); @@ -3719,6 +3714,7 @@ void EditorInspector::_property_deleted(const String &p_path) { if (p_path.begins_with("metadata/")) { String name = p_path.replace_first("metadata/", ""); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat(TTR("Remove metadata %s"), name)); undo_redo->add_do_method(object, "remove_meta", name); undo_redo->add_undo_method(object, "set_meta", name, object->get_meta(name)); @@ -3784,6 +3780,7 @@ void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) { Node *node = Object::cast_to<Node>(object); ERR_FAIL_COND(!node); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); if (undo_redo.is_valid()) { undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path)); undo_redo->add_do_method(node, "_set_property_pinned", p_path, p_pinned); @@ -3981,6 +3978,7 @@ void EditorInspector::_add_meta_confirm() { Variant defval; Callable::CallError ce; Variant::construct(Variant::Type(add_meta_type->get_selected_id()), defval, nullptr, 0, ce); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat(TTR("Add metadata %s"), name)); undo_redo->add_do_method(object, "set_meta", name, defval); undo_redo->add_undo_method(object, "remove_meta", name); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index bada02e254..1b93b19845 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -31,7 +31,6 @@ #ifndef EDITOR_INSPECTOR_H #define EDITOR_INSPECTOR_H -#include "editor/editor_undo_redo_manager.h" #include "editor_property_name_processor.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" @@ -305,8 +304,6 @@ public: class EditorInspectorArray : public EditorInspectorSection { GDCLASS(EditorInspectorArray, EditorInspectorSection); - Ref<EditorUndoRedoManager> undo_redo; - enum Mode { MODE_NONE, MODE_USE_COUNT_PROPERTY, @@ -401,8 +398,6 @@ protected: static void _bind_methods(); public: - void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); - void setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = ""); void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = ""); VBoxContainer *get_vbox(int p_index); @@ -441,7 +436,6 @@ public: class EditorInspector : public ScrollContainer { GDCLASS(EditorInspector, ScrollContainer); - Ref<EditorUndoRedoManager> undo_redo; enum { MAX_PLUGINS = 1024 }; @@ -555,8 +549,6 @@ public: static EditorProperty *instantiate_property_editor(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide = false); - void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); - String get_selected_path() const; void update_tree(); diff --git a/editor/editor_locale_dialog.cpp b/editor/editor_locale_dialog.cpp index 106cc61947..a913fb2fd9 100644 --- a/editor/editor_locale_dialog.cpp +++ b/editor/editor_locale_dialog.cpp @@ -141,6 +141,7 @@ void EditorLocaleDialog::_filter_lang_option_changed() { f_lang_all.sort(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Changed Locale Language Filter")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", f_lang_all); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", prev); @@ -174,6 +175,7 @@ void EditorLocaleDialog::_filter_script_option_changed() { f_script_all.sort(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Changed Locale Script Filter")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", f_script_all); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", prev); @@ -207,6 +209,7 @@ void EditorLocaleDialog::_filter_cnt_option_changed() { f_cnt_all.sort(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Changed Locale Country Filter")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", f_cnt_all); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", prev); @@ -221,6 +224,7 @@ void EditorLocaleDialog::_filter_mode_changed(int p_mode) { prev = GLOBAL_GET("internationalization/locale/locale_filter_mode"); } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Changed Locale Filter Mode")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", f_mode); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", prev); @@ -385,8 +389,6 @@ void EditorLocaleDialog::popup_locale_dialog() { } EditorLocaleDialog::EditorLocaleDialog() { - undo_redo = EditorNode::get_undo_redo(); - set_title(TTR("Select a Locale")); VBoxContainer *vb = memnew(VBoxContainer); diff --git a/editor/editor_locale_dialog.h b/editor/editor_locale_dialog.h index 8ac642a038..48f9edd4b0 100644 --- a/editor/editor_locale_dialog.h +++ b/editor/editor_locale_dialog.h @@ -40,7 +40,6 @@ class VBoxContainer; class LineEdit; class Tree; class OptionButton; -class EditorUndoRedoManager; class EditorLocaleDialog : public ConfirmationDialog { GDCLASS(EditorLocaleDialog, ConfirmationDialog); @@ -63,8 +62,6 @@ class EditorLocaleDialog : public ConfirmationDialog { Tree *script_list = nullptr; Tree *cnt_list = nullptr; - Ref<EditorUndoRedoManager> undo_redo; - bool locale_set = false; bool updating_lists = false; diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 0f2543f708..84284a7f31 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -30,6 +30,7 @@ #include "editor_log.h" +#include "core/object/undo_redo.h" #include "core/os/keyboard.h" #include "core/version.h" #include "editor/editor_node.h" @@ -226,7 +227,7 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) { // Make text split by new lines their own message. // See #41321 for reasoning. At time of writing, multiple print()'s in running projects // get grouped together and sent to the editor log as one message. This can mess with the - // search functionality (see the comments on the PR above for more details). This behaviour + // search functionality (see the comments on the PR above for more details). This behavior // also matches that of other IDE's. Vector<String> lines = p_msg.split("\n", true); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index be1f0be279..716e0b454f 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -110,6 +110,7 @@ #include "editor/export/export_template_manager.h" #include "editor/export/project_export.h" #include "editor/filesystem_dock.h" +#include "editor/history_dock.h" #include "editor/import/audio_stream_import_settings.h" #include "editor/import/dynamic_font_import_settings.h" #include "editor/import/editor_import_collada.h" @@ -3697,6 +3698,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { EditorDebuggerNode::get_singleton()->update_live_edit_root(); ScriptEditor::get_singleton()->set_scene_root_script(editor_data.get_scene_root_script(editor_data.get_edited_scene())); editor_data.notify_edited_scene_changed(); + emit_signal(SNAME("scene_changed")); } bool EditorNode::is_changing_scene() const { @@ -6040,6 +6042,7 @@ void EditorNode::_bind_methods() { ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj"))); ADD_SIGNAL(MethodInfo("scene_saved", PropertyInfo(Variant::STRING, "path"))); ADD_SIGNAL(MethodInfo("project_settings_changed")); + ADD_SIGNAL(MethodInfo("scene_changed")); } static Node *_resource_get_edited_scene() { @@ -7105,6 +7108,8 @@ EditorNode::EditorNode() { filesystem_dock->connect("display_mode_changed", callable_mp(this, &EditorNode::_save_docks)); get_project_settings()->connect_filesystem_dock_signals(filesystem_dock); + HistoryDock *hd = memnew(HistoryDock); + // Scene: Top left. dock_slot[DOCK_SLOT_LEFT_UR]->add_child(SceneTreeDock::get_singleton()); dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(dock_slot[DOCK_SLOT_LEFT_UR]->get_tab_idx_from_control(SceneTreeDock::get_singleton()), TTR("Scene")); @@ -7125,6 +7130,10 @@ EditorNode::EditorNode() { dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(NodeDock::get_singleton()); dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(dock_slot[DOCK_SLOT_RIGHT_UL]->get_tab_idx_from_control(NodeDock::get_singleton()), TTR("Node")); + // History: Full height right, behind Node. + dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(hd); + dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(dock_slot[DOCK_SLOT_RIGHT_UL]->get_tab_idx_from_control(hd), TTR("History")); + // Hide unused dock slots and vsplits. dock_slot[DOCK_SLOT_LEFT_UL]->hide(); dock_slot[DOCK_SLOT_LEFT_BL]->hide(); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index d83dc32e90..8fca21ae7b 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -967,7 +967,7 @@ void EditorPlugin::_bind_methods() { } Ref<EditorUndoRedoManager> EditorPlugin::get_undo_redo() { - return undo_redo; + return EditorNode::get_undo_redo(); } EditorPluginCreateFunc EditorPlugins::creation_funcs[MAX_CREATE_FUNCS]; diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index d8c3cc7330..753ccedf70 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -32,7 +32,6 @@ #define EDITOR_PLUGIN_H #include "core/io/config_file.h" -#include "core/object/undo_redo.h" #include "editor/debugger/editor_debugger_node.h" #include "editor/editor_inspector.h" #include "editor/editor_translation_parser.h" @@ -134,7 +133,6 @@ public: class EditorPlugin : public Node { GDCLASS(EditorPlugin, Node); friend class EditorData; - Ref<EditorUndoRedoManager> undo_redo; bool input_event_forwarding_always_enabled = false; bool force_draw_over_forwarding_enabled = false; diff --git a/editor/editor_plugin_settings.h b/editor/editor_plugin_settings.h index 9c619066f2..829135b544 100644 --- a/editor/editor_plugin_settings.h +++ b/editor/editor_plugin_settings.h @@ -31,7 +31,6 @@ #ifndef EDITOR_PLUGIN_SETTINGS_H #define EDITOR_PLUGIN_SETTINGS_H -#include "core/object/undo_redo.h" #include "editor/editor_data.h" #include "editor/plugin_config_dialog.h" diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 84caed9db5..115fda6ca0 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2688,7 +2688,7 @@ void EditorPropertyQuaternion::_custom_value_changed(double val) { v.y = Math::deg_to_rad(edit_euler.y); v.z = Math::deg_to_rad(edit_euler.z); - Quaternion temp_q = Quaternion(v); + Quaternion temp_q = Quaternion::from_euler(v); spin[0]->set_value(temp_q.x); spin[1]->set_value(temp_q.y); spin[2]->set_value(temp_q.z); @@ -4097,7 +4097,6 @@ void EditorPropertyResource::update_property() { sub_inspector->set_keying(is_keying()); sub_inspector->set_read_only(is_read_only()); sub_inspector->set_use_folding(is_using_folding()); - sub_inspector->set_undo_redo(EditorNode::get_undo_redo()); sub_inspector_vbox = memnew(VBoxContainer); add_child(sub_inspector_vbox); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index bec05a0cd6..659221ee0b 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -466,7 +466,7 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d Variant array = object->get_array(); - // Handle the case where array is not initialised yet. + // Handle the case where array is not initialized yet. if (!array.is_array()) { Callable::CallError ce; Variant::construct(array_type, array, nullptr, 0, ce); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 409ef95fb0..11b8f224f5 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -159,7 +159,7 @@ bool EditorSettings::_get(const StringName &p_name, Variant &r_ret) const { if (!sc->has_meta("original")) { // Getting the meta when it doesn't exist will return an empty array. If the 'shortcut_events' have been cleared, - // we still want save the shortcut in this case so that shortcuts that the user has customised are not reset, + // we still want save the shortcut in this case so that shortcuts that the user has customized are not reset, // even if the 'original' has not been populated yet. This can happen when calling save() from the Project Manager. save_array.push_back(dict); continue; diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index db501aac03..a72c545b2f 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -132,6 +132,7 @@ void EditorSettingsDialog::_notification(int p_what) { } break; case NOTIFICATION_READY: { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_method_notify_callback(EditorDebuggerNode::_method_changeds, nullptr); undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_property_notify_callback(EditorDebuggerNode::_property_changeds, nullptr); undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_commit_notify_callback(_undo_redo_callback, this); @@ -158,9 +159,9 @@ void EditorSettingsDialog::_notification(int p_what) { void EditorSettingsDialog::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); const Ref<InputEventKey> k = p_event; - if (k.is_valid() && k->is_pressed()) { bool handled = false; @@ -229,6 +230,7 @@ void EditorSettingsDialog::_event_config_confirmed() { void EditorSettingsDialog::_update_builtin_action(const String &p_name, const Array &p_events) { Array old_input_array = EditorSettings::get_singleton()->get_builtin_action_overrides(p_name); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Edit Built-in Action") + " '" + p_name + "'"); undo_redo->add_do_method(EditorSettings::get_singleton(), "mark_setting_changed", "builtin_action_overrides"); undo_redo->add_undo_method(EditorSettings::get_singleton(), "mark_setting_changed", "builtin_action_overrides"); @@ -244,6 +246,7 @@ void EditorSettingsDialog::_update_builtin_action(const String &p_name, const Ar void EditorSettingsDialog::_update_shortcut_events(const String &p_path, const Array &p_events) { Ref<Shortcut> current_sc = EditorSettings::get_singleton()->get_shortcut(p_path); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Edit Shortcut") + " '" + p_path + "'"); undo_redo->add_do_method(current_sc.ptr(), "set_events", p_events); undo_redo->add_undo_method(current_sc.ptr(), "set_events", current_sc->get_events()); @@ -697,8 +700,6 @@ void EditorSettingsDialog::_bind_methods() { EditorSettingsDialog::EditorSettingsDialog() { set_title(TTR("Editor Settings")); - undo_redo = EditorNode::get_undo_redo(); - tabs = memnew(TabContainer); tabs->set_theme_type_variation("TabContainerOdd"); tabs->connect("tab_changed", callable_mp(this, &EditorSettingsDialog::_tabs_tab_changed)); @@ -723,7 +724,6 @@ EditorSettingsDialog::EditorSettingsDialog() { inspector->get_inspector()->set_use_filter(true); inspector->register_search_box(search_box); inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL); - inspector->get_inspector()->set_undo_redo(undo_redo); tab_general->add_child(inspector); inspector->get_inspector()->connect("property_edited", callable_mp(this, &EditorSettingsDialog::_settings_property_edited)); inspector->get_inspector()->connect("restart_requested", callable_mp(this, &EditorSettingsDialog::_editor_restart_request)); diff --git a/editor/editor_settings_dialog.h b/editor/editor_settings_dialog.h index 05424a64ed..a330de1e2c 100644 --- a/editor/editor_settings_dialog.h +++ b/editor/editor_settings_dialog.h @@ -40,8 +40,6 @@ #include "scene/gui/tab_container.h" #include "scene/gui/texture_rect.h" -class EditorUndoRedoManager; - class EditorSettingsDialog : public AcceptDialog { GDCLASS(EditorSettingsDialog, AcceptDialog); @@ -75,8 +73,6 @@ class EditorSettingsDialog : public AcceptDialog { Timer *timer = nullptr; - Ref<EditorUndoRedoManager> undo_redo; - virtual void cancel_pressed() override; virtual void ok_pressed() override; diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp index 064448fd96..09b567fc68 100644 --- a/editor/editor_undo_redo_manager.cpp +++ b/editor/editor_undo_redo_manager.cpp @@ -241,6 +241,7 @@ void EditorUndoRedoManager::commit_action(bool p_execute) { history.undo_stack.push_back(pending_action); pending_action = Action(); is_committing = false; + emit_signal(SNAME("history_changed")); } bool EditorUndoRedoManager::is_committing_action() const { @@ -252,14 +253,14 @@ bool EditorUndoRedoManager::undo() { return false; } - History *selected_history = nullptr; + int selected_history = INVALID_HISTORY; double global_timestamp = 0; // Pick the history with greatest last action timestamp (either global or current scene). { History &history = get_or_create_history(GLOBAL_HISTORY); if (!history.undo_stack.is_empty()) { - selected_history = &history; + selected_history = history.id; global_timestamp = history.undo_stack.back()->get().timestamp; } } @@ -267,32 +268,44 @@ bool EditorUndoRedoManager::undo() { { History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) { - selected_history = &history; + selected_history = history.id; } } - if (selected_history) { - Action action = selected_history->undo_stack.back()->get(); - selected_history->undo_stack.pop_back(); - selected_history->redo_stack.push_back(action); - return selected_history->undo_redo->undo(); + if (selected_history != INVALID_HISTORY) { + return undo_history(selected_history); } return false; } +bool EditorUndoRedoManager::undo_history(int p_id) { + ERR_FAIL_COND_V(p_id == INVALID_HISTORY, false); + History &history = get_or_create_history(p_id); + + Action action = history.undo_stack.back()->get(); + history.undo_stack.pop_back(); + history.redo_stack.push_back(action); + + bool success = history.undo_redo->undo(); + if (success) { + emit_signal(SNAME("version_changed")); + } + return success; +} + bool EditorUndoRedoManager::redo() { if (!has_redo()) { return false; } - History *selected_history = nullptr; + int selected_history = INVALID_HISTORY; double global_timestamp = INFINITY; // Pick the history with lowest last action timestamp (either global or current scene). { History &history = get_or_create_history(GLOBAL_HISTORY); if (!history.redo_stack.is_empty()) { - selected_history = &history; + selected_history = history.id; global_timestamp = history.redo_stack.back()->get().timestamp; } } @@ -300,19 +313,31 @@ bool EditorUndoRedoManager::redo() { { History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); if (!history.redo_stack.is_empty() && history.redo_stack.back()->get().timestamp < global_timestamp) { - selected_history = &history; + selected_history = history.id; } } - if (selected_history) { - Action action = selected_history->redo_stack.back()->get(); - selected_history->redo_stack.pop_back(); - selected_history->undo_stack.push_back(action); - return selected_history->undo_redo->redo(); + if (selected_history != INVALID_HISTORY) { + return redo_history(selected_history); } return false; } +bool EditorUndoRedoManager::redo_history(int p_id) { + ERR_FAIL_COND_V(p_id == INVALID_HISTORY, false); + History &history = get_or_create_history(p_id); + + Action action = history.redo_stack.back()->get(); + history.redo_stack.pop_back(); + history.undo_stack.push_back(action); + + bool success = history.undo_redo->redo(); + if (success) { + emit_signal(SNAME("version_changed")); + } + return success; +} + void EditorUndoRedoManager::set_history_as_saved(int p_id) { History &history = get_or_create_history(p_id); history.saved_version = history.undo_redo->get_version(); @@ -352,6 +377,7 @@ void EditorUndoRedoManager::clear_history(bool p_increase_version, int p_idx) { if (!p_increase_version) { set_history_as_saved(p_idx); } + emit_signal(SNAME("history_changed")); return; } @@ -359,6 +385,7 @@ void EditorUndoRedoManager::clear_history(bool p_increase_version, int p_idx) { E.value.undo_redo->clear_history(p_increase_version); set_history_as_saved(E.key); } + emit_signal(SNAME("history_changed")); } String EditorUndoRedoManager::get_current_action_name() { @@ -434,6 +461,9 @@ void EditorUndoRedoManager::_bind_methods() { ClassDB::bind_method(D_METHOD("get_object_history_id", "object"), &EditorUndoRedoManager::get_history_id_for_object); ClassDB::bind_method(D_METHOD("get_history_undo_redo", "id"), &EditorUndoRedoManager::get_history_undo_redo); + ADD_SIGNAL(MethodInfo("history_changed")); + ADD_SIGNAL(MethodInfo("version_changed")); + BIND_ENUM_CONSTANT(GLOBAL_HISTORY); BIND_ENUM_CONSTANT(INVALID_HISTORY); } diff --git a/editor/editor_undo_redo_manager.h b/editor/editor_undo_redo_manager.h index c4d85daa22..60bcd059df 100644 --- a/editor/editor_undo_redo_manager.h +++ b/editor/editor_undo_redo_manager.h @@ -44,7 +44,6 @@ public: INVALID_HISTORY = -99, }; -private: struct Action { int history_id = INVALID_HISTORY; double timestamp = 0; @@ -60,6 +59,7 @@ private: List<Action> redo_stack; }; +private: HashMap<int, History> history_map; Action pending_action; @@ -114,7 +114,9 @@ public: bool is_committing_action() const; bool undo(); + bool undo_history(int p_id); bool redo(); + bool redo_history(int p_id); void clear_history(bool p_increase_version = true, int p_idx = INVALID_HISTORY); void set_history_as_saved(int p_idx); diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index f11e328087..ab96caa88c 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -130,6 +130,7 @@ void GroupDialog::_add_pressed() { return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add to Group")); while (selected) { @@ -159,6 +160,7 @@ void GroupDialog::_removed_pressed() { return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove from Group")); while (selected) { @@ -246,6 +248,7 @@ void GroupDialog::_group_renamed() { renamed_group->set_text(0, name); // Spaces trimmed. + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Rename Group")); List<Node *> nodes; @@ -322,6 +325,7 @@ void GroupDialog::_modify_group_pressed(Object *p_item, int p_column, int p_id, case DELETE_GROUP: { String name = ti->get_text(0); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Delete Group")); List<Node *> nodes; @@ -402,10 +406,6 @@ void GroupDialog::_notification(int p_what) { } } -void GroupDialog::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { - undo_redo = p_undo_redo; -} - void GroupDialog::edit() { popup_centered(); @@ -597,6 +597,7 @@ void GroupsEditor::_add_group(const String &p_group) { return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add to Group")); undo_redo->add_do_method(node, "add_to_group", name, true); @@ -651,6 +652,7 @@ void GroupsEditor::_group_renamed() { ti->set_text(0, name); // Spaces trimmed. + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Rename Group")); undo_redo->add_do_method(node, "remove_from_group", selected_group); @@ -686,6 +688,7 @@ void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id, MouseBu switch (p_id) { case DELETE_GROUP: { const String name = ti->get_text(0); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove from Group")); undo_redo->add_do_method(node, "remove_from_group", name); @@ -764,10 +767,6 @@ void GroupsEditor::update_tree() { } } -void GroupsEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { - undo_redo = p_undo_redo; -} - void GroupsEditor::set_current(Node *p_node) { node = p_node; update_tree(); @@ -775,7 +774,6 @@ void GroupsEditor::set_current(Node *p_node) { void GroupsEditor::_show_group_dialog() { group_dialog->edit(); - group_dialog->set_undo_redo(undo_redo); } void GroupsEditor::_bind_methods() { diff --git a/editor/groups_editor.h b/editor/groups_editor.h index 5d012a3501..3900e0e28c 100644 --- a/editor/groups_editor.h +++ b/editor/groups_editor.h @@ -39,8 +39,6 @@ #include "scene/gui/popup.h" #include "scene/gui/tree.h" -class EditorUndoRedoManager; - class GroupDialog : public AcceptDialog { GDCLASS(GroupDialog, AcceptDialog); @@ -69,8 +67,6 @@ class GroupDialog : public AcceptDialog { String selected_group; - Ref<EditorUndoRedoManager> undo_redo; - void _group_selected(); void _remove_filter_changed(const String &p_filter); @@ -102,7 +98,6 @@ public: }; void edit(); - void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); GroupDialog(); }; @@ -120,8 +115,6 @@ class GroupsEditor : public VBoxContainer { Button *add = nullptr; Tree *tree = nullptr; - Ref<EditorUndoRedoManager> undo_redo; - String selected_group; void update_tree(); @@ -143,7 +136,6 @@ public: COPY_GROUP, }; - void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void set_current(Node *p_node); GroupsEditor(); diff --git a/editor/history_dock.cpp b/editor/history_dock.cpp new file mode 100644 index 0000000000..57088a76cb --- /dev/null +++ b/editor/history_dock.cpp @@ -0,0 +1,251 @@ +/*************************************************************************/ +/* history_dock.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "history_dock.h" + +#include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" +#include "scene/gui/check_box.h" +#include "scene/gui/item_list.h" + +struct SortActionsByTimestamp { + bool operator()(const EditorUndoRedoManager::Action &l, const EditorUndoRedoManager::Action &r) const { + return l.timestamp > r.timestamp; + } +}; + +void HistoryDock::on_history_changed() { + if (is_visible_in_tree()) { + refresh_history(); + } else { + need_refresh = true; + } +} + +void HistoryDock::refresh_history() { + action_list->clear(); + bool include_scene = current_scene_checkbox->is_pressed(); + bool include_global = global_history_checkbox->is_pressed(); + + if (!include_scene && !include_global) { + action_list->add_item(TTR("The Beginning")); + return; + } + + const EditorUndoRedoManager::History ¤t_scene_history = ur_manager->get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); + const EditorUndoRedoManager::History &global_history = ur_manager->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY); + + Vector<EditorUndoRedoManager::Action> full_history; + { + int full_size = 0; + if (include_scene) { + full_size += current_scene_history.redo_stack.size() + current_scene_history.undo_stack.size(); + } + if (include_global) { + full_size += global_history.redo_stack.size() + global_history.undo_stack.size(); + } + full_history.resize(full_size); + } + + int i = 0; + if (include_scene) { + for (const EditorUndoRedoManager::Action &E : current_scene_history.redo_stack) { + full_history.write[i] = E; + i++; + } + for (const EditorUndoRedoManager::Action &E : current_scene_history.undo_stack) { + full_history.write[i] = E; + i++; + } + } + if (include_global) { + for (const EditorUndoRedoManager::Action &E : global_history.redo_stack) { + full_history.write[i] = E; + i++; + } + for (const EditorUndoRedoManager::Action &E : global_history.undo_stack) { + full_history.write[i] = E; + i++; + } + } + + full_history.sort_custom<SortActionsByTimestamp>(); + for (const EditorUndoRedoManager::Action &E : full_history) { + action_list->add_item(E.action_name); + if (E.history_id == EditorUndoRedoManager::GLOBAL_HISTORY) { + action_list->set_item_custom_fg_color(-1, get_theme_color(SNAME("accent_color"), SNAME("Editor"))); + } + } + + action_list->add_item(TTR("The Beginning")); + refresh_version(); +} + +void HistoryDock::on_version_changed() { + if (is_visible_in_tree()) { + refresh_version(); + } else { + need_refresh = true; + } +} + +void HistoryDock::refresh_version() { + int idx = 0; + bool include_scene = current_scene_checkbox->is_pressed(); + bool include_global = global_history_checkbox->is_pressed(); + + if (!include_scene && !include_global) { + current_version = idx; + action_list->set_current(idx); + return; + } + + const EditorUndoRedoManager::History ¤t_scene_history = ur_manager->get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); + const EditorUndoRedoManager::History &global_history = ur_manager->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY); + double newest_undo_timestamp = 0; + + if (include_scene && !current_scene_history.undo_stack.is_empty()) { + newest_undo_timestamp = current_scene_history.undo_stack.front()->get().timestamp; + } + + if (include_global && !global_history.undo_stack.is_empty()) { + double global_undo_timestamp = global_history.undo_stack.front()->get().timestamp; + if (global_undo_timestamp > newest_undo_timestamp) { + newest_undo_timestamp = global_undo_timestamp; + } + } + + if (include_scene) { + int skip = 0; + for (const EditorUndoRedoManager::Action &E : current_scene_history.redo_stack) { + if (E.timestamp < newest_undo_timestamp) { + skip++; + } else { + break; + } + } + idx += current_scene_history.redo_stack.size() - skip; + } + + if (include_global) { + int skip = 0; + for (const EditorUndoRedoManager::Action &E : global_history.redo_stack) { + if (E.timestamp < newest_undo_timestamp) { + skip++; + } else { + break; + } + } + idx += global_history.redo_stack.size() - skip; + } + + current_version = idx; + action_list->set_current(idx); +} + +void HistoryDock::seek_history(int p_index) { + bool include_scene = current_scene_checkbox->is_pressed(); + bool include_global = global_history_checkbox->is_pressed(); + + if (!include_scene && !include_global) { + return; + } + int current_scene_id = EditorNode::get_editor_data().get_current_edited_scene_history_id(); + + while (current_version < p_index) { + if (include_scene) { + if (include_global) { + ur_manager->undo(); + } else { + ur_manager->undo_history(current_scene_id); + } + } else { + ur_manager->undo_history(EditorUndoRedoManager::GLOBAL_HISTORY); + } + } + + while (current_version > p_index) { + if (include_scene) { + if (include_global) { + ur_manager->redo(); + } else { + ur_manager->redo_history(current_scene_id); + } + } else { + ur_manager->redo_history(EditorUndoRedoManager::GLOBAL_HISTORY); + } + } +} + +void HistoryDock::_notification(int p_notification) { + switch (p_notification) { + case NOTIFICATION_ENTER_TREE: { + EditorNode::get_singleton()->connect("scene_changed", callable_mp(this, &HistoryDock::on_history_changed)); + } break; + + case NOTIFICATION_VISIBILITY_CHANGED: { + if (is_visible_in_tree() && need_refresh) { + refresh_history(); + } + } break; + } +} + +HistoryDock::HistoryDock() { + ur_manager = EditorNode::get_undo_redo(); + ur_manager->connect("history_changed", callable_mp(this, &HistoryDock::on_history_changed)); + ur_manager->connect("version_changed", callable_mp(this, &HistoryDock::on_version_changed)); + + HBoxContainer *mode_hb = memnew(HBoxContainer); + add_child(mode_hb); + + current_scene_checkbox = memnew(CheckBox); + mode_hb->add_child(current_scene_checkbox); + current_scene_checkbox->set_flat(true); + current_scene_checkbox->set_pressed(true); + current_scene_checkbox->set_text(TTR("Scene")); + current_scene_checkbox->set_h_size_flags(SIZE_EXPAND_FILL); + current_scene_checkbox->set_clip_text(true); + current_scene_checkbox->connect("toggled", callable_mp(this, &HistoryDock::refresh_history).unbind(1)); + + global_history_checkbox = memnew(CheckBox); + mode_hb->add_child(global_history_checkbox); + global_history_checkbox->set_flat(true); + global_history_checkbox->set_pressed(true); + global_history_checkbox->set_text(TTR("Global")); + global_history_checkbox->set_h_size_flags(SIZE_EXPAND_FILL); + global_history_checkbox->set_clip_text(true); + global_history_checkbox->connect("toggled", callable_mp(this, &HistoryDock::refresh_history).unbind(1)); + + action_list = memnew(ItemList); + add_child(action_list); + action_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + action_list->connect("item_selected", callable_mp(this, &HistoryDock::seek_history)); +} diff --git a/editor/history_dock.h b/editor/history_dock.h new file mode 100644 index 0000000000..9dda8165e6 --- /dev/null +++ b/editor/history_dock.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* history_dock.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef HISTORY_DOCK_H +#define HISTORY_DOCK_H + +#include "scene/gui/box_container.h" + +class CheckBox; +class ItemList; +class EditorUndoRedoManager; + +class HistoryDock : public VBoxContainer { + GDCLASS(HistoryDock, VBoxContainer); + + Ref<EditorUndoRedoManager> ur_manager; + ItemList *action_list = nullptr; + + CheckBox *current_scene_checkbox = nullptr; + CheckBox *global_history_checkbox = nullptr; + + bool need_refresh = true; + int current_version = 0; + + void on_history_changed(); + void refresh_history(); + void on_version_changed(); + void refresh_version(); + +protected: + void _notification(int p_notification); + +public: + void seek_history(int p_index); + + HistoryDock(); +}; + +#endif // HISTORY_DOCK_H diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 27b261f320..bd2eb5fd9e 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -34,6 +34,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/script_editor_plugin.h" InspectorDock *InspectorDock::singleton = nullptr; @@ -769,7 +770,6 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { inspector->set_property_name_style(EditorPropertyNameProcessor::get_default_inspector_style()); inspector->set_use_folding(!bool(EDITOR_GET("interface/inspector/disable_folding"))); inspector->register_text_enter(search); - inspector->set_undo_redo(editor_data->get_undo_redo()); inspector->set_use_filter(true); // TODO: check me diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp index 7727ff31e4..4b8257969c 100644 --- a/editor/localization_editor.cpp +++ b/editor/localization_editor.cpp @@ -81,6 +81,7 @@ void LocalizationEditor::_translation_add(const PackedStringArray &p_paths) { } } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat(TTR("Add %d Translations"), p_paths.size())); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", translations); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", GLOBAL_GET("internationalization/locale/translations")); @@ -111,6 +112,7 @@ void LocalizationEditor::_translation_delete(Object *p_item, int p_column, int p translations.remove_at(idx); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove Translation")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", translations); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations", GLOBAL_GET("internationalization/locale/translations")); @@ -141,6 +143,7 @@ void LocalizationEditor::_translation_res_add(const PackedStringArray &p_paths) } } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat(TTR("Translation Resource Remap: Add %d Path(s)"), p_paths.size())); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", prev); @@ -172,6 +175,7 @@ void LocalizationEditor::_translation_res_option_add(const PackedStringArray &p_ } remaps[key] = r; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat(TTR("Translation Resource Remap: Add %d Remap(s)"), p_paths.size())); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", GLOBAL_GET("internationalization/locale/translation_remaps")); @@ -234,6 +238,8 @@ void LocalizationEditor::_translation_res_option_changed() { remaps[key] = r; updating_translations = true; + + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Change Resource Remap Language")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", GLOBAL_GET("internationalization/locale/translation_remaps")); @@ -267,6 +273,7 @@ void LocalizationEditor::_translation_res_delete(Object *p_item, int p_column, i remaps.erase(key); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove Resource Remap")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", GLOBAL_GET("internationalization/locale/translation_remaps")); @@ -306,6 +313,7 @@ void LocalizationEditor::_translation_res_option_delete(Object *p_item, int p_co r.remove_at(idx); remaps[key] = r; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove Resource Remap Option")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", remaps); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translation_remaps", GLOBAL_GET("internationalization/locale/translation_remaps")); @@ -324,6 +332,7 @@ void LocalizationEditor::_pot_add(const PackedStringArray &p_paths) { } } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(vformat(TTR("Add %d file(s) for POT generation"), p_paths.size())); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", pot_translations); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", GLOBAL_GET("internationalization/locale/translations_pot_files")); @@ -350,6 +359,7 @@ void LocalizationEditor::_pot_delete(Object *p_item, int p_column, int p_button, pot_translations.remove_at(idx); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove file from POT generation")); undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", pot_translations); undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/translations_pot_files", GLOBAL_GET("internationalization/locale/translations_pot_files")); @@ -592,7 +602,6 @@ void LocalizationEditor::_bind_methods() { } LocalizationEditor::LocalizationEditor() { - undo_redo = EditorNode::get_undo_redo(); localization_changed = "localization_changed"; TabContainer *translations = memnew(TabContainer); diff --git a/editor/localization_editor.h b/editor/localization_editor.h index ecac171fe3..5fa6d9519a 100644 --- a/editor/localization_editor.h +++ b/editor/localization_editor.h @@ -31,7 +31,6 @@ #ifndef LOCALIZATION_EDITOR_H #define LOCALIZATION_EDITOR_H -#include "core/object/undo_redo.h" #include "editor/editor_locale_dialog.h" #include "scene/gui/tree.h" @@ -56,7 +55,6 @@ class LocalizationEditor : public VBoxContainer { EditorFileDialog *pot_file_open_dialog = nullptr; EditorFileDialog *pot_generate_dialog = nullptr; - Ref<EditorUndoRedoManager> undo_redo; bool updating_translations = false; String localization_changed; diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp index 55fa2f22dd..266b265819 100644 --- a/editor/node_dock.cpp +++ b/editor/node_dock.cpp @@ -117,13 +117,11 @@ NodeDock::NodeDock() { groups_button->connect("pressed", callable_mp(this, &NodeDock::show_groups)); connections = memnew(ConnectionsDock); - connections->set_undo_redo(EditorNode::get_undo_redo()); add_child(connections); connections->set_v_size_flags(SIZE_EXPAND_FILL); connections->hide(); groups = memnew(GroupsEditor); - groups->set_undo_redo(EditorNode::get_undo_redo()); add_child(groups); groups->set_v_size_flags(SIZE_EXPAND_FILL); groups->hide(); diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index cb07d8cf01..ecb4837695 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -91,6 +91,7 @@ void AbstractPolygon2DEditor::_set_polygon(int p_idx, const Variant &p_polygon) void AbstractPolygon2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) { Node2D *node = _get_node(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->add_do_method(node, "set_polygon", p_polygon); undo_redo->add_undo_method(node, "set_polygon", p_previous); } @@ -100,6 +101,7 @@ Vector2 AbstractPolygon2DEditor::_get_offset(int p_idx) const { } void AbstractPolygon2DEditor::_commit_action() { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); @@ -203,6 +205,7 @@ void AbstractPolygon2DEditor::_wip_close() { if (_is_line()) { _set_polygon(0, wip); } else if (wip.size() >= (_is_line() ? 2 : 3)) { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Create Polygon")); _action_add_polygon(wip); if (_has_uv()) { @@ -254,6 +257,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) return false; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); Ref<InputEventMouseButton> mb = p_event; if (!_has_resource()) { @@ -611,6 +615,7 @@ void AbstractPolygon2DEditor::_bind_methods() { } void AbstractPolygon2DEditor::remove_point(const Vertex &p_vertex) { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); Vector<Vector2> vertices = _get_polygon(p_vertex.polygon); if (vertices.size() > (_is_line() ? 2 : 3)) { @@ -706,8 +711,6 @@ AbstractPolygon2DEditor::PosVertex AbstractPolygon2DEditor::closest_edge_point(c } AbstractPolygon2DEditor::AbstractPolygon2DEditor(bool p_wip_destructive) { - undo_redo = EditorNode::get_undo_redo(); - edited_point = PosVertex(); wip_destructive = p_wip_destructive; diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index 1fbbe67c8d..8a3db9d837 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -36,7 +36,6 @@ #include "scene/gui/box_container.h" class CanvasItemEditor; -class EditorUndoRedoManager; class AbstractPolygon2DEditor : public HBoxContainer { GDCLASS(AbstractPolygon2DEditor, HBoxContainer); @@ -100,8 +99,6 @@ protected: int mode = MODE_EDIT; - Ref<EditorUndoRedoManager> undo_redo; - virtual void _menu_option(int p_option); void _wip_changed(); void _wip_close(); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index b4737976d8..1ebe81c4b0 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -39,6 +39,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning. #include "editor/plugins/node_3d_editor_plugin.h" // For onion skinning. #include "editor/scene_tree_dock.h" @@ -167,6 +168,7 @@ void AnimationPlayerEditor::_autoplay_pressed() { return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); String current = animation->get_item_text(animation->get_selected()); if (player->get_autoplay() == current) { //unset @@ -383,6 +385,7 @@ void AnimationPlayerEditor::_animation_remove_confirmed() { if (current.contains("/")) { current = current.get_slice("/", 1); } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove Animation")); if (player->get_autoplay() == current) { undo_redo->add_do_method(player, "set_autoplay", ""); @@ -458,6 +461,7 @@ void AnimationPlayerEditor::_animation_name_edited() { return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); switch (name_dialog_op) { case TOOL_RENAME_ANIM: { String current = animation->get_item_text(animation->get_selected()); @@ -592,6 +596,7 @@ void AnimationPlayerEditor::_blend_editor_next_changed(const int p_idx) { String current = animation->get_item_text(animation->get_selected()); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Blend Next Changed")); undo_redo->add_do_method(player, "animation_set_next", current, blend_editor.next->get_item_text(p_idx)); undo_redo->add_undo_method(player, "animation_set_next", current, player->animation_get_next(current)); @@ -678,6 +683,7 @@ void AnimationPlayerEditor::_blend_edited() { float blend_time = selected->get_range(1); float prev_blend_time = player->get_blend_time(current, to); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Change Blend Time")); undo_redo->add_do_method(player, "set_blend_time", current, to, blend_time); undo_redo->add_undo_method(player, "set_blend_time", current, to, prev_blend_time); @@ -984,10 +990,6 @@ void AnimationPlayerEditor::_update_name_dialog_library_dropdown() { } } -void AnimationPlayerEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { - undo_redo = p_undo_redo; -} - void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { if (player && pin->is_pressed()) { return; // Ignore, pinned. @@ -1894,7 +1896,6 @@ void AnimationPlayerEditorPlugin::_update_keying() { } void AnimationPlayerEditorPlugin::edit(Object *p_object) { - anim_editor->set_undo_redo(get_undo_redo()); if (!p_object) { return; } @@ -1915,7 +1916,6 @@ void AnimationPlayerEditorPlugin::make_visible(bool p_visible) { AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin() { anim_editor = memnew(AnimationPlayerEditor(this)); - anim_editor->set_undo_redo(EditorNode::get_undo_redo()); EditorNode::get_singleton()->add_bottom_panel_item(TTR("Animation"), anim_editor); } diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index ae570e53f3..6370b00ea8 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -41,7 +41,6 @@ #include "scene/gui/texture_button.h" #include "scene/gui/tree.h" -class EditorUndoRedoManager; class AnimationPlayerEditorPlugin; class AnimationPlayerEditor : public VBoxContainer { @@ -101,7 +100,6 @@ class AnimationPlayerEditor : public VBoxContainer { LineEdit *name = nullptr; OptionButton *library = nullptr; Label *name_title = nullptr; - Ref<EditorUndoRedoManager> undo_redo; Ref<Texture2D> autoplay_icon; Ref<Texture2D> reset_icon; @@ -237,7 +235,6 @@ public: void ensure_visibility(); - void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void edit(AnimationPlayer *p_player); void forward_force_draw_over_viewport(Control *p_overlay); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 3f6769a5ad..1d6f41d4c4 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -868,6 +868,7 @@ void CanvasItemEditor::_commit_canvas_item_state(List<CanvasItem *> p_canvas_ite return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(action_name); for (CanvasItem *ci : modified_canvas_items) { CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci); @@ -934,6 +935,7 @@ void CanvasItemEditor::_add_node_pressed(int p_result) { return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Move Node(s) to Position")); for (Node *node : nodes_to_move) { CanvasItem *ci = Object::cast_to<CanvasItem>(node); @@ -1022,6 +1024,7 @@ void CanvasItemEditor::_on_grid_menu_id_pressed(int p_id) { } bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_event) { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); Ref<InputEventMouseButton> b = p_event; Ref<InputEventMouseMotion> m = p_event; @@ -4000,10 +4003,6 @@ void CanvasItemEditor::_selection_changed() { selected_from_canvas = false; } -void CanvasItemEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { - undo_redo = p_undo_redo; -} - void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { Array selection = editor_selection->get_selected_nodes(); if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) { @@ -4278,6 +4277,7 @@ void CanvasItemEditor::_update_override_camera_button(bool p_game_running) { } void CanvasItemEditor::_popup_callback(int p_op) { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); last_option = MenuOption(p_op); switch (p_op) { case SHOW_ORIGIN: { @@ -4983,7 +4983,6 @@ CanvasItemEditor::CanvasItemEditor() { snap_target[0] = SNAP_TARGET_NONE; snap_target[1] = SNAP_TARGET_NONE; - undo_redo = EditorNode::get_singleton()->get_undo_redo(); editor_selection = EditorNode::get_singleton()->get_editor_selection(); editor_selection->add_editor_plugin(this); editor_selection->connect("selection_changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); @@ -5434,7 +5433,6 @@ CanvasItemEditor::CanvasItemEditor() { CanvasItemEditor *CanvasItemEditor::singleton = nullptr; void CanvasItemEditorPlugin::edit(Object *p_object) { - canvas_item_editor->set_undo_redo(EditorNode::get_undo_redo()); canvas_item_editor->edit(Object::cast_to<CanvasItem>(p_object)); } @@ -5581,37 +5579,38 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & String name = path.get_file().get_basename(); child->set_name(Node::adjust_name_casing(name)); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); Ref<Texture2D> texture = ResourceCache::get_ref(path); if (parent) { - editor_data->get_undo_redo()->add_do_method(parent, "add_child", child, true); - editor_data->get_undo_redo()->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); - editor_data->get_undo_redo()->add_do_reference(child); - editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", child); + undo_redo->add_do_method(parent, "add_child", child, true); + undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_reference(child); + undo_redo->add_undo_method(parent, "remove_child", child); } else { // If no parent is selected, set as root node of the scene. - editor_data->get_undo_redo()->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child); - editor_data->get_undo_redo()->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); - editor_data->get_undo_redo()->add_do_reference(child); - editor_data->get_undo_redo()->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); + undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child); + undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_reference(child); + undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); } if (parent) { String new_name = parent->validate_child_name(child); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo()->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), child->get_class(), new_name); - editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), child->get_class(), new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); } if (Object::cast_to<TouchScreenButton>(child) || Object::cast_to<TextureButton>(child)) { - editor_data->get_undo_redo()->add_do_property(child, "texture_normal", texture); + undo_redo->add_do_property(child, "texture_normal", texture); } else { - editor_data->get_undo_redo()->add_do_property(child, "texture", texture); + undo_redo->add_do_property(child, "texture", texture); } // make visible for certain node type if (Object::cast_to<Control>(child)) { Size2 texture_size = texture->get_size(); - editor_data->get_undo_redo()->add_do_property(child, "rect_size", texture_size); + undo_redo->add_do_property(child, "rect_size", texture_size); } else if (Object::cast_to<Polygon2D>(child)) { Size2 texture_size = texture->get_size(); Vector<Vector2> list = { @@ -5620,7 +5619,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & Vector2(texture_size.width, texture_size.height), Vector2(0, texture_size.height) }; - editor_data->get_undo_redo()->add_do_property(child, "polygon", list); + undo_redo->add_do_property(child, "polygon", list); } // Compute the global position @@ -5629,7 +5628,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & // there's nothing to be used as source position so snapping will work as absolute if enabled target_position = canvas_item_editor->snap_point(target_position); - editor_data->get_undo_redo()->add_do_method(child, "set_global_position", target_position); + undo_redo->add_do_method(child, "set_global_position", target_position); } bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { @@ -5654,15 +5653,16 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path)); - editor_data->get_undo_redo()->add_do_method(parent, "add_child", instantiated_scene, true); - editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_owner", edited_scene); - editor_data->get_undo_redo()->add_do_reference(instantiated_scene); - editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", instantiated_scene); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->add_do_method(parent, "add_child", instantiated_scene, true); + undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene); + undo_redo->add_do_reference(instantiated_scene); + undo_redo->add_undo_method(parent, "remove_child", instantiated_scene); String new_name = parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo()->add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), path, new_name); - editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name)); + undo_redo->add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), path, new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name)); CanvasItem *instance_ci = Object::cast_to<CanvasItem>(instantiated_scene); if (instance_ci) { @@ -5676,7 +5676,7 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons // Preserve instance position of the original scene. target_pos += instance_ci->_edit_get_position(); - editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_position", target_pos); + undo_redo->add_do_method(instantiated_scene, "set_position", target_pos); } return true; @@ -5694,7 +5694,8 @@ void CanvasItemEditorViewport::_perform_drop_data() { Vector<String> error_files; - editor_data->get_undo_redo()->create_action(TTR("Create Node")); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Create Node")); for (int i = 0; i < selected_files.size(); i++) { String path = selected_files[i]; @@ -5725,7 +5726,7 @@ void CanvasItemEditorViewport::_perform_drop_data() { } } - editor_data->get_undo_redo()->commit_action(); + undo_redo->commit_action(); if (error_files.size() > 0) { String files_str; @@ -5918,7 +5919,6 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it texture_node_types.push_back("NinePatchRect"); target_node = nullptr; - editor_data = SceneTreeDock::get_singleton()->get_editor_data(); canvas_item_editor = p_canvas_item_editor; preview_node = memnew(Control); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 3ade048e4b..ba193a67b8 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -45,7 +45,6 @@ class EditorData; class CanvasItemEditorViewport; class ViewPanner; -class EditorUndoRedoManager; class CanvasItemEditorSelectedItem : public Object { GDCLASS(CanvasItemEditorSelectedItem, Object); @@ -401,8 +400,6 @@ private: void _prepare_grid_menu(); void _on_grid_menu_id_pressed(int p_id); - Ref<EditorUndoRedoManager> undo_redo; - List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true); Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list); void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true); @@ -548,7 +545,6 @@ public: Tool get_current_tool() { return tool; } void set_current_tool(Tool p_tool); - void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void edit(CanvasItem *p_canvas_item); void focus_selection(); @@ -593,7 +589,6 @@ class CanvasItemEditorViewport : public Control { Node *target_node = nullptr; Point2 drop_pos; - EditorData *editor_data = nullptr; CanvasItemEditor *canvas_item_editor = nullptr; Control *preview_node = nullptr; AcceptDialog *accept = nullptr; diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 6149127fcc..c7bb6b79ef 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -37,6 +37,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" CurveEditor::CurveEditor() { _selected_point = -1; @@ -139,14 +140,13 @@ void CurveEditor::gui_input(const Ref<InputEvent> &p_event) { if (!mb.is_pressed() && _dragging && mb.get_button_index() == MouseButton::LEFT) { _dragging = false; if (_has_undo_data) { - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); - - ur->create_action(_selected_tangent == TANGENT_NONE ? TTR("Modify Curve Point") : TTR("Modify Curve Tangent")); - ur->add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data()); - ur->add_undo_method(*_curve_ref, "_set_data", _undo_data); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(_selected_tangent == TANGENT_NONE ? TTR("Modify Curve Point") : TTR("Modify Curve Tangent")); + undo_redo->add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data()); + undo_redo->add_undo_method(*_curve_ref, "_set_data", _undo_data); // Note: this will trigger one more "changed" signal even if nothing changes, // but it's ok since it would have fired every frame during the drag anyways - ur->commit_action(); + undo_redo->commit_action(); _has_undo_data = false; } @@ -301,13 +301,11 @@ void CurveEditor::on_preset_item_selected(int preset_id) { break; } - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); - ur->create_action(TTR("Load Curve Preset")); - - ur->add_do_method(&curve, "_set_data", curve.get_data()); - ur->add_undo_method(&curve, "_set_data", previous_data); - - ur->commit_action(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Load Curve Preset")); + undo_redo->add_do_method(&curve, "_set_data", curve.get_data()); + undo_redo->add_undo_method(&curve, "_set_data", previous_data); + undo_redo->commit_action(); } void CurveEditor::_curve_changed() { @@ -435,8 +433,8 @@ CurveEditor::TangentIndex CurveEditor::get_tangent_at(Vector2 pos) const { void CurveEditor::add_point(Vector2 pos) { ERR_FAIL_COND(_curve_ref.is_null()); - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); - ur->create_action(TTR("Remove Curve Point")); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Remove Curve Point")); Vector2 point_pos = get_world_pos(pos); if (point_pos.y < 0.0) { @@ -449,22 +447,21 @@ void CurveEditor::add_point(Vector2 pos) { int i = _curve_ref->add_point(point_pos); _curve_ref->remove_point(i); - ur->add_do_method(*_curve_ref, "add_point", point_pos); - ur->add_undo_method(*_curve_ref, "remove_point", i); - - ur->commit_action(); + undo_redo->add_do_method(*_curve_ref, "add_point", point_pos); + undo_redo->add_undo_method(*_curve_ref, "remove_point", i); + undo_redo->commit_action(); } void CurveEditor::remove_point(int index) { ERR_FAIL_COND(_curve_ref.is_null()); - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); - ur->create_action(TTR("Remove Curve Point")); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Remove Curve Point")); Curve::Point p = _curve_ref->get_point(index); - ur->add_do_method(*_curve_ref, "remove_point", index); - ur->add_undo_method(*_curve_ref, "add_point", p.position, p.left_tangent, p.right_tangent, p.left_mode, p.right_mode); + undo_redo->add_do_method(*_curve_ref, "remove_point", index); + undo_redo->add_undo_method(*_curve_ref, "add_point", p.position, p.left_tangent, p.right_tangent, p.left_mode, p.right_mode); if (index == _selected_point) { set_selected_point(-1); @@ -474,14 +471,14 @@ void CurveEditor::remove_point(int index) { set_hover_point_index(-1); } - ur->commit_action(); + undo_redo->commit_action(); } void CurveEditor::toggle_linear(TangentIndex tangent) { ERR_FAIL_COND(_curve_ref.is_null()); - Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); - ur->create_action(TTR("Toggle Curve Linear Tangent")); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Toggle Curve Linear Tangent")); if (tangent == TANGENT_NONE) { tangent = _selected_tangent; @@ -493,8 +490,8 @@ void CurveEditor::toggle_linear(TangentIndex tangent) { Curve::TangentMode prev_mode = _curve_ref->get_point_left_mode(_selected_point); Curve::TangentMode mode = is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR; - ur->add_do_method(*_curve_ref, "set_point_left_mode", _selected_point, mode); - ur->add_undo_method(*_curve_ref, "set_point_left_mode", _selected_point, prev_mode); + undo_redo->add_do_method(*_curve_ref, "set_point_left_mode", _selected_point, mode); + undo_redo->add_undo_method(*_curve_ref, "set_point_left_mode", _selected_point, prev_mode); } else { bool is_linear = _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR; @@ -502,11 +499,11 @@ void CurveEditor::toggle_linear(TangentIndex tangent) { Curve::TangentMode prev_mode = _curve_ref->get_point_right_mode(_selected_point); Curve::TangentMode mode = is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR; - ur->add_do_method(*_curve_ref, "set_point_right_mode", _selected_point, mode); - ur->add_undo_method(*_curve_ref, "set_point_right_mode", _selected_point, prev_mode); + undo_redo->add_do_method(*_curve_ref, "set_point_right_mode", _selected_point, mode); + undo_redo->add_undo_method(*_curve_ref, "set_point_right_mode", _selected_point, prev_mode); } - ur->commit_action(); + undo_redo->commit_action(); } void CurveEditor::set_selected_point(int index) { diff --git a/editor/plugins/gradient_editor.cpp b/editor/plugins/gradient_editor.cpp index 822f303d93..7039ada10a 100644 --- a/editor/plugins/gradient_editor.cpp +++ b/editor/plugins/gradient_editor.cpp @@ -99,7 +99,7 @@ void GradientEditor::_gradient_changed() { void GradientEditor::_ramp_changed() { editing = true; - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS); undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets()); undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors()); diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp index e7ef65c32b..f2c46cc9e8 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.cpp +++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -30,6 +30,9 @@ #include "light_occluder_2d_editor_plugin.h" +#include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" + Ref<OccluderPolygon2D> LightOccluder2DEditor::_ensure_occluder() const { Ref<OccluderPolygon2D> occluder = node->get_occluder_polygon(); if (!occluder.is_valid()) { @@ -81,6 +84,7 @@ void LightOccluder2DEditor::_set_polygon(int p_idx, const Variant &p_polygon) co void LightOccluder2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) { Ref<OccluderPolygon2D> occluder = _ensure_occluder(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->add_do_method(occluder.ptr(), "set_polygon", p_polygon); undo_redo->add_undo_method(occluder.ptr(), "set_polygon", p_previous); } @@ -94,6 +98,7 @@ void LightOccluder2DEditor::_create_resource() { return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Create Occluder Polygon")); undo_redo->add_do_method(node, "set_occluder_polygon", Ref<OccluderPolygon2D>(memnew(OccluderPolygon2D))); undo_redo->add_undo_method(node, "set_occluder_polygon", Variant(Ref<RefCounted>())); diff --git a/editor/plugins/line_2d_editor_plugin.cpp b/editor/plugins/line_2d_editor_plugin.cpp index d73761d770..9182b23867 100644 --- a/editor/plugins/line_2d_editor_plugin.cpp +++ b/editor/plugins/line_2d_editor_plugin.cpp @@ -30,6 +30,9 @@ #include "line_2d_editor_plugin.h" +#include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" + Node2D *Line2DEditor::_get_node() const { return node; } @@ -52,6 +55,7 @@ void Line2DEditor::_set_polygon(int p_idx, const Variant &p_polygon) const { void Line2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) { Node2D *_node = _get_node(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->add_do_method(_node, "set_points", p_polygon); undo_redo->add_undo_method(_node, "set_points", p_previous); } diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp index 8f3553b8cf..664e18f555 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.cpp +++ b/editor/plugins/navigation_polygon_editor_plugin.cpp @@ -30,6 +30,9 @@ #include "navigation_polygon_editor_plugin.h" +#include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" + Ref<NavigationPolygon> NavigationPolygonEditor::_ensure_navpoly() const { Ref<NavigationPolygon> navpoly = node->get_navigation_polygon(); if (!navpoly.is_valid()) { @@ -73,6 +76,7 @@ void NavigationPolygonEditor::_set_polygon(int p_idx, const Variant &p_polygon) void NavigationPolygonEditor::_action_add_polygon(const Variant &p_polygon) { Ref<NavigationPolygon> navpoly = _ensure_navpoly(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->add_do_method(navpoly.ptr(), "add_outline", p_polygon); undo_redo->add_undo_method(navpoly.ptr(), "remove_outline", navpoly->get_outline_count()); undo_redo->add_do_method(navpoly.ptr(), "make_polygons_from_outlines"); @@ -81,6 +85,7 @@ void NavigationPolygonEditor::_action_add_polygon(const Variant &p_polygon) { void NavigationPolygonEditor::_action_remove_polygon(int p_idx) { Ref<NavigationPolygon> navpoly = _ensure_navpoly(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->add_do_method(navpoly.ptr(), "remove_outline", p_idx); undo_redo->add_undo_method(navpoly.ptr(), "add_outline_at_index", navpoly->get_outline(p_idx), p_idx); undo_redo->add_do_method(navpoly.ptr(), "make_polygons_from_outlines"); @@ -89,6 +94,7 @@ void NavigationPolygonEditor::_action_remove_polygon(int p_idx) { void NavigationPolygonEditor::_action_set_polygon(int p_idx, const Variant &p_previous, const Variant &p_polygon) { Ref<NavigationPolygon> navpoly = _ensure_navpoly(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->add_do_method(navpoly.ptr(), "set_outline", p_idx, p_polygon); undo_redo->add_undo_method(navpoly.ptr(), "set_outline", p_idx, p_previous); undo_redo->add_do_method(navpoly.ptr(), "make_polygons_from_outlines"); @@ -104,6 +110,7 @@ void NavigationPolygonEditor::_create_resource() { return; } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Create Navigation Polygon")); undo_redo->add_do_method(node, "set_navigation_polygon", Ref<NavigationPolygon>(memnew(NavigationPolygon))); undo_redo->add_undo_method(node, "set_navigation_polygon", Variant(Ref<RefCounted>())); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 6a9568b2a2..7febb50f5c 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -40,6 +40,7 @@ #include "editor/debugger/editor_debugger_node.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/node_3d_editor_gizmos.h" #include "editor/plugins/script_editor_plugin.h" @@ -2898,6 +2899,7 @@ void Node3DEditorViewport::_draw() { } void Node3DEditorViewport::_menu_option(int p_option) { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); switch (p_option) { case VIEW_TOP: { cursor.y_rot = 0; @@ -4048,15 +4050,16 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path)); } - editor_data->get_undo_redo()->add_do_method(parent, "add_child", instantiated_scene, true); - editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene()); - editor_data->get_undo_redo()->add_do_reference(instantiated_scene); - editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", instantiated_scene); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + undo_redo->add_do_method(parent, "add_child", instantiated_scene, true); + undo_redo->add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_reference(instantiated_scene); + undo_redo->add_undo_method(parent, "remove_child", instantiated_scene); String new_name = parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo()->add_do_method(ed, "live_debug_instance_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name); - editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + undo_redo->add_do_method(ed, "live_debug_instance_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); Node3D *node3d = Object::cast_to<Node3D>(instantiated_scene); if (node3d) { @@ -4069,26 +4072,27 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po gl_transform.origin = preview_node_pos; gl_transform.basis *= node3d->get_transform().basis; - editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_global_transform", gl_transform); + undo_redo->add_do_method(instantiated_scene, "set_global_transform", gl_transform); } return true; } void Node3DEditorViewport::_perform_drop_data() { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); if (spatial_editor->get_preview_material_target().is_valid()) { GeometryInstance3D *geometry_instance = Object::cast_to<GeometryInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target())); MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target())); if (mesh_instance && spatial_editor->get_preview_material_surface() != -1) { - editor_data->get_undo_redo()->create_action(vformat(TTR("Set Surface %d Override Material"), spatial_editor->get_preview_material_surface())); - editor_data->get_undo_redo()->add_do_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_material()); - editor_data->get_undo_redo()->add_undo_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_reset_material()); - editor_data->get_undo_redo()->commit_action(); + undo_redo->create_action(vformat(TTR("Set Surface %d Override Material"), spatial_editor->get_preview_material_surface())); + undo_redo->add_do_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_material()); + undo_redo->add_undo_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_reset_material()); + undo_redo->commit_action(); } else if (geometry_instance) { - editor_data->get_undo_redo()->create_action(TTR("Set Material Override")); - editor_data->get_undo_redo()->add_do_method(geometry_instance, "set_material_override", spatial_editor->get_preview_material()); - editor_data->get_undo_redo()->add_undo_method(geometry_instance, "set_material_override", spatial_editor->get_preview_reset_material()); - editor_data->get_undo_redo()->commit_action(); + undo_redo->create_action(TTR("Set Material Override")); + undo_redo->add_do_method(geometry_instance, "set_material_override", spatial_editor->get_preview_material()); + undo_redo->add_undo_method(geometry_instance, "set_material_override", spatial_editor->get_preview_reset_material()); + undo_redo->commit_action(); } _remove_preview_material(); @@ -4099,7 +4103,7 @@ void Node3DEditorViewport::_perform_drop_data() { Vector<String> error_files; - editor_data->get_undo_redo()->create_action(TTR("Create Node")); + undo_redo->create_action(TTR("Create Node")); for (int i = 0; i < selected_files.size(); i++) { String path = selected_files[i]; @@ -4117,7 +4121,7 @@ void Node3DEditorViewport::_perform_drop_data() { } } - editor_data->get_undo_redo()->commit_action(); + undo_redo->commit_action(); if (error_files.size() > 0) { String files_str; @@ -4285,6 +4289,7 @@ void Node3DEditorViewport::commit_transform() { TTRC("Translate"), TTRC("Scale"), }; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(_transform_name[_edit.mode]); List<Node *> &selection = editor_selection->get_selected_node_list(); @@ -4690,9 +4695,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p _edit.gizmo_handle_secondary = false; index = p_index; - editor_data = SceneTreeDock::get_singleton()->get_editor_data(); editor_selection = EditorNode::get_singleton()->get_editor_selection(); - undo_redo = EditorNode::get_singleton()->get_undo_redo(); orthogonal = false; auto_orthogonal = false; @@ -5776,6 +5779,7 @@ void Node3DEditor::_xform_dialog_action() { t.basis.rotate(rotate); t.origin = translate; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("XForm Dialog")); const List<Node *> &selection = editor_selection->get_selected_node_list(); @@ -5887,6 +5891,7 @@ void Node3DEditor::_update_camera_override_viewport(Object *p_viewport) { } void Node3DEditor::_menu_item_pressed(int p_option) { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); switch (p_option) { case MENU_TOOL_SELECT: case MENU_TOOL_MOVE: @@ -7072,6 +7077,7 @@ void Node3DEditor::_snap_selected_nodes_to_floor() { } if (snapped_to_floor) { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Snap Nodes to Floor")); // Perform snapping if at least one node can be snapped @@ -7141,6 +7147,7 @@ void Node3DEditor::_add_sun_to_scene(bool p_already_added_environment) { ERR_FAIL_COND(!base); Node *new_sun = preview_sun->duplicate(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add Preview Sun to Scene")); undo_redo->add_do_method(base, "add_child", new_sun, true); // Move to the beginning of the scene tree since more "global" nodes @@ -7174,6 +7181,7 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) { new_env->set_camera_attributes(preview_environment->get_camera_attributes()->duplicate(true)); } + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add Preview Environment to Scene")); undo_redo->add_do_method(base, "add_child", new_env, true); // Move to the beginning of the scene tree since more "global" nodes @@ -7313,14 +7321,6 @@ Vector<int> Node3DEditor::get_subgizmo_selection() { return ret; } -void Node3DEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { - undo_redo = p_undo_redo; -} - -Ref<EditorUndoRedoManager> Node3DEditor::get_undo_redo() { - return undo_redo; -} - void Node3DEditor::add_control_to_menu_panel(Control *p_control) { context_menu_hbox->add_child(p_control); } @@ -7771,7 +7771,6 @@ Node3DEditor::Node3DEditor() { gizmo.scale = 1.0; viewport_environment = Ref<Environment>(memnew(Environment)); - undo_redo = EditorNode::get_singleton()->get_undo_redo(); VBoxContainer *vbc = this; custom_camera = nullptr; diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 7dbe153efd..5ea1bf6dc1 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -53,7 +53,6 @@ class Node3DEditorViewport; class SubViewportContainer; class DirectionalLight3D; class WorldEnvironment; -class EditorUndoRedoManager; class ViewportRotationControl : public Control { GDCLASS(ViewportRotationControl, Control); @@ -203,9 +202,7 @@ private: Node *target_node = nullptr; Point2 drop_pos; - EditorData *editor_data = nullptr; EditorSelection *editor_selection = nullptr; - Ref<EditorUndoRedoManager> undo_redo; CheckBox *preview_camera = nullptr; SubViewportContainer *subviewport_container = nullptr; @@ -686,7 +683,6 @@ private: HBoxContainer *context_menu_hbox = nullptr; void _generate_selection_boxes(); - Ref<EditorUndoRedoManager> undo_redo; int camera_override_viewport_id; @@ -835,9 +831,6 @@ public: Ref<Environment> get_viewport_environment() { return viewport_environment; } - void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); - Ref<EditorUndoRedoManager> get_undo_redo(); - void add_control_to_menu_panel(Control *p_control); void remove_control_from_menu_panel(Control *p_control); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 3f8ca825c0..6218c887fb 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -32,8 +32,10 @@ #include "core/input/input_event.h" #include "core/math/geometry_2d.h" +#include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "scene/2d/skeleton_2d.h" #include "scene/gui/menu_button.h" @@ -150,6 +152,7 @@ void Polygon2DEditor::_sync_bones() { Array new_bones = node->call("_get_bones"); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Sync Bones")); undo_redo->add_do_method(node, "_set_bones", new_bones); undo_redo->add_undo_method(node, "_set_bones", prev_bones); @@ -279,6 +282,7 @@ void Polygon2DEditor::_uv_edit_popup_hide() { } void Polygon2DEditor::_menu_option(int p_option) { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); switch (p_option) { case MODE_EDIT_UV: { if (node->get_texture().is_null()) { @@ -391,6 +395,7 @@ void Polygon2DEditor::_update_polygon_editing_state() { void Polygon2DEditor::_commit_action() { // Makes that undo/redoing actions made outside of the UV editor still affect its polygon. + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->add_do_method(uv_edit_draw, "queue_redraw"); undo_redo->add_undo_method(uv_edit_draw, "queue_redraw"); undo_redo->add_do_method(CanvasItemEditor::get_singleton(), "update_viewport"); @@ -458,8 +463,9 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { mtx.columns[2] = -uv_draw_ofs; mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom)); - Ref<InputEventMouseButton> mb = p_input; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + Ref<InputEventMouseButton> mb = p_input; if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index 21647d1b69..e35e794b24 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -36,6 +36,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" void ResourcePreloaderEditor::_notification(int p_what) { switch (p_what) { diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 456c28d887..2f80e41dd4 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/filesystem_dock.h" #include "editor/plugins/text_shader_editor.h" #include "editor/plugins/visual_shader_editor_plugin.h" @@ -225,7 +226,7 @@ void ShaderEditorPlugin::_close_shader(int p_index) { memdelete(c); edited_shaders.remove_at(p_index); _update_shader_list(); - EditorNode::get_singleton()->get_undo_redo()->clear_history(); // To prevent undo on deleted graphs. + EditorNode::get_undo_redo()->clear_history(); // To prevent undo on deleted graphs. } void ShaderEditorPlugin::_resource_saved(Object *obj) { diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index f1dfba6f35..8bf99db43b 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -33,7 +33,9 @@ #include "tiles_editor_plugin.h" #include "editor/editor_inspector.h" +#include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/progress_dialog.h" #include "editor/editor_node.h" @@ -1310,6 +1312,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven } void TileSetAtlasSourceEditor::_end_dragging() { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); switch (drag_type) { case DRAG_TYPE_CREATE_TILES: undo_redo->create_action(TTR("Create tiles")); @@ -1540,6 +1543,8 @@ HashMap<Vector2i, List<const PropertyInfo *>> TileSetAtlasSourceEditor::_group_p } void TileSetAtlasSourceEditor::_menu_option(int p_option) { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); + switch (p_option) { case TILE_DELETE: { List<PropertyInfo> list; @@ -2198,6 +2203,7 @@ void TileSetAtlasSourceEditor::_auto_create_tiles() { Vector2i separation = tile_set_atlas_source->get_separation(); Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size(); Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Create tiles in non-transparent texture regions")); for (int y = 0; y < grid_size.y; y++) { for (int x = 0; x < grid_size.x; x++) { @@ -2243,6 +2249,7 @@ void TileSetAtlasSourceEditor::_auto_remove_tiles() { Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size(); Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove tiles in fully transparent texture regions")); List<PropertyInfo> list; @@ -2336,8 +2343,6 @@ void TileSetAtlasSourceEditor::_bind_methods() { } TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { - undo_redo = EditorNode::get_undo_redo(); - set_process_unhandled_key_input(true); set_process_internal(true); @@ -2366,7 +2371,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_proxy_object_changed)); tile_inspector = memnew(EditorInspector); - tile_inspector->set_undo_redo(undo_redo); tile_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); tile_inspector->edit(tile_proxy_object); tile_inspector->set_use_folding(true); @@ -2414,7 +2418,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { atlas_source_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed)); atlas_source_inspector = memnew(EditorInspector); - atlas_source_inspector->set_undo_redo(undo_redo); atlas_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); atlas_source_inspector->edit(atlas_source_proxy_object); middle_vbox_container->add_child(atlas_source_inspector); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index badb702e29..8e217e359f 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -114,8 +114,6 @@ private: TileSetAtlasSource *tile_set_atlas_source = nullptr; int tile_set_atlas_source_id = TileSet::INVALID_SOURCE; - Ref<EditorUndoRedoManager> undo_redo; - bool tile_set_changed_needs_update = false; // -- Properties painting -- diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index ef69326686..3b711a568e 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -35,6 +35,7 @@ #include "editor/editor_resource_preview.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/item_list.h" @@ -235,6 +236,7 @@ void TileSetScenesCollectionSourceEditor::_scenes_list_item_activated(int p_inde void TileSetScenesCollectionSourceEditor::_source_add_pressed() { int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add a Scene Tile")); undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", Ref<PackedScene>(), scene_id); undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); @@ -249,6 +251,7 @@ void TileSetScenesCollectionSourceEditor::_source_delete_pressed() { ERR_FAIL_COND(selected_indices.size() <= 0); int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove a Scene Tile")); undo_redo->add_do_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); undo_redo->add_undo_method(tile_set_scenes_collection_source, "create_scene_tile", tile_set_scenes_collection_source->get_scene_tile_scene(scene_id), scene_id); @@ -400,6 +403,7 @@ void TileSetScenesCollectionSourceEditor::_drop_data_fw(const Point2 &p_point, c Ref<PackedScene> resource = ResourceLoader::load(files[i]); if (resource.is_valid()) { int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add a Scene Tile")); undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", resource, scene_id); undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); @@ -453,8 +457,6 @@ void TileSetScenesCollectionSourceEditor::_bind_methods() { } TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { - undo_redo = EditorNode::get_undo_redo(); - // -- Right side -- HSplitContainer *split_container_right_side = memnew(HSplitContainer); split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL); @@ -479,7 +481,6 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { scenes_collection_source_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_scenes_collection_source_proxy_object_changed)); scenes_collection_source_inspector = memnew(EditorInspector); - scenes_collection_source_inspector->set_undo_redo(undo_redo); scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object); middle_vbox_container->add_child(scenes_collection_source_inspector); @@ -495,7 +496,6 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() { tile_proxy_object->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_update_action_buttons).unbind(1)); tile_inspector = memnew(EditorInspector); - tile_inspector->set_undo_redo(undo_redo); tile_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); tile_inspector->edit(tile_proxy_object); tile_inspector->set_use_folding(true); diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h index 0284b45c0f..7270cccbd8 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h @@ -37,8 +37,6 @@ #include "scene/gui/item_list.h" #include "scene/resources/tile_set.h" -class UndoRedo; - class TileSetScenesCollectionSourceEditor : public HBoxContainer { GDCLASS(TileSetScenesCollectionSourceEditor, HBoxContainer); @@ -97,8 +95,6 @@ private: TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; int tile_set_source_id = -1; - Ref<EditorUndoRedoManager> undo_redo; - bool tile_set_scenes_collection_source_changed_needs_update = false; // Source inspector. diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 336ce9e4c8..4fdfaed50e 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -430,7 +430,7 @@ void VersionControlEditorPlugin::_discard_file(String p_file_path, EditorVCSInte CHECK_PLUGIN_INITIALIZED(); EditorVCSInterface::get_singleton()->discard_file(p_file_path); } - // FIXIT: The project.godot file shows weird behaviour + // FIXIT: The project.godot file shows weird behavior EditorFileSystem::get_singleton()->update_file(p_file_path); } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 9b6ff894b9..c8f6a2431d 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -4062,7 +4062,7 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> p_input, bool type_changed = next_input_type != prev_input_type; - Ref<EditorUndoRedoManager> undo_redo_man = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo_man = EditorNode::get_undo_redo(); undo_redo_man->create_action(TTR("Visual Shader Input Type Changed")); undo_redo_man->add_do_method(p_input.ptr(), "set_input_name", p_name); @@ -4131,7 +4131,7 @@ void VisualShaderEditor::_parameter_ref_select_item(Ref<VisualShaderNodeParamete bool type_changed = p_parameter_ref->get_parameter_type_by_name(p_name) != p_parameter_ref->get_parameter_type_by_name(prev_name); - Ref<EditorUndoRedoManager> undo_redo_man = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo_man = EditorNode::get_undo_redo(); undo_redo_man->create_action(TTR("ParameterRef Name Changed")); undo_redo_man->add_do_method(p_parameter_ref.ptr(), "set_parameter_name", p_name); @@ -4175,7 +4175,7 @@ void VisualShaderEditor::_varying_select_item(Ref<VisualShaderNodeVarying> p_var bool is_getter = Ref<VisualShaderNodeVaryingGetter>(p_varying.ptr()).is_valid(); - Ref<EditorUndoRedoManager> undo_redo_man = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo_man = EditorNode::get_undo_redo(); undo_redo_man->create_action(TTR("Varying Name Changed")); undo_redo_man->add_do_method(p_varying.ptr(), "set_varying_name", p_name); @@ -5892,7 +5892,7 @@ public: return; } - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); updating = true; undo_redo->create_action(TTR("Edit Visual Property:") + " " + p_property, UndoRedo::MERGE_ENDS); @@ -6094,7 +6094,7 @@ void EditorPropertyVisualShaderMode::_option_selected(int p_which) { return; } - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Visual Shader Mode Changed")); //do is easy undo_redo->add_do_method(visual_shader.ptr(), "set_mode", p_which); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index f23b08409e..28111bed58 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -114,6 +114,7 @@ void ProjectSettingsEditor::_add_setting() { Variant value; Variant::construct(Variant::Type(type_box->get_selected_id()), value, nullptr, 0, ce); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add Project Setting")); undo_redo->add_do_property(ps, setting, value); undo_redo->add_undo_property(ps, setting, ps->has_setting(setting) ? ps->get(setting) : Variant()); @@ -133,6 +134,7 @@ void ProjectSettingsEditor::_delete_setting() { Variant value = ps->get(setting); int order = ps->get_order(setting); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Delete Item")); undo_redo->add_do_method(ps, "clear", setting); @@ -221,9 +223,9 @@ void ProjectSettingsEditor::_select_type(Variant::Type p_type) { void ProjectSettingsEditor::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); const Ref<InputEventKey> k = p_event; - if (k.is_valid() && k->is_pressed()) { bool handled = false; @@ -339,6 +341,7 @@ void ProjectSettingsEditor::_action_added(const String &p_name) { action["events"] = Array(); action["deadzone"] = 0.5f; + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add Input Action")); undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name); @@ -354,6 +357,7 @@ void ProjectSettingsEditor::_action_edited(const String &p_name, const Dictionar const String property_name = "input/" + p_name; Dictionary old_val = GLOBAL_GET(property_name); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); if (old_val["deadzone"] != p_action["deadzone"]) { // Deadzone Changed undo_redo->create_action(TTR("Change Action deadzone")); @@ -390,6 +394,7 @@ void ProjectSettingsEditor::_action_removed(const String &p_name) { Dictionary old_val = GLOBAL_GET(property_name); int order = ProjectSettings::get_singleton()->get_order(property_name); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Erase Input Action")); undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", property_name); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val); @@ -412,6 +417,7 @@ void ProjectSettingsEditor::_action_renamed(const String &p_old_name, const Stri int order = ProjectSettings::get_singleton()->get_order(old_property_name); Dictionary action = GLOBAL_GET(old_property_name); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Rename Input Action Event")); // Do: clear old, set new undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", old_property_name); @@ -441,6 +447,7 @@ void ProjectSettingsEditor::_action_reordered(const String &p_action_name, const HashMap<String, Variant> action_values; ProjectSettings::get_singleton()->get_property_list(&props); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Update Input Action Order")); for (const PropertyInfo &prop : props) { @@ -573,7 +580,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { set_title(TTR("Project Settings (project.godot)")); ps = ProjectSettings::get_singleton(); - undo_redo = p_data->get_undo_redo(); data = p_data; tab_container = memnew(TabContainer); @@ -632,7 +638,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { custom_properties->add_child(del_button); general_settings_inspector = memnew(SectionedInspector); - general_settings_inspector->get_inspector()->set_undo_redo(EditorNode::get_undo_redo()); general_settings_inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL); general_settings_inspector->register_search_box(search_box); general_settings_inspector->get_inspector()->set_use_filter(true); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index ec10c76cb7..7f6dd1b692 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -42,7 +42,6 @@ #include "editor/shader_globals_editor.h" #include "scene/gui/tab_container.h" -class EditorUndoRedoManager; class FileSystemDock; class ProjectSettingsEditor : public AcceptDialog { @@ -78,7 +77,6 @@ class ProjectSettingsEditor : public AcceptDialog { ImportDefaultsEditor *import_defaults_editor = nullptr; EditorData *data = nullptr; - Ref<EditorUndoRedoManager> undo_redo; void _advanced_toggled(bool p_button_pressed); void _update_advanced(bool p_is_advanced); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 6a935b4f7b..64ac38aaa5 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -42,6 +42,7 @@ #include "editor/editor_paths.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/multi_node_edit.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" @@ -267,7 +268,7 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) return; } - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Replace with Branch Scene")); Node *parent = base->get_parent(); @@ -3568,7 +3569,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec editor_selection->connect("selection_changed", callable_mp(this, &SceneTreeDock::_selection_changed)); - scene_tree->set_undo_redo(editor_data->get_undo_redo()); + scene_tree->set_as_scene_tree_dock(); scene_tree->set_editor_selection(editor_selection); create_dialog = memnew(CreateDialog); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index c8bc189106..46d3502a24 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -68,6 +68,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i Node *n = get_node(np); ERR_FAIL_COND(!n); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); if (p_id == BUTTON_SUBSCENE) { if (n == get_scene_node()) { if (n && n->get_scene_inherited_state().is_valid()) { @@ -167,6 +168,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i void SceneTreeEditor::_toggle_visible(Node *p_node) { if (p_node->has_method("is_visible") && p_node->has_method("set_visible")) { bool v = bool(p_node->call("is_visible")); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->add_do_method(p_node, "set_visible", !v); undo_redo->add_undo_method(p_node, "set_visible", v); } @@ -379,8 +381,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->set_tooltip_text(0, tooltip); } - if (can_open_instance && undo_redo.is_valid()) { //Show buttons only when necessary(SceneTreeDock) to avoid crashes - + if (can_open_instance && is_scene_tree_dock) { // Show buttons only when necessary (SceneTreeDock) to avoid crashes. if (!p_node->is_connected("script_changed", callable_mp(this, &SceneTreeEditor::_node_script_changed))) { p_node->connect("script_changed", callable_mp(this, &SceneTreeEditor::_node_script_changed).bind(p_node)); } @@ -997,11 +998,12 @@ void SceneTreeEditor::_renamed() { return; } - if (!undo_redo.is_valid()) { + if (!is_scene_tree_dock) { n->set_name(new_name); which->set_metadata(0, n->get_path()); emit_signal(SNAME("node_renamed")); } else { + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Rename Node")); emit_signal(SNAME("node_prerename"), n, new_name); undo_redo->add_do_method(this, "_rename_node", n->get_instance_id(), new_name); @@ -1045,8 +1047,8 @@ String SceneTreeEditor::get_filter_term_warning() { return filter_term_warning; } -void SceneTreeEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { - undo_redo = p_undo_redo; +void SceneTreeEditor::set_as_scene_tree_dock() { + is_scene_tree_dock = true; } void SceneTreeEditor::set_display_foreign_nodes(bool p_display) { diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index dcdfead885..33ef5dbcb3 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -37,8 +37,6 @@ #include "scene/gui/dialogs.h" #include "scene/gui/tree.h" -class EditorUndoRedoManager; - class SceneTreeEditor : public Control { GDCLASS(SceneTreeEditor, Control); @@ -98,9 +96,9 @@ class SceneTreeEditor : public Control { bool can_open_instance; bool updating_tree = false; bool show_enabled_subscene = false; + bool is_scene_tree_dock = false; void _renamed(); - Ref<EditorUndoRedoManager> undo_redo; HashSet<Node *> marked; bool marked_selectable = false; @@ -145,7 +143,7 @@ public: String get_filter() const; String get_filter_term_warning(); - void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); + void set_as_scene_tree_dock(); void set_display_foreign_nodes(bool p_display); void set_marked(const HashSet<Node *> &p_marked, bool p_selectable = false, bool p_children_selectable = true); diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index 6967536692..596b3eefba 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -86,7 +86,7 @@ protected: return false; } - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Set Shader Global Variable")); undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_set", p_name, p_value); diff --git a/main/main.cpp b/main/main.cpp index e77b2f1d8e..5b998d98ce 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -621,7 +621,7 @@ int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) { * - setup(execpath, argc, argv, p_second_phase) is the main entry point for all platforms, * responsible for the initialization of all low level singletons and core types, and parsing * command line arguments to configure things accordingly. - * If p_second_phase is true, it will chain into setup2() (default behaviour). This is + * If p_second_phase is true, it will chain into setup2() (default behavior). This is * disabled on some platforms (Android, iOS, UWP) which trigger the second step in their * own time. * diff --git a/main/main.h b/main/main.h index f0bfe69b34..1e8c4f288b 100644 --- a/main/main.h +++ b/main/main.h @@ -76,7 +76,7 @@ public: static void cleanup(bool p_force = false); }; -// Test main override is for the testing behaviour. +// Test main override is for the testing behavior. #define TEST_MAIN_OVERRIDE \ bool run_test = false; \ int return_code = Main::test_entrypoint(argc, argv, run_test); \ diff --git a/misc/scripts/codespell.sh b/misc/scripts/codespell.sh index 4cc01ec637..c00d897666 100755 --- a/misc/scripts/codespell.sh +++ b/misc/scripts/codespell.sh @@ -1,5 +1,5 @@ #!/bin/sh -SKIP_LIST="./thirdparty,*.gen.*,*.po,*.pot,package-lock.json,./core/string/locales.h,./DONORS.md,./misc/dist/linux/org.godotengine.Godot.desktop,./misc/scripts/codespell.sh" +SKIP_LIST="./.git,./bin,./thirdparty,*.gen.*,*.po,*.pot,package-lock.json,./core/string/locales.h,./DONORS.md,./misc/dist/linux/org.godotengine.Godot.desktop,./misc/scripts/codespell.sh" IGNORE_LIST="alo,ba,childs,complies,curvelinear,doubleclick,expct,fave,findn,gird,gud,inout,lod,nd,numer,ois,readded,ro,sav,statics,te,varius,varn,wan" codespell -w -q 3 -S "${SKIP_LIST}" -L "${IGNORE_LIST}" diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp index 31ae643b59..43c512ae16 100644 --- a/modules/enet/enet_multiplayer_peer.cpp +++ b/modules/enet/enet_multiplayer_peer.cpp @@ -159,10 +159,7 @@ void ENetMultiplayerPeer::_disconnect_inactive_peers() { if (hosts.has(P)) { hosts.erase(P); } - if (active_mode == MODE_CLIENT) { - ERR_CONTINUE(P != TARGET_PEER_SERVER); - emit_signal(SNAME("server_disconnected")); - } + ERR_CONTINUE(active_mode == MODE_CLIENT && P != TARGET_PEER_SERVER); emit_signal(SNAME("peer_disconnected"), P); } } @@ -186,14 +183,10 @@ void ENetMultiplayerPeer::poll() { if (ret == ENetConnection::EVENT_CONNECT) { connection_status = CONNECTION_CONNECTED; emit_signal(SNAME("peer_connected"), 1); - emit_signal(SNAME("connection_succeeded")); } else if (ret == ENetConnection::EVENT_DISCONNECT) { if (connection_status == CONNECTION_CONNECTED) { // Client just disconnected from server. - emit_signal(SNAME("server_disconnected")); emit_signal(SNAME("peer_disconnected"), 1); - } else { - emit_signal(SNAME("connection_failed")); } close(); } else if (ret == ENetConnection::EVENT_RECEIVE) { diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index cb148463a7..99803ed05d 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -6289,7 +6289,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state for (int32_t key_i = 0; key_i < key_count; key_i++) { Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i); - p_track.rotation_track.values.write[key_i] = Quaternion(rotation_radian); + p_track.rotation_track.values.write[key_i] = Quaternion::from_euler(rotation_radian); } } else if (path.contains(":scale")) { p_track.scale_track.times = times; diff --git a/modules/mbedtls/stream_peer_mbedtls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp index a97c6bd916..26cd3bc3f9 100644 --- a/modules/mbedtls/stream_peer_mbedtls.cpp +++ b/modules/mbedtls/stream_peer_mbedtls.cpp @@ -242,7 +242,7 @@ void StreamPeerMbedTLS::poll() { return; } - // We could pass nullptr as second parameter, but some behaviour sanitizers don't seem to like that. + // We could pass nullptr as second parameter, but some behavior sanitizers don't seem to like that. // Passing a 1 byte buffer to workaround it. uint8_t byte; int ret = mbedtls_ssl_read(tls_ctx->get_context(), &byte, 0); diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index db186079b0..63592042c7 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -6,7 +6,7 @@ <description> This is a generic mobile VR implementation where you need to provide details about the phone and HMD used. It does not rely on any existing framework. This is the most basic interface we have. For the best effect, you need a mobile phone with a gyroscope and accelerometer. Note that even though there is no positional tracking, the camera will assume the headset is at a height of 1.85 meters. You can change this by setting [member eye_height]. - You can initialise this interface as follows: + You can initialize this interface as follows: [codeblock] var interface = XRServer.find_interface("Native mobile") if interface and interface.initialize(): diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index b14f5f469c..582be98d61 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -155,7 +155,7 @@ void MobileVRInterface::set_position_from_sensors() { last_magnetometer_data = magneto; if (grav.length() < 0.1) { - // not ideal but use our accelerometer, this will contain shaky user behaviour + // not ideal but use our accelerometer, this will contain shaky user behavior // maybe look into some math but I'm guessing that if this isn't available, it's because we lack the gyro sensor to actually work out // what a stable gravity vector is grav = acc; diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp index 4df8af9009..dd35b3d164 100644 --- a/modules/mobile_vr/register_types.cpp +++ b/modules/mobile_vr/register_types.cpp @@ -53,7 +53,7 @@ void uninitialize_mobile_vr_module(ModuleInitializationLevel p_level) { } if (mobile_vr.is_valid()) { - // uninitialise our interface if it is initialised + // uninitialize our interface if it is initialized if (mobile_vr->is_initialized()) { mobile_vr->uninitialize(); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index a80963a37e..5dd629aeb0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -507,35 +507,6 @@ namespace Godot } /// <summary> - /// Constructs a <see cref="Quaternion"/> that will perform a rotation specified by - /// Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last), - /// given in the vector format as (X angle, Y angle, Z angle). - /// </summary> - /// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param> - public Quaternion(Vector3 eulerYXZ) - { - real_t halfA1 = eulerYXZ.y * 0.5f; - real_t halfA2 = eulerYXZ.x * 0.5f; - real_t halfA3 = eulerYXZ.z * 0.5f; - - // R = Y(a1).X(a2).Z(a3) convention for Euler angles. - // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) - // a3 is the angle of the first rotation, following the notation in this reference. - - real_t cosA1 = Mathf.Cos(halfA1); - real_t sinA1 = Mathf.Sin(halfA1); - real_t cosA2 = Mathf.Cos(halfA2); - real_t sinA2 = Mathf.Sin(halfA2); - real_t cosA3 = Mathf.Cos(halfA3); - real_t sinA3 = Mathf.Sin(halfA3); - - x = (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3); - y = (sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3); - z = (cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3); - w = (sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3); - } - - /// <summary> /// Constructs a <see cref="Quaternion"/> that will rotate around the given axis /// by the specified angle. The axis must be a normalized vector. /// </summary> @@ -597,6 +568,37 @@ namespace Godot } /// <summary> + /// Constructs a <see cref="Quaternion"/> that will perform a rotation specified by + /// Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last), + /// given in the vector format as (X angle, Y angle, Z angle). + /// </summary> + /// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param> + public static Quaternion FromEuler(Vector3 eulerYXZ) + { + real_t halfA1 = eulerYXZ.y * 0.5f; + real_t halfA2 = eulerYXZ.x * 0.5f; + real_t halfA3 = eulerYXZ.z * 0.5f; + + // R = Y(a1).X(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cosA1 = Mathf.Cos(halfA1); + real_t sinA1 = Mathf.Sin(halfA1); + real_t cosA2 = Mathf.Cos(halfA2); + real_t sinA2 = Mathf.Sin(halfA2); + real_t cosA3 = Mathf.Cos(halfA3); + real_t sinA3 = Mathf.Sin(halfA3); + + return new Quaternion( + (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3), + (sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3), + (cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3), + (sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3) + ); + } + + /// <summary> /// Composes these two quaternions by multiplying them together. /// This has the effect of rotating the second quaternion /// (the child) by the first quaternion (the parent). diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml index c0265c9161..a3ca2d6486 100644 --- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml +++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml @@ -6,7 +6,6 @@ <description> Spawnable scenes can be configured in the editor or through code (see [method add_spawnable_scene]). Also supports custom node spawns through [method spawn], calling [method _spawn_custom] on all peers. - Internally, [MultiplayerSpawner] uses [method MultiplayerAPI.object_configuration_add] to notify spawns passing the spawned node as the [code]object[/code] and itself as the [code]configuration[/code], and [method MultiplayerAPI.object_configuration_remove] to notify despawns in a similar way. </description> <tutorials> @@ -17,8 +16,7 @@ <param index="0" name="data" type="Variant" /> <description> Method called on all peers when a custom spawn was requested by the authority using [method spawn]. Should return a [Node] that is not in the scene tree. - - [b]Note:[/b] Spawned nodes should [b]not[/b] be added to the scene with `add_child`. This is done automatically. + [b]Note:[/b] Spawned nodes should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically. </description> </method> <method name="add_spawnable_scene"> @@ -52,7 +50,6 @@ <param index="0" name="data" type="Variant" default="null" /> <description> Requests a custom spawn, with [code]data[/code] passed to [method _spawn_custom] on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by [member spawn_path]. - [b]Note:[/b] Spawnable scenes are spawned automatically. [method spawn] is only needed for custom spawns. </description> </method> @@ -60,7 +57,6 @@ <members> <member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0"> Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns. - When set to [code]0[/code] (the default), there is no limit. </member> <member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath("")"> diff --git a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml index 42c190f504..7ed6255a62 100644 --- a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml +++ b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml @@ -6,9 +6,7 @@ <description> By default, [MultiplayerSynchronizer] synchronizes configured properties to all peers. Visibility can be handled directly with [method set_visibility_for] or as-needed with [method add_visibility_filter] and [method update_visibility]. - [MultiplayerSpawner]s will handle nodes according to visibility of synchronizers as long as the node at [member root_path] was spawned by one. - Internally, [MultiplayerSynchronizer] uses [method MultiplayerAPI.object_configuration_add] to notify synchronization start passing the [Node] at [member root_path] as the [code]object[/code] and itself as the [code]configuration[/code], and uses [method MultiplayerAPI.object_configuration_remove] to notify synchronization end in a similar way. </description> <tutorials> @@ -19,7 +17,6 @@ <param index="0" name="filter" type="Callable" /> <description> Adds a peer visibility filter for this synchronizer. - [code]filter[/code] should take a peer id [int] and return a [bool]. </description> </method> diff --git a/modules/multiplayer/doc_classes/SceneMultiplayer.xml b/modules/multiplayer/doc_classes/SceneMultiplayer.xml index a7b89f58ac..e4e2b4f631 100644 --- a/modules/multiplayer/doc_classes/SceneMultiplayer.xml +++ b/modules/multiplayer/doc_classes/SceneMultiplayer.xml @@ -19,6 +19,35 @@ Clears the current SceneMultiplayer network state (you shouldn't call this unless you know what you are doing). </description> </method> + <method name="complete_auth"> + <return type="int" enum="Error" /> + <param index="0" name="id" type="int" /> + <description> + Mark the authentication step as completed for the remote peer identified by [param id]. The [signal MultiplayerAPI.peer_connected] signal will be emitted for this peer once the remote side also completes the authentication. No further authentication messages are expected to be received from this peer. + If a peer disconnects before completing authentication, either due to a network issue, the [member auth_timeout] expiring, or manually calling [method disconnect_peer], the [signal peer_authentication_failed] signal will be emitted instead of [signal MultiplayerAPI.peer_disconnected]. + </description> + </method> + <method name="disconnect_peer"> + <return type="void" /> + <param index="0" name="id" type="int" /> + <description> + Disconnects the peer identified by [param id], removing it from the list of connected peers, and closing the underlying connection with it. + </description> + </method> + <method name="get_authenticating_peers"> + <return type="PackedInt32Array" /> + <description> + Returns the IDs of the peers currently trying to authenticate with this [MultiplayerAPI]. + </description> + </method> + <method name="send_auth"> + <return type="int" enum="Error" /> + <param index="0" name="id" type="int" /> + <param index="1" name="data" type="PackedByteArray" /> + <description> + Sends the specified [param data] to the remote peer identified by [param id] as part of an authentication message. This can be used to authenticate peers, and control when [signal MultiplayerAPI.peer_connected] is emitted (and the remote peer accepted as one of the connected peers). + </description> + </method> <method name="send_bytes"> <return type="int" enum="Error" /> <param index="0" name="bytes" type="PackedByteArray" /> @@ -35,6 +64,12 @@ If [code]true[/code], the MultiplayerAPI will allow encoding and decoding of object during RPCs. [b]Warning:[/b] Deserialized objects can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threat such as remote code execution. </member> + <member name="auth_callback" type="Callable" setter="set_auth_callback" getter="get_auth_callback"> + The callback to execute when when receiving authentication data sent via [method send_auth]. If the [Callable] is empty (default), peers will be automatically accepted as soon as they connect. + </member> + <member name="auth_timeout" type="float" setter="set_auth_timeout" getter="get_auth_timeout" default="3.0"> + If set to a value greater than [code]0.0[/code], the maximum amount of time peers can stay in the authenticating state, after which the authentication will automatically fail. See the [signal peer_authenticating] and [signal peer_authentication_failed] signals. + </member> <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" default="false"> If [code]true[/code], the MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] refuses new incoming connections. </member> @@ -48,6 +83,18 @@ </member> </members> <signals> + <signal name="peer_authenticating"> + <param index="0" name="id" type="int" /> + <description> + Emitted when this MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] connects to a new peer and a valid [member auth_callback] is set. In this case, the [signal MultiplayerAPI.peer_connected] will not be emitted until [method complete_auth] is called with given peer [param id]. While in this state, the peer will not be included in the list returned by [method MultiplayerAPI.get_peers] (but in the one returned by [method get_authenticating_peers]), and only authentication data will be sent or received. See [method send_auth] for sending authentication data. + </description> + </signal> + <signal name="peer_authentication_failed"> + <param index="0" name="id" type="int" /> + <description> + Emitted when this MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] disconnects from a peer for which authentication had not yet completed. See [signal peer_authenticating]. + </description> + </signal> <signal name="peer_packet"> <param index="0" name="id" type="int" /> <param index="1" name="packet" type="PackedByteArray" /> diff --git a/modules/multiplayer/editor/replication_editor_plugin.cpp b/modules/multiplayer/editor/replication_editor_plugin.cpp index f045018f25..aee5f5b483 100644 --- a/modules/multiplayer/editor/replication_editor_plugin.cpp +++ b/modules/multiplayer/editor/replication_editor_plugin.cpp @@ -355,7 +355,7 @@ void ReplicationEditor::_tree_item_edited() { int column = tree->get_edited_column(); ERR_FAIL_COND(column < 1 || column > 2); const NodePath prop = ti->get_metadata(0); - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); bool value = ti->is_checked(column); String method; if (column == 1) { @@ -395,7 +395,7 @@ void ReplicationEditor::_dialog_closed(bool p_confirmed) { int idx = config->property_get_index(prop); bool spawn = config->property_get_spawn(prop); bool sync = config->property_get_sync(prop); - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Remove Property")); undo_redo->add_do_method(config.ptr(), "remove_property", prop); undo_redo->add_undo_method(config.ptr(), "add_property", prop, idx); diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp index f94f8ef658..db7c5037cd 100644 --- a/modules/multiplayer/scene_multiplayer.cpp +++ b/modules/multiplayer/scene_multiplayer.cpp @@ -51,14 +51,32 @@ void SceneMultiplayer::profile_bandwidth(const String &p_inout, int p_size) { } #endif +void SceneMultiplayer::_update_status() { + MultiplayerPeer::ConnectionStatus status = multiplayer_peer.is_valid() ? multiplayer_peer->get_connection_status() : MultiplayerPeer::CONNECTION_DISCONNECTED; + if (last_connection_status != status) { + if (status == MultiplayerPeer::CONNECTION_DISCONNECTED) { + if (last_connection_status == MultiplayerPeer::CONNECTION_CONNECTING) { + emit_signal(SNAME("connection_failed")); + } else { + emit_signal(SNAME("server_disconnected")); + } + clear(); + } + last_connection_status = status; + } +} + Error SceneMultiplayer::poll() { - if (!multiplayer_peer.is_valid() || multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) { - return ERR_UNCONFIGURED; + _update_status(); + if (last_connection_status == MultiplayerPeer::CONNECTION_DISCONNECTED) { + return OK; } multiplayer_peer->poll(); - if (!multiplayer_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here. + _update_status(); + if (last_connection_status != MultiplayerPeer::CONNECTION_CONNECTED) { + // We might be still connecting, or polling might have resulted in a disconnection. return OK; } @@ -73,8 +91,48 @@ Error SceneMultiplayer::poll() { Error err = multiplayer_peer->get_packet(&packet, len); ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error getting packet! %d", err)); + if (pending_peers.has(sender)) { + if (pending_peers[sender].local) { + // If the auth is over, admit the peer at the first packet. + pending_peers.erase(sender); + _admit_peer(sender); + } else { + ERR_CONTINUE(len < 2 || (packet[0] & CMD_MASK) != NETWORK_COMMAND_SYS || packet[1] != SYS_COMMAND_AUTH); + // Auth message. + PackedByteArray pba; + pba.resize(len - 2); + if (pba.size()) { + memcpy(pba.ptrw(), &packet[2], len - 2); + // User callback + const Variant sv = sender; + const Variant pbav = pba; + const Variant *argv[2] = { &sv, &pbav }; + Variant ret; + Callable::CallError ce; + auth_callback.callp(argv, 2, ret, ce); + ERR_CONTINUE_MSG(ce.error != Callable::CallError::CALL_OK, "Failed to call authentication callback"); + } else { + // Remote complete notification. + pending_peers[sender].remote = true; + if (pending_peers[sender].local) { + pending_peers.erase(sender); + _admit_peer(sender); + } + } + continue; // Auth in progress. + } + } + + ERR_CONTINUE(!connected_peers.has(sender)); + if (len && (packet[0] & CMD_MASK) == NETWORK_COMMAND_SYS) { // Sys messages are processed separately since they might call _process_packet themselves. + if (len > 1 && packet[1] == SYS_COMMAND_AUTH) { + ERR_CONTINUE(len != 2); + // If we are here, we already admitted the peer locally, and this is just a confirmation packet. + continue; + } + _process_sys(sender, packet, len, mode, channel); } else { remote_sender_id = sender; @@ -82,17 +140,42 @@ Error SceneMultiplayer::poll() { remote_sender_id = 0; } - if (!multiplayer_peer.is_valid()) { - return OK; // It's also possible that a packet or RPC caused a disconnection, so also check here. + _update_status(); + if (last_connection_status != MultiplayerPeer::CONNECTION_CONNECTED) { // It's possible that processing a packet might have resulted in a disconnection, so check here. + return OK; + } + } + if (pending_peers.size() && auth_timeout) { + HashSet<int> to_drop; + uint64_t time = OS::get_singleton()->get_ticks_msec(); + for (const KeyValue<int, PendingPeer> &pending : pending_peers) { + if (pending.value.time + auth_timeout <= time) { + multiplayer_peer->disconnect_peer(pending.key); + to_drop.insert(pending.key); + } + } + for (const int &P : to_drop) { + // Each signal might trigger a disconnection. + pending_peers.erase(P); + emit_signal(SNAME("peer_authentication_failed"), P); } } + + _update_status(); + if (last_connection_status != MultiplayerPeer::CONNECTION_CONNECTED) { // Signals might have triggered disconnection. + return OK; + } + replicator->on_network_process(); return OK; } void SceneMultiplayer::clear() { + last_connection_status = MultiplayerPeer::CONNECTION_DISCONNECTED; + pending_peers.clear(); connected_peers.clear(); packet_cache.clear(); + replicator->on_reset(); cache->clear(); relay_buffer->clear(); } @@ -117,9 +200,6 @@ void SceneMultiplayer::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) if (multiplayer_peer.is_valid()) { multiplayer_peer->disconnect("peer_connected", callable_mp(this, &SceneMultiplayer::_add_peer)); multiplayer_peer->disconnect("peer_disconnected", callable_mp(this, &SceneMultiplayer::_del_peer)); - multiplayer_peer->disconnect("connection_succeeded", callable_mp(this, &SceneMultiplayer::_connected_to_server)); - multiplayer_peer->disconnect("connection_failed", callable_mp(this, &SceneMultiplayer::_connection_failed)); - multiplayer_peer->disconnect("server_disconnected", callable_mp(this, &SceneMultiplayer::_server_disconnected)); clear(); } @@ -128,11 +208,8 @@ void SceneMultiplayer::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) if (multiplayer_peer.is_valid()) { multiplayer_peer->connect("peer_connected", callable_mp(this, &SceneMultiplayer::_add_peer)); multiplayer_peer->connect("peer_disconnected", callable_mp(this, &SceneMultiplayer::_del_peer)); - multiplayer_peer->connect("connection_succeeded", callable_mp(this, &SceneMultiplayer::_connected_to_server)); - multiplayer_peer->connect("connection_failed", callable_mp(this, &SceneMultiplayer::_connection_failed)); - multiplayer_peer->connect("server_disconnected", callable_mp(this, &SceneMultiplayer::_server_disconnected)); } - replicator->on_reset(); + _update_status(); } Ref<MultiplayerPeer> SceneMultiplayer::get_multiplayer_peer() { @@ -193,18 +270,19 @@ Error SceneMultiplayer::send_command(int p_to, const uint8_t *p_packet, int p_pa const Vector<uint8_t> data = relay_buffer->get_data_array(); return multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position()); } - if (p_to < 0) { + if (p_to > 0) { + ERR_FAIL_COND_V(!connected_peers.has(p_to), ERR_BUG); + multiplayer_peer->set_target_peer(p_to); + return multiplayer_peer->put_packet(p_packet, p_packet_len); + } else { for (const int &pid : connected_peers) { - if (pid == -p_to) { + if (p_to && pid == -p_to) { continue; } multiplayer_peer->set_target_peer(pid); multiplayer_peer->put_packet(p_packet, p_packet_len); } return OK; - } else { - multiplayer_peer->set_target_peer(p_to); - return multiplayer_peer->put_packet(p_packet, p_packet_len); } } @@ -215,7 +293,7 @@ void SceneMultiplayer::_process_sys(int p_from, const uint8_t *p_packet, int p_p switch (sys_cmd_type) { case SYS_COMMAND_ADD_PEER: { ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1); - _add_peer(peer); + _admit_peer(peer); // Relayed peers are automatically accepted. } break; case SYS_COMMAND_DEL_PEER: { ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1); @@ -273,6 +351,17 @@ void SceneMultiplayer::_process_sys(int p_from, const uint8_t *p_packet, int p_p } void SceneMultiplayer::_add_peer(int p_id) { + if (auth_callback.is_valid()) { + pending_peers[p_id] = PendingPeer(); + pending_peers[p_id].time = OS::get_singleton()->get_ticks_msec(); + emit_signal(SNAME("peer_authenticating"), p_id); + return; + } else { + _admit_peer(p_id); + } +} + +void SceneMultiplayer::_admit_peer(int p_id) { if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) { // Notify others of connection, and send connected peers to newly connected one. uint8_t buf[SYS_CMD_SIZE]; @@ -295,10 +384,21 @@ void SceneMultiplayer::_add_peer(int p_id) { connected_peers.insert(p_id); cache->on_peer_change(p_id, true); replicator->on_peer_change(p_id, true); + if (p_id == 1) { + emit_signal(SNAME("connected_to_server")); + } emit_signal(SNAME("peer_connected"), p_id); } void SceneMultiplayer::_del_peer(int p_id) { + if (pending_peers.has(p_id)) { + pending_peers.erase(p_id); + emit_signal(SNAME("peer_authentication_failed"), p_id); + return; + } else if (!connected_peers.has(p_id)) { + return; + } + if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) { // Notify others of disconnection. uint8_t buf[SYS_CMD_SIZE]; @@ -322,17 +422,14 @@ void SceneMultiplayer::_del_peer(int p_id) { emit_signal(SNAME("peer_disconnected"), p_id); } -void SceneMultiplayer::_connected_to_server() { - emit_signal(SNAME("connected_to_server")); -} - -void SceneMultiplayer::_connection_failed() { - emit_signal(SNAME("connection_failed")); -} - -void SceneMultiplayer::_server_disconnected() { - replicator->on_reset(); - emit_signal(SNAME("server_disconnected")); +void SceneMultiplayer::disconnect_peer(int p_id) { + ERR_FAIL_COND(multiplayer_peer.is_null() || multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED); + if (pending_peers.has(p_id)) { + pending_peers.erase(p_id); + } else if (connected_peers.has(p_id)) { + connected_peers.has(p_id); + } + multiplayer_peer->disconnect_peer(p_id); } Error SceneMultiplayer::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode, int p_channel) { @@ -353,6 +450,61 @@ Error SceneMultiplayer::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer return send_command(p_to, packet_cache.ptr(), p_data.size() + 1); } +Error SceneMultiplayer::send_auth(int p_to, Vector<uint8_t> p_data) { + ERR_FAIL_COND_V(multiplayer_peer.is_null() || multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!pending_peers.has(p_to), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(pending_peers[p_to].local, ERR_FILE_CANT_WRITE, "The authentication session was previously marked as completed, no more authentication data can be sent."); + ERR_FAIL_COND_V_MSG(pending_peers[p_to].remote, ERR_FILE_CANT_WRITE, "The remote peer notified that the authentication session was completed, no more authentication data can be sent."); + + if (packet_cache.size() < p_data.size() + 2) { + packet_cache.resize(p_data.size() + 2); + } + + packet_cache.write[0] = NETWORK_COMMAND_SYS; + packet_cache.write[1] = SYS_COMMAND_AUTH; + memcpy(&packet_cache.write[2], p_data.ptr(), p_data.size()); + + multiplayer_peer->set_target_peer(p_to); + multiplayer_peer->set_transfer_channel(0); + multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); + return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 2); +} + +Error SceneMultiplayer::complete_auth(int p_peer) { + ERR_FAIL_COND_V(multiplayer_peer.is_null() || multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!pending_peers.has(p_peer), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(pending_peers[p_peer].local, ERR_FILE_CANT_WRITE, "The authentication session was already marked as completed."); + pending_peers[p_peer].local = true; + // Notify the remote peer that the authentication has completed. + uint8_t buf[2] = { NETWORK_COMMAND_SYS, SYS_COMMAND_AUTH }; + Error err = multiplayer_peer->put_packet(buf, 2); + // The remote peer already reported the authentication as completed, so admit the peer. + // May generate new packets, so it must happen after sending confirmation. + if (pending_peers[p_peer].remote) { + pending_peers.erase(p_peer); + _admit_peer(p_peer); + } + return err; +} + +void SceneMultiplayer::set_auth_callback(Callable p_callback) { + auth_callback = p_callback; +} + +Callable SceneMultiplayer::get_auth_callback() const { + return auth_callback; +} + +void SceneMultiplayer::set_auth_timeout(double p_timeout) { + ERR_FAIL_COND_MSG(p_timeout < 0, "Timeout must be greater or equal to 0 (where 0 means no timeout)"); + auth_timeout = uint64_t(p_timeout * 1000); +} + +double SceneMultiplayer::get_auth_timeout() const { + return double(auth_timeout) / 1000.0; +} + void SceneMultiplayer::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); @@ -392,6 +544,16 @@ Vector<int> SceneMultiplayer::get_peer_ids() { return ret; } +Vector<int> SceneMultiplayer::get_authenticating_peer_ids() { + Vector<int> out; + out.resize(pending_peers.size()); + int idx = 0; + for (const KeyValue<int, PendingPeer> &E : pending_peers) { + out.write[idx++] = E.key; + } + return out; +} + void SceneMultiplayer::set_allow_object_decoding(bool p_enable) { allow_object_decoding = p_enable; } @@ -453,6 +615,18 @@ void SceneMultiplayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_path", "path"), &SceneMultiplayer::set_root_path); ClassDB::bind_method(D_METHOD("get_root_path"), &SceneMultiplayer::get_root_path); ClassDB::bind_method(D_METHOD("clear"), &SceneMultiplayer::clear); + + ClassDB::bind_method(D_METHOD("disconnect_peer", "id"), &SceneMultiplayer::disconnect_peer); + + ClassDB::bind_method(D_METHOD("get_authenticating_peers"), &SceneMultiplayer::get_authenticating_peer_ids); + ClassDB::bind_method(D_METHOD("send_auth", "id", "data"), &SceneMultiplayer::send_auth); + ClassDB::bind_method(D_METHOD("complete_auth", "id"), &SceneMultiplayer::complete_auth); + + ClassDB::bind_method(D_METHOD("set_auth_callback", "callback"), &SceneMultiplayer::set_auth_callback); + ClassDB::bind_method(D_METHOD("get_auth_callback"), &SceneMultiplayer::get_auth_callback); + ClassDB::bind_method(D_METHOD("set_auth_timeout", "timeout"), &SceneMultiplayer::set_auth_timeout); + ClassDB::bind_method(D_METHOD("get_auth_timeout"), &SceneMultiplayer::get_auth_timeout); + ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "refuse"), &SceneMultiplayer::set_refuse_new_connections); ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &SceneMultiplayer::is_refusing_new_connections); ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &SceneMultiplayer::set_allow_object_decoding); @@ -462,12 +636,16 @@ void SceneMultiplayer::_bind_methods() { ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &SceneMultiplayer::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path"); + ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "auth_callback"), "set_auth_callback", "get_auth_callback"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auth_timeout", PROPERTY_HINT_RANGE, "0,30,0.1,or_greater,suffix:s"), "set_auth_timeout", "get_auth_timeout"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled"); ADD_PROPERTY_DEFAULT("refuse_new_connections", false); + ADD_SIGNAL(MethodInfo("peer_authenticating", PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("peer_authentication_failed", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet"))); } diff --git a/modules/multiplayer/scene_multiplayer.h b/modules/multiplayer/scene_multiplayer.h index 06d7b2c7a7..b0ecc48f8c 100644 --- a/modules/multiplayer/scene_multiplayer.h +++ b/modules/multiplayer/scene_multiplayer.h @@ -53,6 +53,7 @@ public: }; enum SysCommands { + SYS_COMMAND_AUTH, SYS_COMMAND_ADD_PEER, SYS_COMMAND_DEL_PEER, SYS_COMMAND_RELAY, @@ -76,7 +77,17 @@ public: }; private: + struct PendingPeer { + bool local = false; + bool remote = false; + uint64_t time = 0; + }; + Ref<MultiplayerPeer> multiplayer_peer; + MultiplayerPeer::ConnectionStatus last_connection_status = MultiplayerPeer::CONNECTION_DISCONNECTED; + HashMap<int, PendingPeer> pending_peers; // true if locally finalized. + Callable auth_callback; + uint64_t auth_timeout = 3000; HashSet<int> connected_peers; int remote_sender_id = 0; int remote_sender_override = 0; @@ -100,10 +111,9 @@ protected: void _process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel); void _add_peer(int p_id); + void _admit_peer(int p_id); void _del_peer(int p_id); - void _connected_to_server(); - void _connection_failed(); - void _server_disconnected(); + void _update_status(); public: virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) override; @@ -125,6 +135,16 @@ public: void set_root_path(const NodePath &p_path); NodePath get_root_path() const; + void disconnect_peer(int p_id); + + Error send_auth(int p_to, Vector<uint8_t> p_bytes); + Error complete_auth(int p_peer); + void set_auth_callback(Callable p_callback); + Callable get_auth_callback() const; + void set_auth_timeout(double p_timeout); + double get_auth_timeout() const; + Vector<int> get_authenticating_peer_ids(); + Error send_command(int p_to, const uint8_t *p_packet, int p_packet_len); // Used internally to relay packets when needed. Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE, int p_channel = 0); String get_rpc_md5(const Object *p_obj); diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp index 659ce7316a..f1bab7327a 100644 --- a/modules/multiplayer/scene_replication_interface.cpp +++ b/modules/multiplayer/scene_replication_interface.cpp @@ -325,7 +325,7 @@ Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const Obje // Check visibility for each peers. for (const KeyValue<int, PeerInfo> &E : peers_info) { if (is_visible) { - // This is fast, since the the object is visibile to everyone, we don't need to check each peer. + // This is fast, since the the object is visible to everyone, we don't need to check each peer. if (E.value.spawn_nodes.has(p_oid)) { // Already spawned. continue; diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h index ee454f604e..c8bd96eb87 100644 --- a/modules/multiplayer/scene_replication_interface.h +++ b/modules/multiplayer/scene_replication_interface.h @@ -75,7 +75,7 @@ private: HashSet<ObjectID> spawned_nodes; HashSet<ObjectID> sync_nodes; - // Pending spawn informations. + // Pending spawn information. ObjectID pending_spawn; int pending_spawn_remote = 0; const uint8_t *pending_buffer = nullptr; diff --git a/modules/openxr/doc_classes/OpenXRAction.xml b/modules/openxr/doc_classes/OpenXRAction.xml index d1a2ce2d2e..a3a45ebb4c 100644 --- a/modules/openxr/doc_classes/OpenXRAction.xml +++ b/modules/openxr/doc_classes/OpenXRAction.xml @@ -6,7 +6,7 @@ <description> This resource defines an OpenXR action. Actions can be used both for inputs (buttons/joystick/trigger/etc) and outputs (haptics). OpenXR performs automatic conversion between action type and input type whenever possible. An analogue trigger bound to a boolean action will thus return [code]false[/code] if the trigger is depressed and [code]true[/code] if pressed fully. - Actions are not directly bound to specific devices, instead OpenXR recognises a limited number of top level paths that identify devices by usage. We can restrict which devices an action can be bound to by these top level paths. For instance an action that should only be used for hand held controllers can have the top level paths "/user/hand/left" and "/user/hand/right" associated with them. See the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-reserved]reserved path section in the OpenXR specification[/url] for more info on the top level paths. + Actions are not directly bound to specific devices, instead OpenXR recognizes a limited number of top level paths that identify devices by usage. We can restrict which devices an action can be bound to by these top level paths. For instance an action that should only be used for hand held controllers can have the top level paths "/user/hand/left" and "/user/hand/right" associated with them. See the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-reserved]reserved path section in the OpenXR specification[/url] for more info on the top level paths. Note that the name of the resource is used to register the action with. </description> <tutorials> @@ -16,7 +16,7 @@ The type of action. </member> <member name="localized_name" type="String" setter="set_localized_name" getter="get_localized_name" default=""""> - The localised description of this action. + The localized description of this action. </member> <member name="toplevel_paths" type="PackedStringArray" setter="set_toplevel_paths" getter="get_toplevel_paths" default="PackedStringArray()"> A collections of toplevel paths to which this action can be bound. diff --git a/modules/openxr/doc_classes/OpenXRActionSet.xml b/modules/openxr/doc_classes/OpenXRActionSet.xml index db3259ec07..39e518750a 100644 --- a/modules/openxr/doc_classes/OpenXRActionSet.xml +++ b/modules/openxr/doc_classes/OpenXRActionSet.xml @@ -36,7 +36,7 @@ Collection of actions for this action set. </member> <member name="localized_name" type="String" setter="set_localized_name" getter="get_localized_name" default=""""> - The localised name of this action set. + The localized name of this action set. </member> <member name="priority" type="int" setter="set_priority" getter="get_priority" default="0"> The priority for this action set. diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index f089fd066e..7251a4a9bd 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -5,7 +5,7 @@ </brief_description> <description> The OpenXR interface allows Godot to interact with OpenXR runtimes and make it possible to create XR experiences and games. - Due to the needs of OpenXR this interface works slightly different than other plugin based XR interfaces. It needs to be initialised when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset. + Due to the needs of OpenXR this interface works slightly different than other plugin based XR interfaces. It needs to be initialized when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset. </description> <tutorials> <link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link> diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp index 4b30965ce5..85e2ee4903 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp @@ -102,7 +102,7 @@ void OpenXRHandTrackingExtension::on_state_ready() { // Setup our hands and reset data for (int i = 0; i < MAX_OPENXR_TRACKED_HANDS; i++) { // we'll do this later - hand_trackers[i].is_initialised = false; + hand_trackers[i].is_initialized = false; hand_trackers[i].hand_tracker = XR_NULL_HANDLE; hand_trackers[i].aimState.aimPose = { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } }; @@ -144,7 +144,7 @@ void OpenXRHandTrackingExtension::on_process() { if (XR_FAILED(result)) { // not successful? then we do nothing. print_line("OpenXR: Failed to obtain hand tracking information [", openxr_api->get_error_string(result), "]"); - hand_trackers[i].is_initialised = false; + hand_trackers[i].is_initialized = false; } else { void *next_pointer = nullptr; if (hand_tracking_aim_state_ext) { @@ -172,11 +172,11 @@ void OpenXRHandTrackingExtension::on_process() { hand_trackers[i].locations.jointCount = XR_HAND_JOINT_COUNT_EXT; hand_trackers[i].locations.jointLocations = hand_trackers[i].joint_locations; - hand_trackers[i].is_initialised = true; + hand_trackers[i].is_initialized = true; } } - if (hand_trackers[i].is_initialised) { + if (hand_trackers[i].is_initialized) { void *next_pointer = nullptr; XrHandJointsMotionRangeInfoEXT motionRangeInfo; @@ -240,7 +240,7 @@ void OpenXRHandTrackingExtension::cleanup_hand_tracking() { if (hand_trackers[i].hand_tracker != XR_NULL_HANDLE) { xrDestroyHandTrackerEXT(hand_trackers[i].hand_tracker); - hand_trackers[i].is_initialised = false; + hand_trackers[i].is_initialized = false; hand_trackers[i].hand_tracker = XR_NULL_HANDLE; } } diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.h b/modules/openxr/extensions/openxr_hand_tracking_extension.h index f8c26339b0..0eca80bcfb 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.h +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.h @@ -40,7 +40,7 @@ class OpenXRHandTrackingExtension : public OpenXRExtensionWrapper { public: struct HandTracker { - bool is_initialised = false; + bool is_initialized = false; XrHandJointsMotionRangeEXT motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT; XrHandTrackerEXT hand_tracker = XR_NULL_HANDLE; diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index bdf437b0b7..be6b7e4411 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -96,7 +96,7 @@ void OpenXRInterface::_load_action_map() { // This may seem a bit duplicitous to a little bit of background info here. // OpenXRActionMap (with all its sub resource classes) is a class that allows us to configure and store an action map in. - // This gives the user the ability to edit the action map in a UI and customise the actions. + // This gives the user the ability to edit the action map in a UI and customize the actions. // OpenXR however requires us to submit an action map and it takes over from that point and we can no longer change it. // This system does that push and we store the info needed to then work with this action map going forward. @@ -166,7 +166,7 @@ void OpenXRInterface::_load_action_map() { } } - // Only add our action if we have atleast one valid toplevel path + // Only add our action if we have at least one valid toplevel path if (trackers_for_action.size() > 0) { Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers_for_action); if (action) { @@ -355,7 +355,7 @@ OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_ Ref<XRPositionalTracker> positional_tracker; positional_tracker.instantiate(); - // We have standardised some names to make things nicer to the user so lets recognise the toplevel paths related to these. + // We have standardized some names to make things nicer to the user so lets recognize the toplevel paths related to these. if (p_tracker_name == "/user/hand/left") { positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); positional_tracker->set_tracker_name("left_hand"); diff --git a/modules/openxr/scene/openxr_hand.cpp b/modules/openxr/scene/openxr_hand.cpp index 2ae13a1026..588b818148 100644 --- a/modules/openxr/scene/openxr_hand.cpp +++ b/modules/openxr/scene/openxr_hand.cpp @@ -206,7 +206,7 @@ void OpenXRHand::_update_skeleton() { const OpenXRHandTrackingExtension::HandTracker *hand_tracker = hand_tracking_ext->get_hand_tracker(hand); const float ws = XRServer::get_singleton()->get_world_scale(); - if (hand_tracker->is_initialised && hand_tracker->locations.isActive) { + if (hand_tracker->is_initialized && hand_tracker->locations.isActive) { for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) { confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_NONE; quaternions[i] = Quaternion(); diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index 43dc3a65df..02260c837e 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -4,7 +4,7 @@ Class for searching text for patterns using regular expressions. </brief_description> <description> - A regular expression (or regex) is a compact language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For example, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet. + A regular expression (or regex) is a compact language that can be used to recognize strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For example, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet. To begin, the RegEx object needs to be compiled with the search pattern using [method compile] before it can be used. [codeblock] var regex = RegEx.new() diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml index d4054948f6..92e25efbe0 100644 --- a/modules/upnp/doc_classes/UPNP.xml +++ b/modules/upnp/doc_classes/UPNP.xml @@ -74,7 +74,7 @@ <param index="3" name="proto" type="String" default=""UDP"" /> <param index="4" name="duration" type="int" default="0" /> <description> - Adds a mapping to forward the external [code]port[/code] (between 1 and 65535, although recommended to use port 1024 or above) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]TCP[/code] or [code]UDP[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any. Note that forwarding a well-known port (below 1024) with UPnP may fail depending on the device. + Adds a mapping to forward the external [code]port[/code] (between 1 and 65535, although recommended to use port 1024 or above) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]"TCP"[/code] or [code]"UDP"[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any. Note that forwarding a well-known port (below 1024) with UPnP may fail depending on the device. Depending on the gateway device, if a mapping for that port already exists, it will either be updated or it will refuse this command due to that conflict, especially if the existing mapping for that port wasn't created via UPnP or points to a different network address (or device) than this one. If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value). The description ([code]desc[/code]) is shown in some routers management UIs and can be used to point out which application added the mapping. @@ -93,7 +93,7 @@ <param index="0" name="port" type="int" /> <param index="1" name="proto" type="String" default=""UDP"" /> <description> - Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]TCP[/code] or [code]UDP[/code]. May be refused for mappings pointing to addresses other than this one, for well-known ports (below 1024), or for mappings not added via UPnP. See [enum UPNPResult] for possible return values. + Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]"TCP"[/code] or [code]"UDP"[/code]. May be refused for mappings pointing to addresses other than this one, for well-known ports (below 1024), or for mappings not added via UPnP. See [enum UPNPResult] for possible return values. </description> </method> <method name="discover"> diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml index 91ee65e9bd..5266a36637 100644 --- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml +++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml @@ -6,7 +6,7 @@ <description> This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.multiplayer_peer]. You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections. - [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless the peer is created using [method create_client]. Beside that data transfer works like in a [MultiplayerPeer]. + When creating the peer via [method create_client] or [method create_server] the [method MultiplayerPeer.is_server_relay_supported] method will return [code]true[/code] enabling peer exchange and packet relaying when supported by the [MultiplayerAPI] implementation. [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp index 163b17fa6f..38c33a2dbc 100644 --- a/modules/webrtc/webrtc_multiplayer_peer.cpp +++ b/modules/webrtc/webrtc_multiplayer_peer.cpp @@ -341,11 +341,6 @@ void WebRTCMultiplayerPeer::remove_peer(int p_peer_id) { peer->connected = false; emit_signal(SNAME("peer_disconnected"), p_peer_id); if (network_mode == MODE_CLIENT && p_peer_id == TARGET_PEER_SERVER) { - if (connection_status == CONNECTION_CONNECTING) { - emit_signal(SNAME("connection_failed")); - } else { - emit_signal(SNAME("server_disconnected")); - } connection_status = CONNECTION_DISCONNECTED; } } diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index fe0aae412e..41d166a0f5 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -133,7 +133,7 @@ <return type="int" enum="Error" /> <param index="0" name="message" type="String" /> <description> - Sends the given [param message] using WebSocket text mode. Perfer this method over [method PacketPeer.put_packet] when interacting with third-party text-based API (e.g. when using [JSON] formatted messages). + Sends the given [param message] using WebSocket text mode. Prefer this method over [method PacketPeer.put_packet] when interacting with third-party text-based API (e.g. when using [JSON] formatted messages). </description> </method> <method name="set_no_delay"> diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index 827c618e4e..14f9c0ba4d 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -264,9 +264,7 @@ void WebSocketMultiplayerPeer::_poll_client() { } } else if (peer->get_ready_state() == WebSocketPeer::STATE_CLOSED) { if (connection_status == CONNECTION_CONNECTED) { - emit_signal(SNAME("server_disconnected")); - } else { - emit_signal(SNAME("connection_failed")); + emit_signal(SNAME("peer_disconnected"), 1); } _clear(); return; @@ -276,7 +274,6 @@ void WebSocketMultiplayerPeer::_poll_client() { ERR_FAIL_COND(!pending_peers.has(1)); // Bug. if (OS::get_singleton()->get_ticks_msec() - pending_peers[1].time > handshake_timeout) { print_verbose(vformat("WebSocket handshake timed out after %.3f seconds.", handshake_timeout * 0.001)); - emit_signal(SNAME("connection_failed")); _clear(); return; } diff --git a/modules/webxr/register_types.cpp b/modules/webxr/register_types.cpp index f4959c482f..8d30f4bd8c 100644 --- a/modules/webxr/register_types.cpp +++ b/modules/webxr/register_types.cpp @@ -57,7 +57,7 @@ void uninitialize_webxr_module(ModuleInitializationLevel p_level) { #ifdef WEB_ENABLED if (webxr.is_valid()) { - // uninitialise our interface if it is initialised + // uninitialize our interface if it is initialized if (webxr->is_initialized()) { webxr->uninitialize(); } diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index a674498620..b6b94d2f5e 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -396,7 +396,14 @@ void OS_IOS::vibrate_handheld(int p_duration_ms) { } bool OS_IOS::_check_internal_feature_support(const String &p_feature) { - return p_feature == "mobile"; + if (p_feature == "system_fonts") { + return true; + } + if (p_feature == "mobile") { + return true; + } + + return false; } void OS_IOS::on_focus_out() { diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index 11b667fcef..db9b3ed77f 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -481,7 +481,16 @@ Error OS_LinuxBSD::shell_open(String p_uri) { } bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; +#ifdef FONTCONFIG_ENABLED + if (p_feature == "system_fonts") { + return font_config_initialized; + } +#endif + if (p_feature == "pc") { + return true; + } + + return false; } uint64_t OS_LinuxBSD::get_embedded_pck_offset() const { diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index 8ffb0abfdb..e620b058d3 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -499,7 +499,14 @@ String OS_MacOS::get_unique_id() const { } bool OS_MacOS::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; + if (p_feature == "system_fonts") { + return true; + } + if (p_feature == "pc") { + return true; + } + + return false; } void OS_MacOS::disable_crash_handler() { diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index 10ed8b8343..2c1e87e663 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -212,7 +212,7 @@ int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double void DisplayServerWeb::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) { MouseButton input_mask = Input::get_singleton()->get_mouse_button_mask(); // For motion outside the canvas, only read mouse movement if dragging - // started inside the canvas; imitating desktop app behaviour. + // started inside the canvas; imitating desktop app behavior. if (!get_singleton()->cursor_inside_canvas && input_mask == MouseButton::NONE) { return; } diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index d039fd13a7..2b5c8cad48 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -167,7 +167,7 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { const GUID &guid = instance->guidProduct; char uid[128]; - ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognised."); + ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognized."); WORD type = BSWAP16(0x03); WORD vendor = BSWAP16(LOWORD(guid.Data1)); WORD product = BSWAP16(HIWORD(guid.Data1)); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index d95a88fac1..36fd86c4f5 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1181,7 +1181,14 @@ String OS_Windows::get_unique_id() const { } bool OS_Windows::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; + if (p_feature == "system_fonts") { + return true; + } + if (p_feature == "pc") { + return true; + } + + return false; } void OS_Windows::disable_crash_handler() { diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index c1044fdf5b..b5945a4562 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -175,51 +175,18 @@ void PathFollow2D::_update_transform() { if (path_length == 0) { return; } - Vector2 pos = c->sample_baked(progress, cubic); if (rotates) { - real_t ahead = progress + lookahead; - - if (loop && ahead >= path_length) { - // If our lookahead will loop, we need to check if the path is closed. - int point_count = c->get_point_count(); - if (point_count > 0) { - Vector2 start_point = c->get_point_position(0); - Vector2 end_point = c->get_point_position(point_count - 1); - if (start_point == end_point) { - // Since the path is closed we want to 'smooth off' - // the corner at the start/end. - // So we wrap the lookahead back round. - ahead = Math::fmod(ahead, path_length); - } - } - } - - Vector2 ahead_pos = c->sample_baked(ahead, cubic); - - Vector2 tangent_to_curve; - if (ahead_pos == pos) { - // This will happen at the end of non-looping or non-closed paths. - // We'll try a look behind instead, in order to get a meaningful angle. - tangent_to_curve = - (pos - c->sample_baked(progress - lookahead, cubic)).normalized(); - } else { - tangent_to_curve = (ahead_pos - pos).normalized(); - } - - Vector2 normal_of_curve = -tangent_to_curve.orthogonal(); - - pos += tangent_to_curve * h_offset; - pos += normal_of_curve * v_offset; - - set_rotation(tangent_to_curve.angle()); - + Transform2D xform = c->sample_baked_with_rotation(progress, cubic, loop, lookahead); + xform.translate_local(v_offset, h_offset); + set_rotation(xform[1].angle()); + set_position(xform[2]); } else { + Vector2 pos = c->sample_baked(progress, cubic); pos.x += h_offset; pos.y += v_offset; + set_position(pos); } - - set_position(pos); } void PathFollow2D::_notification(int p_what) { diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp index 6222b0db14..4c2e9e4c19 100644 --- a/scene/2d/shape_cast_2d.cpp +++ b/scene/2d/shape_cast_2d.cpp @@ -107,6 +107,11 @@ Object *ShapeCast2D::get_collider(int p_idx) const { return ObjectDB::get_instance(result[p_idx].collider_id); } +RID ShapeCast2D::get_collider_rid(int p_idx) const { + ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), RID(), "No collider RID found."); + return result[p_idx].rid; +} + int ShapeCast2D::get_collider_shape(int p_idx) const { ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), -1, "No collider shape found."); return result[p_idx].shape; @@ -422,6 +427,7 @@ void ShapeCast2D::_bind_methods() { ClassDB::bind_method(D_METHOD("force_shapecast_update"), &ShapeCast2D::force_shapecast_update); ClassDB::bind_method(D_METHOD("get_collider", "index"), &ShapeCast2D::get_collider); + ClassDB::bind_method(D_METHOD("get_collider_rid", "index"), &ShapeCast2D::get_collider_rid); ClassDB::bind_method(D_METHOD("get_collider_shape", "index"), &ShapeCast2D::get_collider_shape); ClassDB::bind_method(D_METHOD("get_collision_point", "index"), &ShapeCast2D::get_collision_point); ClassDB::bind_method(D_METHOD("get_collision_normal", "index"), &ShapeCast2D::get_collision_normal); diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h index 7b55566b01..134f236d3b 100644 --- a/scene/2d/shape_cast_2d.h +++ b/scene/2d/shape_cast_2d.h @@ -104,6 +104,7 @@ public: int get_collision_count() const; Object *get_collider(int p_idx) const; + RID get_collider_rid(int p_idx) const; int get_collider_shape(int p_idx) const; Vector2 get_collision_point(int p_idx) const; Vector2 get_collision_normal(int p_idx) const; diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp index 03cbd984cd..267139c7d0 100644 --- a/scene/3d/shape_cast_3d.cpp +++ b/scene/3d/shape_cast_3d.cpp @@ -115,6 +115,7 @@ void ShapeCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("force_shapecast_update"), &ShapeCast3D::force_shapecast_update); ClassDB::bind_method(D_METHOD("get_collider", "index"), &ShapeCast3D::get_collider); + ClassDB::bind_method(D_METHOD("get_collider_rid", "index"), &ShapeCast3D::get_collider_rid); ClassDB::bind_method(D_METHOD("get_collider_shape", "index"), &ShapeCast3D::get_collider_shape); ClassDB::bind_method(D_METHOD("get_collision_point", "index"), &ShapeCast3D::get_collision_point); ClassDB::bind_method(D_METHOD("get_collision_normal", "index"), &ShapeCast3D::get_collision_normal); @@ -282,6 +283,11 @@ Object *ShapeCast3D::get_collider(int p_idx) const { return ObjectDB::get_instance(result[p_idx].collider_id); } +RID ShapeCast3D::get_collider_rid(int p_idx) const { + ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), RID(), "No collider RID found."); + return result[p_idx].rid; +} + int ShapeCast3D::get_collider_shape(int p_idx) const { ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), -1, "No collider shape found."); return result[p_idx].shape; diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h index 2526d8d32c..abe10c9b24 100644 --- a/scene/3d/shape_cast_3d.h +++ b/scene/3d/shape_cast_3d.h @@ -120,6 +120,7 @@ public: int get_collision_count() const; Object *get_collider(int p_idx) const; + RID get_collider_rid(int p_idx) const; int get_collider_shape(int p_idx) const; Vector3 get_collision_point(int p_idx) const; Vector3 get_collision_normal(int p_idx) const; diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp index b0d15aa7f4..44c5ec62f8 100644 --- a/scene/gui/flow_container.cpp +++ b/scene/gui/flow_container.cpp @@ -152,6 +152,28 @@ void FlowContainer::_resort() { line_data = lines_data[current_line_idx]; } + // The first child of each line adds the offset caused by the alignment, + // but only if the line doesn't contain a child that expands. + if (child_idx_in_line == 0 && Math::is_equal_approx(line_data.stretch_ratio_total, 0)) { + int alignment_ofs = 0; + switch (alignment) { + case ALIGNMENT_CENTER: + alignment_ofs = line_data.stretch_avail / 2; + break; + case ALIGNMENT_END: + alignment_ofs = line_data.stretch_avail; + break; + default: + break; + } + + if (vertical) { /* VERTICAL */ + ofs.y += alignment_ofs; + } else { /* HORIZONTAL */ + ofs.x += alignment_ofs; + } + } + if (vertical) { /* VERTICAL */ if (child->get_h_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) { child_size.width = line_data.min_line_height; @@ -282,6 +304,18 @@ int FlowContainer::get_line_count() const { return cached_line_count; } +void FlowContainer::set_alignment(AlignmentMode p_alignment) { + if (alignment == p_alignment) { + return; + } + alignment = p_alignment; + _resort(); +} + +FlowContainer::AlignmentMode FlowContainer::get_alignment() const { + return alignment; +} + void FlowContainer::set_vertical(bool p_vertical) { ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); vertical = p_vertical; @@ -300,8 +334,15 @@ FlowContainer::FlowContainer(bool p_vertical) { void FlowContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count); + ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &FlowContainer::set_alignment); + ClassDB::bind_method(D_METHOD("get_alignment"), &FlowContainer::get_alignment); ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &FlowContainer::set_vertical); ClassDB::bind_method(D_METHOD("is_vertical"), &FlowContainer::is_vertical); + BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN); + BIND_ENUM_CONSTANT(ALIGNMENT_CENTER); + BIND_ENUM_CONSTANT(ALIGNMENT_END); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h index 536df27ad6..6a61e9b904 100644 --- a/scene/gui/flow_container.h +++ b/scene/gui/flow_container.h @@ -36,11 +36,19 @@ class FlowContainer : public Container { GDCLASS(FlowContainer, Container); +public: + enum AlignmentMode { + ALIGNMENT_BEGIN, + ALIGNMENT_CENTER, + ALIGNMENT_END + }; + private: int cached_size = 0; int cached_line_count = 0; bool vertical = false; + AlignmentMode alignment = ALIGNMENT_BEGIN; struct ThemeCache { int h_separation = 0; @@ -61,6 +69,9 @@ protected: public: int get_line_count() const; + void set_alignment(AlignmentMode p_alignment); + AlignmentMode get_alignment() const; + void set_vertical(bool p_vertical); bool is_vertical() const; @@ -88,4 +99,6 @@ public: FlowContainer(true) { is_fixed = true; } }; +VARIANT_ENUM_CAST(FlowContainer::AlignmentMode); + #endif // FLOW_CONTAINER_H diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index cc9fc99b9c..1b8444abf4 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -833,7 +833,7 @@ void TextEdit::_notification(int p_what) { continue; } - // If we've changed colour we are at the start of a new section, therefore we need to go back to the end + // If we've changed color we are at the start of a new section, therefore we need to go back to the end // of the previous section to draw it, we'll also add the character back on. if (color != previous_color) { characters--; @@ -2051,7 +2051,8 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } if (is_shortcut_keys_enabled()) { - // SELECT ALL, SELECT WORD UNDER CARET, ADD SELECTION FOR NEXT OCCURRENCE, CUT, COPY, PASTE. + // SELECT ALL, SELECT WORD UNDER CARET, ADD SELECTION FOR NEXT OCCURRENCE, + // CLEAR CARETS AND SELECTIONS, CUT, COPY, PASTE. if (k->is_action("ui_text_select_all", true)) { select_all(); accept_event(); @@ -2067,10 +2068,12 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { accept_event(); return; } - if (k->is_action("ui_text_remove_secondary_carets", true) && _should_remove_secondary_carets()) { - remove_secondary_carets(); - accept_event(); - return; + if (k->is_action("ui_text_clear_carets_and_selection", true)) { + // Since the default shortcut is ESC, accepts the event only if it's actually performed. + if (_clear_carets_and_selection()) { + accept_event(); + return; + } } if (k->is_action("ui_cut", true)) { cut(); @@ -2656,7 +2659,7 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { set_caret_line(get_caret_line(caret_idx), false, true, 0, caret_idx); set_caret_column(column, caret_idx == 0, caret_idx); - // Now we can clean up the overlaping caret. + // Now we can clean up the overlapping caret. if (overlapping_caret_index != -1) { backspace(overlapping_caret_index); i++; @@ -2824,8 +2827,18 @@ void TextEdit::_move_caret_document_end(bool p_select) { } } -bool TextEdit::_should_remove_secondary_carets() { - return carets.size() > 1; +bool TextEdit::_clear_carets_and_selection() { + if (get_caret_count() > 1) { + remove_secondary_carets(); + return true; + } + + if (has_selection()) { + deselect(); + return true; + } + + return false; } 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 { @@ -6547,7 +6560,7 @@ void TextEdit::_cut_internal(int p_caret) { int indent_level = get_indent_level(cl); double hscroll = get_h_scroll(); - // Check for overlaping carets. + // Check for overlapping carets. // We don't need to worry about selections as that is caught before this entire section. for (int j = i - 1; j >= 0; j--) { if (get_caret_line(caret_edit_order[j]) == cl) { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 263a36aba8..92acbaf521 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -597,7 +597,7 @@ private: void _delete(bool p_word = false, bool p_all_to_right = false); void _move_caret_document_start(bool p_select); void _move_caret_document_end(bool p_select); - bool _should_remove_secondary_carets(); + bool _clear_carets_and_selection(); // 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; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 3bf4e95e68..7bcd4721fc 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -1112,7 +1112,7 @@ uint32_t CanvasItem::get_visibility_layer() const { } void CanvasItem::set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_enable) { - ERR_FAIL_INDEX(p_visibility_layer, 32); + ERR_FAIL_UNSIGNED_INDEX(p_visibility_layer, 32); if (p_enable) { set_visibility_layer(visibility_layer | (1 << p_visibility_layer)); } else { @@ -1121,7 +1121,7 @@ void CanvasItem::set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_en } bool CanvasItem::get_visibility_layer_bit(uint32_t p_visibility_layer) const { - ERR_FAIL_INDEX_V(p_visibility_layer, 32, false); + ERR_FAIL_UNSIGNED_INDEX_V(p_visibility_layer, 32, false); return (visibility_layer & (1 << p_visibility_layer)); } diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp index b4e5b11abd..92ba3debd1 100644 --- a/scene/main/multiplayer_peer.cpp +++ b/scene/main/multiplayer_peer.cpp @@ -123,9 +123,6 @@ void MultiplayerPeer::_bind_methods() { ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id"))); - ADD_SIGNAL(MethodInfo("server_disconnected")); - ADD_SIGNAL(MethodInfo("connection_succeeded")); - ADD_SIGNAL(MethodInfo("connection_failed")); } /*************/ diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 2385e4f54a..f8b0a66a71 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3255,7 +3255,7 @@ uint32_t Viewport::get_canvas_cull_mask() const { } void Viewport::set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable) { - ERR_FAIL_INDEX(p_layer, 32); + ERR_FAIL_UNSIGNED_INDEX(p_layer, 32); if (p_enable) { set_canvas_cull_mask(canvas_cull_mask | (1 << p_layer)); } else { @@ -3264,7 +3264,7 @@ void Viewport::set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable) { } bool Viewport::get_canvas_cull_mask_bit(uint32_t p_layer) const { - ERR_FAIL_INDEX_V(p_layer, 32, false); + ERR_FAIL_UNSIGNED_INDEX_V(p_layer, 32, false); return (canvas_cull_mask & (1 << p_layer)); } diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index e536aeee51..0f55c7b3b8 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -1184,7 +1184,7 @@ void unregister_scene_types() { ResourceLoader::remove_resource_format_loader(resource_loader_shader_include); resource_loader_shader_include.unref(); - // StandardMaterial3D is not initialised when 3D is disabled, so it shouldn't be cleaned up either + // StandardMaterial3D is not initialized when 3D is disabled, so it shouldn't be cleaned up either #ifndef _3D_DISABLED BaseMaterial3D::finish_shaders(); PhysicalSkyMaterial::cleanup_shader(); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 49b78a091d..eda9af9dde 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -936,6 +936,46 @@ Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const { } } +Transform2D Curve2D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, bool p_loop, real_t p_lookahead) const { + real_t path_length = get_baked_length(); // Ensure baked. + ERR_FAIL_COND_V_MSG(path_length == 0, Transform2D(), "Length of Curve2D is 0."); + + Vector2 pos = sample_baked(p_offset, p_cubic); + + real_t ahead = p_offset + p_lookahead; + + if (p_loop && ahead >= path_length) { + // If our lookahead will loop, we need to check if the path is closed. + int point_count = get_point_count(); + if (point_count > 0) { + Vector2 start_point = get_point_position(0); + Vector2 end_point = get_point_position(point_count - 1); + if (start_point == end_point) { + // Since the path is closed we want to 'smooth off' + // the corner at the start/end. + // So we wrap the lookahead back round. + ahead = Math::fmod(ahead, path_length); + } + } + } + + Vector2 ahead_pos = sample_baked(ahead, p_cubic); + + Vector2 tangent_to_curve; + if (ahead_pos == pos) { + // This will happen at the end of non-looping or non-closed paths. + // We'll try a look behind instead, in order to get a meaningful angle. + tangent_to_curve = + (pos - sample_baked(p_offset - p_lookahead, p_cubic)).normalized(); + } else { + tangent_to_curve = (ahead_pos - pos).normalized(); + } + + Vector2 normal_of_curve = -tangent_to_curve.orthogonal(); + + return Transform2D(normal_of_curve, tangent_to_curve, pos); +} + PackedVector2Array Curve2D::get_baked_points() const { if (baked_cache_dirty) { _bake(); @@ -1184,6 +1224,7 @@ void Curve2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length); ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve2D::sample_baked, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked_with_rotation", "offset", "cubic", "loop", "lookahead"), &Curve2D::sample_baked_with_rotation, DEFVAL(false), DEFVAL(true), DEFVAL(4.0)); ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points); ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point); ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset); diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 88b6dda096..fa1d35aab1 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -216,6 +216,7 @@ public: real_t get_baked_length() const; Vector2 sample_baked(real_t p_offset, bool p_cubic = false) const; + Transform2D sample_baked_with_rotation(real_t p_offset, bool p_cubic = false, bool p_loop = true, real_t p_lookahead = 4.0) const; PackedVector2Array get_baked_points() const; //useful for going through Vector2 get_closest_point(const Vector2 &p_to_point) const; real_t get_closest_offset(const Vector2 &p_to_point) const; diff --git a/servers/audio/effects/audio_effect_delay.cpp b/servers/audio/effects/audio_effect_delay.cpp index f71ff05b23..69c62e3a13 100644 --- a/servers/audio/effects/audio_effect_delay.cpp +++ b/servers/audio/effects/audio_effect_delay.cpp @@ -94,7 +94,7 @@ void AudioEffectDelayInstance::_process_chunk(const AudioFrame *p_src_frames, Au //apply lowpass and feedback gain AudioFrame fb_in = out * feedback_level_f * lpf_ic + h * lpf_c; - fb_in.undenormalise(); //avoid denormals + fb_in.undenormalize(); //avoid denormals h = fb_in; fb_buf[feedback_buffer_pos] = fb_in; diff --git a/servers/audio/effects/audio_effect_distortion.cpp b/servers/audio/effects/audio_effect_distortion.cpp index 5987ed7bb2..95156d8d66 100644 --- a/servers/audio/effects/audio_effect_distortion.cpp +++ b/servers/audio/effects/audio_effect_distortion.cpp @@ -50,7 +50,7 @@ void AudioEffectDistortionInstance::process(const AudioFrame *p_src_frames, Audi float lofi_mult = powf(2.0, 2.0 + (1.0 - drive_f) * 14); //goes from 16 to 2 bits for (int i = 0; i < p_frame_count * 2; i++) { - float out = undenormalise(src[i] * lpf_ic + lpf_c * h[i & 1]); + float out = undenormalize(src[i] * lpf_ic + lpf_c * h[i & 1]); h[i & 1] = out; float a = out; float ha = src[i] - out; //high freqs diff --git a/servers/audio/effects/reverb_filter.cpp b/servers/audio/effects/reverb_filter.cpp index 0363706714..2b60b73b7e 100644 --- a/servers/audio/effects/reverb_filter.cpp +++ b/servers/audio/effects/reverb_filter.cpp @@ -77,7 +77,7 @@ void Reverb::process(float *p_src, float *p_dst, int p_frames) { read_pos += echo_buffer_size; } - float in = undenormalise(echo_buffer[read_pos] * params.predelay_fb + p_src[i]); + float in = undenormalize(echo_buffer[read_pos] * params.predelay_fb + p_src[i]); echo_buffer[echo_buffer_pos] = in; @@ -111,7 +111,7 @@ void Reverb::process(float *p_src, float *p_dst, int p_frames) { c.pos = 0; } - float out = undenormalise(c.buffer[c.pos] * c.feedback); + float out = undenormalize(c.buffer[c.pos] * c.feedback); out = out * (1.0 - c.damp) + c.damp_h * c.damp; //lowpass c.damp_h = out; c.buffer[c.pos] = input_buffer[j] + out; @@ -138,7 +138,7 @@ void Reverb::process(float *p_src, float *p_dst, int p_frames) { ap=&allpass[m_ap]; \ if (ap->pos>=ap_size_limit[m_ap]) \ ap->pos=0; \ - aux=undenormalise(ap->buffer[ap->pos]); \ + aux=undenormalize(ap->buffer[ap->pos]); \ in=sample; \ sample=-in+aux; \ ap->pos++; @@ -163,7 +163,7 @@ void Reverb::process(float *p_src, float *p_dst, int p_frames) { } float aux = a.buffer[a.pos]; - a.buffer[a.pos] = undenormalise(allpass_feedback * aux + p_dst[j]); + a.buffer[a.pos] = undenormalize(allpass_feedback * aux + p_dst[j]); p_dst[j] = aux - allpass_feedback * a.buffer[a.pos]; a.pos++; } diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 6940276040..0acd48c22a 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -361,7 +361,7 @@ void SkyRD::ReflectionData::update_reflection_data(int p_size, int p_mipmaps, bo uint32_t w = p_size, h = p_size; EffectsRD *effects = RendererCompositorRD::singleton->get_effects(); - ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialised"); + ERR_FAIL_NULL_MSG(effects, "Effects haven't been initialized"); bool prefer_raster_effects = effects->get_prefer_raster_effects(); if (p_use_array) { @@ -465,7 +465,7 @@ void SkyRD::ReflectionData::update_reflection_data(int p_size, int p_mipmaps, bo void SkyRD::ReflectionData::create_reflection_fast_filter(bool p_use_arrays) { RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton(); - ERR_FAIL_NULL_MSG(copy_effects, "Effects haven't been initialised"); + ERR_FAIL_NULL_MSG(copy_effects, "Effects haven't been initialized"); bool prefer_raster_effects = copy_effects->get_prefer_raster_effects(); if (prefer_raster_effects) { @@ -523,7 +523,7 @@ void SkyRD::ReflectionData::create_reflection_fast_filter(bool p_use_arrays) { void SkyRD::ReflectionData::create_reflection_importance_sample(bool p_use_arrays, int p_cube_side, int p_base_layer, uint32_t p_sky_ggx_samples_quality) { RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton(); - ERR_FAIL_NULL_MSG(copy_effects, "Effects haven't been initialised"); + ERR_FAIL_NULL_MSG(copy_effects, "Effects haven't been initialized"); bool prefer_raster_effects = copy_effects->get_prefer_raster_effects(); if (prefer_raster_effects) { @@ -592,7 +592,7 @@ void SkyRD::ReflectionData::create_reflection_importance_sample(bool p_use_array void SkyRD::ReflectionData::update_reflection_mipmaps(int p_start, int p_end) { RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton(); - ERR_FAIL_NULL_MSG(copy_effects, "Effects haven't been initialised"); + ERR_FAIL_NULL_MSG(copy_effects, "Effects haven't been initialized"); bool prefer_raster_effects = copy_effects->get_prefer_raster_effects(); RD::get_singleton()->draw_command_begin_label("Update Radiance Cubemap Array Mipmaps"); diff --git a/servers/xr/xr_interface_extension.cpp b/servers/xr/xr_interface_extension.cpp index f90e32a987..89752b3017 100644 --- a/servers/xr/xr_interface_extension.cpp +++ b/servers/xr/xr_interface_extension.cpp @@ -106,15 +106,15 @@ uint32_t XRInterfaceExtension::get_capabilities() const { } bool XRInterfaceExtension::is_initialized() const { - bool initialised = false; - GDVIRTUAL_CALL(_is_initialized, initialised); - return initialised; + bool initialized = false; + GDVIRTUAL_CALL(_is_initialized, initialized); + return initialized; } bool XRInterfaceExtension::initialize() { - bool initialised = false; - GDVIRTUAL_CALL(_initialize, initialised); - return initialised; + bool initialized = false; + GDVIRTUAL_CALL(_initialize, initialized); + return initialized; } void XRInterfaceExtension::uninitialize() { diff --git a/servers/xr_server.h b/servers/xr_server.h index 57e42deccb..4da5619fd3 100644 --- a/servers/xr_server.h +++ b/servers/xr_server.h @@ -58,7 +58,7 @@ class XRServer : public Object { public: enum XRMode { - XRMODE_DEFAULT, /* Default behaviour, means we check project settings */ + XRMODE_DEFAULT, /* Default behavior, means we check project settings */ XRMODE_OFF, /* Ignore project settings, disable OpenXR, disable shaders */ XRMODE_ON, /* Ignore project settings, enable OpenXR, enable shaders, run editor in VR (if applicable) */ }; diff --git a/tests/core/input/test_input_event_key.h b/tests/core/input/test_input_event_key.h index ef0a656b18..b852f3ccb9 100644 --- a/tests/core/input/test_input_event_key.h +++ b/tests/core/input/test_input_event_key.h @@ -102,7 +102,7 @@ TEST_CASE("[InputEventKey] Key correctly converts itself to text") { // as text. These cases are a bit weird, since None has no textual representation // (find_keycode_name(Key::NONE) results in a nullptr). Thus, these tests look weird // with only (Physical) or a lonely modifier with (Physical) but (as far as I - // understand the code, that is intended behaviour. + // understand the code, that is intended behavior. // Key is None without a physical key. none_key.set_keycode(Key::NONE); diff --git a/tests/core/math/test_geometry_2d.h b/tests/core/math/test_geometry_2d.h index db4e6e2177..54893a0b87 100644 --- a/tests/core/math/test_geometry_2d.h +++ b/tests/core/math/test_geometry_2d.h @@ -64,7 +64,7 @@ TEST_CASE("[Geometry2D] Point in triangle") { // This tests points on the edge of the triangle. They are treated as being outside the triangle. // In `is_point_in_circle` and `is_point_in_polygon` they are treated as being inside, so in order the make - // the behaviour consistent this may change in the future (see issue #44717 and PR #44274). + // the behavior consistent this may change in the future (see issue #44717 and PR #44274). CHECK_FALSE(Geometry2D::is_point_in_triangle(Vector2(1, 1), Vector2(-1, 1), Vector2(0, -1), Vector2(1, 1))); CHECK_FALSE(Geometry2D::is_point_in_triangle(Vector2(0, 1), Vector2(-1, 1), Vector2(0, -1), Vector2(1, 1))); } diff --git a/tests/core/math/test_quaternion.h b/tests/core/math/test_quaternion.h index eb5fea1d2d..909fc2499f 100644 --- a/tests/core/math/test_quaternion.h +++ b/tests/core/math/test_quaternion.h @@ -47,9 +47,9 @@ Quaternion quat_euler_yxz_deg(Vector3 angle) { // Generate YXZ (Z-then-X-then-Y) Quaternion using single-axis Euler // constructor and quaternion product, both tested separately. - Quaternion q_y(Vector3(0.0, yaw, 0.0)); - Quaternion q_p(Vector3(pitch, 0.0, 0.0)); - Quaternion q_r(Vector3(0.0, 0.0, roll)); + Quaternion q_y = Quaternion::from_euler(Vector3(0.0, yaw, 0.0)); + Quaternion q_p = Quaternion::from_euler(Vector3(pitch, 0.0, 0.0)); + Quaternion q_r = Quaternion::from_euler(Vector3(0.0, 0.0, roll)); // Roll-Z is followed by Pitch-X, then Yaw-Y. Quaternion q_yxz = q_y * q_p * q_r; @@ -134,21 +134,21 @@ TEST_CASE("[Quaternion] Construct Euler SingleAxis") { double roll = Math::deg_to_rad(10.0); Vector3 euler_y(0.0, yaw, 0.0); - Quaternion q_y(euler_y); + Quaternion q_y = Quaternion::from_euler(euler_y); CHECK(q_y[0] == doctest::Approx(0.0)); CHECK(q_y[1] == doctest::Approx(0.382684)); CHECK(q_y[2] == doctest::Approx(0.0)); CHECK(q_y[3] == doctest::Approx(0.923879)); Vector3 euler_p(pitch, 0.0, 0.0); - Quaternion q_p(euler_p); + Quaternion q_p = Quaternion::from_euler(euler_p); CHECK(q_p[0] == doctest::Approx(0.258819)); CHECK(q_p[1] == doctest::Approx(0.0)); CHECK(q_p[2] == doctest::Approx(0.0)); CHECK(q_p[3] == doctest::Approx(0.965926)); Vector3 euler_r(0.0, 0.0, roll); - Quaternion q_r(euler_r); + Quaternion q_r = Quaternion::from_euler(euler_r); CHECK(q_r[0] == doctest::Approx(0.0)); CHECK(q_r[1] == doctest::Approx(0.0)); CHECK(q_r[2] == doctest::Approx(0.0871558)); @@ -163,11 +163,11 @@ TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") { // Generate YXZ comparison data (Z-then-X-then-Y) using single-axis Euler // constructor and quaternion product, both tested separately. Vector3 euler_y(0.0, yaw, 0.0); - Quaternion q_y(euler_y); + Quaternion q_y = Quaternion::from_euler(euler_y); Vector3 euler_p(pitch, 0.0, 0.0); - Quaternion q_p(euler_p); + Quaternion q_p = Quaternion::from_euler(euler_p); Vector3 euler_r(0.0, 0.0, roll); - Quaternion q_r(euler_r); + Quaternion q_r = Quaternion::from_euler(euler_r); // Roll-Z is followed by Pitch-X. Quaternion check_xz = q_p * q_r; @@ -176,7 +176,7 @@ TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") { // Test construction from YXZ Euler angles. Vector3 euler_yxz(pitch, yaw, roll); - Quaternion q(euler_yxz); + Quaternion q = Quaternion::from_euler(euler_yxz); CHECK(q[0] == doctest::Approx(check_yxz[0])); CHECK(q[1] == doctest::Approx(check_yxz[1])); CHECK(q[2] == doctest::Approx(check_yxz[2])); @@ -191,7 +191,7 @@ TEST_CASE("[Quaternion] Construct Basis Euler") { double pitch = Math::deg_to_rad(30.0); double roll = Math::deg_to_rad(10.0); Vector3 euler_yxz(pitch, yaw, roll); - Quaternion q_yxz(euler_yxz); + Quaternion q_yxz = Quaternion::from_euler(euler_yxz); Basis basis_axes = Basis::from_euler(euler_yxz); Quaternion q(basis_axes); CHECK(q.is_equal_approx(q_yxz)); @@ -209,7 +209,7 @@ TEST_CASE("[Quaternion] Construct Basis Axes") { // Quaternion from local calculation. Quaternion q_local = quat_euler_yxz_deg(Vector3(31.41, -49.16, 12.34)); // Quaternion from Euler angles constructor. - Quaternion q_euler(euler_yxz); + Quaternion q_euler = Quaternion::from_euler(euler_yxz); CHECK(q_calc.is_equal_approx(q_local)); CHECK(q_local.is_equal_approx(q_euler)); @@ -253,21 +253,21 @@ TEST_CASE("[Quaternion] Product") { double roll = Math::deg_to_rad(10.0); Vector3 euler_y(0.0, yaw, 0.0); - Quaternion q_y(euler_y); + Quaternion q_y = Quaternion::from_euler(euler_y); CHECK(q_y[0] == doctest::Approx(0.0)); CHECK(q_y[1] == doctest::Approx(0.382684)); CHECK(q_y[2] == doctest::Approx(0.0)); CHECK(q_y[3] == doctest::Approx(0.923879)); Vector3 euler_p(pitch, 0.0, 0.0); - Quaternion q_p(euler_p); + Quaternion q_p = Quaternion::from_euler(euler_p); CHECK(q_p[0] == doctest::Approx(0.258819)); CHECK(q_p[1] == doctest::Approx(0.0)); CHECK(q_p[2] == doctest::Approx(0.0)); CHECK(q_p[3] == doctest::Approx(0.965926)); Vector3 euler_r(0.0, 0.0, roll); - Quaternion q_r(euler_r); + Quaternion q_r = Quaternion::from_euler(euler_r); CHECK(q_r[0] == doctest::Approx(0.0)); CHECK(q_r[1] == doctest::Approx(0.0)); CHECK(q_r[2] == doctest::Approx(0.0871558)); diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h index a87b9ffc02..f7e9259329 100644 --- a/tests/core/math/test_vector2.h +++ b/tests/core/math/test_vector2.h @@ -382,13 +382,13 @@ TEST_CASE("[Vector2] Plane methods") { ERR_PRINT_OFF; CHECK_MESSAGE( vector.bounce(vector_non_normal).is_equal_approx(Vector2()), - "Vector2 bounce should return empty Vector2 with non-normalised input."); + "Vector2 bounce should return empty Vector2 with non-normalized input."); CHECK_MESSAGE( vector.reflect(vector_non_normal).is_equal_approx(Vector2()), - "Vector2 reflect should return empty Vector2 with non-normalised input."); + "Vector2 reflect should return empty Vector2 with non-normalized input."); CHECK_MESSAGE( vector.slide(vector_non_normal).is_equal_approx(Vector2()), - "Vector2 slide should return empty Vector2 with non-normalised input."); + "Vector2 slide should return empty Vector2 with non-normalized input."); ERR_PRINT_ON; } diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h index 4932cd04db..77d3a9d93c 100644 --- a/tests/core/math/test_vector3.h +++ b/tests/core/math/test_vector3.h @@ -389,13 +389,13 @@ TEST_CASE("[Vector3] Plane methods") { ERR_PRINT_OFF; CHECK_MESSAGE( vector.bounce(vector_non_normal).is_equal_approx(Vector3()), - "Vector3 bounce should return empty Vector3 with non-normalised input."); + "Vector3 bounce should return empty Vector3 with non-normalized input."); CHECK_MESSAGE( vector.reflect(vector_non_normal).is_equal_approx(Vector3()), - "Vector3 reflect should return empty Vector3 with non-normalised input."); + "Vector3 reflect should return empty Vector3 with non-normalized input."); CHECK_MESSAGE( vector.slide(vector_non_normal).is_equal_approx(Vector3()), - "Vector3 slide should return empty Vector3 with non-normalised input."); + "Vector3 slide should return empty Vector3 with non-normalized input."); ERR_PRINT_ON; } diff --git a/tests/scene/test_animation.h b/tests/scene/test_animation.h index 9199713fd9..ca80f0ecab 100644 --- a/tests/scene/test_animation.h +++ b/tests/scene/test_animation.h @@ -140,13 +140,13 @@ TEST_CASE("[Animation] Create 3D rotation track") { Ref<Animation> animation = memnew(Animation); const int track_index = animation->add_track(Animation::TYPE_ROTATION_3D); animation->track_set_path(track_index, NodePath("Enemy:rotation")); - animation->rotation_track_insert_key(track_index, 0.0, Quaternion(Vector3(0, 1, 2))); - animation->rotation_track_insert_key(track_index, 0.5, Quaternion(Vector3(3.5, 4, 5))); + animation->rotation_track_insert_key(track_index, 0.0, Quaternion::from_euler(Vector3(0, 1, 2))); + animation->rotation_track_insert_key(track_index, 0.5, Quaternion::from_euler(Vector3(3.5, 4, 5))); CHECK(animation->get_track_count() == 1); CHECK(!animation->track_is_compressed(0)); - CHECK(Quaternion(animation->track_get_key_value(0, 0)).is_equal_approx(Quaternion(Vector3(0, 1, 2)))); - CHECK(Quaternion(animation->track_get_key_value(0, 1)).is_equal_approx(Quaternion(Vector3(3.5, 4, 5)))); + CHECK(Quaternion(animation->track_get_key_value(0, 0)).is_equal_approx(Quaternion::from_euler(Vector3(0, 1, 2)))); + CHECK(Quaternion(animation->track_get_key_value(0, 1)).is_equal_approx(Quaternion::from_euler(Vector3(3.5, 4, 5)))); Quaternion r_interpolation; diff --git a/tests/scene/test_bit_map.h b/tests/scene/test_bit_map.h index a102f40725..aca7e5fe22 100644 --- a/tests/scene/test_bit_map.h +++ b/tests/scene/test_bit_map.h @@ -234,7 +234,7 @@ TEST_CASE("[BitMap] Resize") { TEST_CASE("[BitMap] Grow and shrink mask") { const Size2i dim{ 256, 256 }; BitMap bit_map{}; - bit_map.grow_mask(100, Rect2i(0, 0, 128, 128)); // Check if method does not crash when working with an uninitialised bit map. + bit_map.grow_mask(100, Rect2i(0, 0, 128, 128)); // Check if method does not crash when working with an uninitialized bit map. CHECK_MESSAGE(bit_map.get_size() == Size2i(0, 0), "Size should still be equal to 0x0"); bit_map.create(dim); @@ -335,21 +335,21 @@ TEST_CASE("[BitMap] Blit") { blit_bit_map.instantiate(); - // Testing if uninitialised blit bit map and uninitialised bit map does not crash + // Testing if uninitialized blit bit map and uninitialized bit map does not crash bit_map.blit(blit_pos, blit_bit_map); - // Testing if uninitialised bit map does not crash + // Testing if uninitialized bit map does not crash blit_bit_map->create(blit_size); bit_map.blit(blit_pos, blit_bit_map); - // Testing if uninitialised bit map does not crash + // Testing if uninitialized bit map does not crash blit_bit_map.unref(); blit_bit_map.instantiate(); CHECK_MESSAGE(blit_bit_map->get_size() == Point2i(0, 0), "Size should be cleared by unref and instance calls."); bit_map.create(bit_map_size); bit_map.blit(Point2i(128, 128), blit_bit_map); - // Testing if both initialised does not crash. + // Testing if both initialized does not crash. blit_bit_map->create(blit_size); bit_map.blit(blit_pos, blit_bit_map); diff --git a/tests/scene/test_path_2d.h b/tests/scene/test_path_2d.h new file mode 100644 index 0000000000..dc5eee4012 --- /dev/null +++ b/tests/scene/test_path_2d.h @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* test_path_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_PATH_2D_H +#define TEST_PATH_2D_H + +#include "scene/2d/path_2d.h" + +#include "tests/test_macros.h" + +namespace TestPath2D { + +TEST_CASE("[SceneTree][Path2D] Initialization") { + SUBCASE("Path should be empty right after initialization") { + Path2D *test_path = memnew(Path2D); + CHECK(test_path->get_curve() == nullptr); + memdelete(test_path); + } +} + +TEST_CASE("[SceneTree][Path2D] Curve setter and getter") { + Path2D *test_path = memnew(Path2D); + const Ref<Curve2D> &curve = memnew(Curve2D); + + SUBCASE("Curve passed to the class should remain the same") { + test_path->set_curve(curve); + CHECK(test_path->get_curve() == curve); + } + SUBCASE("Curve passed many times to the class should remain the same") { + test_path->set_curve(curve); + test_path->set_curve(curve); + test_path->set_curve(curve); + CHECK(test_path->get_curve() == curve); + } + SUBCASE("Curve rewrite testing") { + const Ref<Curve2D> &curve1 = memnew(Curve2D); + const Ref<Curve2D> &curve2 = memnew(Curve2D); + + test_path->set_curve(curve1); + test_path->set_curve(curve2); + CHECK_MESSAGE(test_path->get_curve() != curve1, + "After rewrite, second curve should be in class"); + CHECK_MESSAGE(test_path->get_curve() == curve2, + "After rewrite, second curve should be in class"); + } + + SUBCASE("Assign same curve to two paths") { + Path2D *path2 = memnew(Path2D); + + test_path->set_curve(curve); + path2->set_curve(curve); + CHECK_MESSAGE(test_path->get_curve() == path2->get_curve(), + "Both paths have the same curve."); + memdelete(path2); + } + + SUBCASE("Swapping curves between two paths") { + Path2D *path2 = memnew(Path2D); + const Ref<Curve2D> &curve1 = memnew(Curve2D); + const Ref<Curve2D> &curve2 = memnew(Curve2D); + + test_path->set_curve(curve1); + path2->set_curve(curve2); + CHECK(test_path->get_curve() == curve1); + CHECK(path2->get_curve() == curve2); + + // Do the swap + Ref<Curve2D> temp = test_path->get_curve(); + test_path->set_curve(path2->get_curve()); + path2->set_curve(temp); + + CHECK(test_path->get_curve() == curve2); + CHECK(path2->get_curve() == curve1); + memdelete(path2); + } + + memdelete(test_path); +} + +} // namespace TestPath2D + +#endif // TEST_PATH_2D_H diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 652cbed6e8..e34f2970d4 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -2337,7 +2337,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); - // Normal left shoud deselect and place at selection start. + // Normal left should deselect and place at selection start. SEND_GUI_ACTION(text_edit, "ui_text_caret_left"); CHECK(text_edit->get_viewport()->is_input_handled()); @@ -2497,7 +2497,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); - // Normal right shoud deselect and place at selection start. + // Normal right should deselect and place at selection start. SEND_GUI_ACTION(text_edit, "ui_text_caret_right"); CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 80099d1dd4..453310f977 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -90,6 +90,7 @@ #include "tests/scene/test_code_edit.h" #include "tests/scene/test_curve.h" #include "tests/scene/test_gradient.h" +#include "tests/scene/test_path_2d.h" #include "tests/scene/test_path_3d.h" #include "tests/scene/test_sprite_frames.h" #include "tests/scene/test_text_edit.h" diff --git a/tests/test_validate_testing.h b/tests/test_validate_testing.h index 1471a952cd..41185a7d16 100644 --- a/tests/test_validate_testing.h +++ b/tests/test_validate_testing.h @@ -86,7 +86,7 @@ TEST_SUITE("Validate tests") { Plane plane(Vector3(1, 1, 1), 1.0); INFO(plane); - Quaternion quat(Vector3(0.5, 1.0, 2.0)); + Quaternion quat = Quaternion::from_euler(Vector3(0.5, 1.0, 2.0)); INFO(quat); AABB aabb(Vector3(), Vector3(100, 100, 100)); |