diff options
69 files changed, 487 insertions, 221 deletions
diff --git a/SConstruct b/SConstruct index 260e6bb48a..05e69c431d 100644 --- a/SConstruct +++ b/SConstruct @@ -13,6 +13,10 @@ import time from types import ModuleType from collections import OrderedDict from importlib.util import spec_from_file_location, module_from_spec +from SCons import __version__ as scons_raw_version +from SCons.Script.SConscript import SConsEnvironment + +scons_ver = SConsEnvironment._get_major_minor_revision(scons_raw_version) # Explicitly resolve the helper modules, this is done to avoid clash with # modules of the same name that might be randomly added (e.g. someone adding @@ -168,7 +172,11 @@ if profile: opts = Variables(customs, ARGUMENTS) # Target build options -opts.Add(["platform", "p"], "Target platform (%s)" % "|".join(platform_list), "") +if scons_ver >= (4, 3): + opts.Add(["platform", "p"], "Target platform (%s)" % "|".join(platform_list), "") +else: + opts.Add("platform", "Target platform (%s)" % "|".join(platform_list), "") + opts.Add("p", "Alias for 'platform'", "") opts.Add(EnumVariable("target", "Compilation target", "editor", ("editor", "template_release", "template_debug"))) opts.Add(EnumVariable("arch", "CPU architecture", "auto", ["auto"] + architectures, architecture_aliases)) opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False)) @@ -286,6 +294,9 @@ if env["import_env_vars"]: selected_platform = env["platform"] +if scons_ver < (4, 3) and not selected_platform: + selected_platform = env["p"] + if selected_platform == "": # Missing `platform` argument, try to detect platform automatically if ( @@ -972,11 +983,6 @@ if env["vsproj"]: env.vs_incs = [] env.vs_srcs = [] -# CompileDB and Ninja are only available with certain SCons versions which -# not everybody might have yet, so we have to check. -from SCons import __version__ as scons_raw_version - -scons_ver = env._get_major_minor_revision(scons_raw_version) if env["compiledb"] and scons_ver < (4, 0, 0): # Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later. print("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version) diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 155a5b2781..8e702ce8bb 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -2373,184 +2373,183 @@ Variant::operator IPAddress() const { return IPAddress(operator String()); } -Variant::Variant(bool p_bool) { - type = BOOL; +Variant::Variant(bool p_bool) : + type(BOOL) { _data._bool = p_bool; } -Variant::Variant(int64_t p_int64) { - type = INT; +Variant::Variant(int64_t p_int64) : + type(INT) { _data._int = p_int64; } -Variant::Variant(int32_t p_int32) { - type = INT; +Variant::Variant(int32_t p_int32) : + type(INT) { _data._int = p_int32; } -Variant::Variant(int16_t p_int16) { - type = INT; +Variant::Variant(int16_t p_int16) : + type(INT) { _data._int = p_int16; } -Variant::Variant(int8_t p_int8) { - type = INT; +Variant::Variant(int8_t p_int8) : + type(INT) { _data._int = p_int8; } -Variant::Variant(uint64_t p_uint64) { - type = INT; +Variant::Variant(uint64_t p_uint64) : + type(INT) { _data._int = p_uint64; } -Variant::Variant(uint32_t p_uint32) { - type = INT; +Variant::Variant(uint32_t p_uint32) : + type(INT) { _data._int = p_uint32; } -Variant::Variant(uint16_t p_uint16) { - type = INT; +Variant::Variant(uint16_t p_uint16) : + type(INT) { _data._int = p_uint16; } -Variant::Variant(uint8_t p_uint8) { - type = INT; +Variant::Variant(uint8_t p_uint8) : + type(INT) { _data._int = p_uint8; } -Variant::Variant(float p_float) { - type = FLOAT; +Variant::Variant(float p_float) : + type(FLOAT) { _data._float = p_float; } -Variant::Variant(double p_double) { - type = FLOAT; +Variant::Variant(double p_double) : + type(FLOAT) { _data._float = p_double; } -Variant::Variant(const ObjectID &p_id) { - type = INT; +Variant::Variant(const ObjectID &p_id) : + type(INT) { _data._int = p_id; } -Variant::Variant(const StringName &p_string) { - type = STRING_NAME; +Variant::Variant(const StringName &p_string) : + type(STRING_NAME) { memnew_placement(_data._mem, StringName(p_string)); } -Variant::Variant(const String &p_string) { - type = STRING; +Variant::Variant(const String &p_string) : + type(STRING) { memnew_placement(_data._mem, String(p_string)); } -Variant::Variant(const char *const p_cstring) { - type = STRING; +Variant::Variant(const char *const p_cstring) : + type(STRING) { memnew_placement(_data._mem, String((const char *)p_cstring)); } -Variant::Variant(const char32_t *p_wstring) { - type = STRING; +Variant::Variant(const char32_t *p_wstring) : + type(STRING) { memnew_placement(_data._mem, String(p_wstring)); } -Variant::Variant(const Vector3 &p_vector3) { - type = VECTOR3; +Variant::Variant(const Vector3 &p_vector3) : + type(VECTOR3) { memnew_placement(_data._mem, Vector3(p_vector3)); } -Variant::Variant(const Vector3i &p_vector3i) { - type = VECTOR3I; +Variant::Variant(const Vector3i &p_vector3i) : + type(VECTOR3I) { memnew_placement(_data._mem, Vector3i(p_vector3i)); } -Variant::Variant(const Vector4 &p_vector4) { - type = VECTOR4; +Variant::Variant(const Vector4 &p_vector4) : + type(VECTOR4) { memnew_placement(_data._mem, Vector4(p_vector4)); } -Variant::Variant(const Vector4i &p_vector4i) { - type = VECTOR4I; +Variant::Variant(const Vector4i &p_vector4i) : + type(VECTOR4I) { memnew_placement(_data._mem, Vector4i(p_vector4i)); } -Variant::Variant(const Vector2 &p_vector2) { - type = VECTOR2; +Variant::Variant(const Vector2 &p_vector2) : + type(VECTOR2) { memnew_placement(_data._mem, Vector2(p_vector2)); } -Variant::Variant(const Vector2i &p_vector2i) { - type = VECTOR2I; +Variant::Variant(const Vector2i &p_vector2i) : + type(VECTOR2I) { memnew_placement(_data._mem, Vector2i(p_vector2i)); } -Variant::Variant(const Rect2 &p_rect2) { - type = RECT2; +Variant::Variant(const Rect2 &p_rect2) : + type(RECT2) { memnew_placement(_data._mem, Rect2(p_rect2)); } -Variant::Variant(const Rect2i &p_rect2i) { - type = RECT2I; +Variant::Variant(const Rect2i &p_rect2i) : + type(RECT2I) { memnew_placement(_data._mem, Rect2i(p_rect2i)); } -Variant::Variant(const Plane &p_plane) { - type = PLANE; +Variant::Variant(const Plane &p_plane) : + type(PLANE) { memnew_placement(_data._mem, Plane(p_plane)); } -Variant::Variant(const ::AABB &p_aabb) { - type = AABB; +Variant::Variant(const ::AABB &p_aabb) : + type(AABB) { _data._aabb = (::AABB *)Pools::_bucket_small.alloc(); memnew_placement(_data._aabb, ::AABB(p_aabb)); } -Variant::Variant(const Basis &p_matrix) { - type = BASIS; +Variant::Variant(const Basis &p_matrix) : + type(BASIS) { _data._basis = (Basis *)Pools::_bucket_medium.alloc(); memnew_placement(_data._basis, Basis(p_matrix)); } -Variant::Variant(const Quaternion &p_quaternion) { - type = QUATERNION; +Variant::Variant(const Quaternion &p_quaternion) : + type(QUATERNION) { memnew_placement(_data._mem, Quaternion(p_quaternion)); } -Variant::Variant(const Transform3D &p_transform) { - type = TRANSFORM3D; +Variant::Variant(const Transform3D &p_transform) : + type(TRANSFORM3D) { _data._transform3d = (Transform3D *)Pools::_bucket_medium.alloc(); memnew_placement(_data._transform3d, Transform3D(p_transform)); } -Variant::Variant(const Projection &pp_projection) { - type = PROJECTION; +Variant::Variant(const Projection &pp_projection) : + type(PROJECTION) { _data._projection = (Projection *)Pools::_bucket_large.alloc(); memnew_placement(_data._projection, Projection(pp_projection)); } -Variant::Variant(const Transform2D &p_transform) { - type = TRANSFORM2D; +Variant::Variant(const Transform2D &p_transform) : + type(TRANSFORM2D) { _data._transform2d = (Transform2D *)Pools::_bucket_small.alloc(); memnew_placement(_data._transform2d, Transform2D(p_transform)); } -Variant::Variant(const Color &p_color) { - type = COLOR; +Variant::Variant(const Color &p_color) : + type(COLOR) { memnew_placement(_data._mem, Color(p_color)); } -Variant::Variant(const NodePath &p_node_path) { - type = NODE_PATH; +Variant::Variant(const NodePath &p_node_path) : + type(NODE_PATH) { memnew_placement(_data._mem, NodePath(p_node_path)); } -Variant::Variant(const ::RID &p_rid) { - type = RID; +Variant::Variant(const ::RID &p_rid) : + type(RID) { memnew_placement(_data._mem, ::RID(p_rid)); } -Variant::Variant(const Object *p_object) { - type = OBJECT; - +Variant::Variant(const Object *p_object) : + type(OBJECT) { memnew_placement(_data._mem, ObjData); if (p_object) { @@ -2571,76 +2570,74 @@ Variant::Variant(const Object *p_object) { } } -Variant::Variant(const Callable &p_callable) { - type = CALLABLE; +Variant::Variant(const Callable &p_callable) : + type(CALLABLE) { memnew_placement(_data._mem, Callable(p_callable)); } -Variant::Variant(const Signal &p_callable) { - type = SIGNAL; +Variant::Variant(const Signal &p_callable) : + type(SIGNAL) { memnew_placement(_data._mem, Signal(p_callable)); } -Variant::Variant(const Dictionary &p_dictionary) { - type = DICTIONARY; +Variant::Variant(const Dictionary &p_dictionary) : + type(DICTIONARY) { memnew_placement(_data._mem, Dictionary(p_dictionary)); } -Variant::Variant(const Array &p_array) { - type = ARRAY; +Variant::Variant(const Array &p_array) : + type(ARRAY) { memnew_placement(_data._mem, Array(p_array)); } -Variant::Variant(const PackedByteArray &p_byte_array) { - type = PACKED_BYTE_ARRAY; - +Variant::Variant(const PackedByteArray &p_byte_array) : + type(PACKED_BYTE_ARRAY) { _data.packed_array = PackedArrayRef<uint8_t>::create(p_byte_array); } -Variant::Variant(const PackedInt32Array &p_int32_array) { - type = PACKED_INT32_ARRAY; +Variant::Variant(const PackedInt32Array &p_int32_array) : + type(PACKED_INT32_ARRAY) { _data.packed_array = PackedArrayRef<int32_t>::create(p_int32_array); } -Variant::Variant(const PackedInt64Array &p_int64_array) { - type = PACKED_INT64_ARRAY; +Variant::Variant(const PackedInt64Array &p_int64_array) : + type(PACKED_INT64_ARRAY) { _data.packed_array = PackedArrayRef<int64_t>::create(p_int64_array); } -Variant::Variant(const PackedFloat32Array &p_float32_array) { - type = PACKED_FLOAT32_ARRAY; +Variant::Variant(const PackedFloat32Array &p_float32_array) : + type(PACKED_FLOAT32_ARRAY) { _data.packed_array = PackedArrayRef<float>::create(p_float32_array); } -Variant::Variant(const PackedFloat64Array &p_float64_array) { - type = PACKED_FLOAT64_ARRAY; +Variant::Variant(const PackedFloat64Array &p_float64_array) : + type(PACKED_FLOAT64_ARRAY) { _data.packed_array = PackedArrayRef<double>::create(p_float64_array); } -Variant::Variant(const PackedStringArray &p_string_array) { - type = PACKED_STRING_ARRAY; +Variant::Variant(const PackedStringArray &p_string_array) : + type(PACKED_STRING_ARRAY) { _data.packed_array = PackedArrayRef<String>::create(p_string_array); } -Variant::Variant(const PackedVector2Array &p_vector2_array) { - type = PACKED_VECTOR2_ARRAY; +Variant::Variant(const PackedVector2Array &p_vector2_array) : + type(PACKED_VECTOR2_ARRAY) { _data.packed_array = PackedArrayRef<Vector2>::create(p_vector2_array); } -Variant::Variant(const PackedVector3Array &p_vector3_array) { - type = PACKED_VECTOR3_ARRAY; +Variant::Variant(const PackedVector3Array &p_vector3_array) : + type(PACKED_VECTOR3_ARRAY) { _data.packed_array = PackedArrayRef<Vector3>::create(p_vector3_array); } -Variant::Variant(const PackedColorArray &p_color_array) { - type = PACKED_COLOR_ARRAY; +Variant::Variant(const PackedColorArray &p_color_array) : + type(PACKED_COLOR_ARRAY) { _data.packed_array = PackedArrayRef<Color>::create(p_color_array); } /* helpers */ -Variant::Variant(const Vector<::RID> &p_array) { - type = ARRAY; - +Variant::Variant(const Vector<::RID> &p_array) : + type(ARRAY) { Array *rid_array = memnew_placement(_data._mem, Array); rid_array->resize(p_array.size()); @@ -2650,9 +2647,8 @@ Variant::Variant(const Vector<::RID> &p_array) { } } -Variant::Variant(const Vector<Plane> &p_array) { - type = ARRAY; - +Variant::Variant(const Vector<Plane> &p_array) : + type(ARRAY) { Array *plane_array = memnew_placement(_data._mem, Array); plane_array->resize(p_array.size()); @@ -2662,7 +2658,8 @@ Variant::Variant(const Vector<Plane> &p_array) { } } -Variant::Variant(const Vector<Face3> &p_face_array) { +Variant::Variant(const Vector<Face3> &p_face_array) : + type(NIL) { PackedVector3Array vertices; int face_count = p_face_array.size(); vertices.resize(face_count * 3); @@ -2678,13 +2675,11 @@ Variant::Variant(const Vector<Face3> &p_face_array) { } } - type = NIL; - *this = vertices; } -Variant::Variant(const Vector<Variant> &p_array) { - type = NIL; +Variant::Variant(const Vector<Variant> &p_array) : + type(NIL) { Array arr; arr.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { @@ -2693,8 +2688,8 @@ Variant::Variant(const Vector<Variant> &p_array) { *this = arr; } -Variant::Variant(const Vector<StringName> &p_array) { - type = NIL; +Variant::Variant(const Vector<StringName> &p_array) : + type(NIL) { PackedStringArray v; int len = p_array.size(); v.resize(len); @@ -2863,12 +2858,13 @@ void Variant::operator=(const Variant &p_variant) { } } -Variant::Variant(const IPAddress &p_address) { - type = STRING; +Variant::Variant(const IPAddress &p_address) : + type(STRING) { memnew_placement(_data._mem, String(p_address)); } -Variant::Variant(const Variant &p_variant) { +Variant::Variant(const Variant &p_variant) : + type(NIL) { reference(p_variant); } diff --git a/core/variant/variant.h b/core/variant/variant.h index 10f8dc3c7f..e40df3171f 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -165,7 +165,7 @@ private: // Variant takes 20 bytes when real_t is float, and 36 if double // it only allocates extra memory for aabb/matrix. - Type type = NIL; + Type type; struct ObjData { ObjectID id; @@ -483,8 +483,8 @@ public: Variant(const IPAddress &p_address); #define VARIANT_ENUM_CLASS_CONSTRUCTOR(m_enum) \ - Variant(m_enum p_value) { \ - type = INT; \ + Variant(m_enum p_value) : \ + type(INT) { \ _data._int = (int64_t)p_value; \ } @@ -788,7 +788,8 @@ public: static void unregister_types(); Variant(const Variant &p_variant); - _FORCE_INLINE_ Variant() {} + _FORCE_INLINE_ Variant() : + type(NIL) {} _FORCE_INLINE_ ~Variant() { clear(); } diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 06daaab6a2..50f8007efa 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -1789,7 +1789,7 @@ static String rtos_fix(double p_value) { } } -Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count) { +Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count, bool p_compat) { switch (p_variant.get_type()) { case Variant::NIL: { p_store_string_func(p_store_string_ud, "null"); @@ -2009,7 +2009,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, "\"" + E.name + "\":"); - write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); } } @@ -2035,9 +2035,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, "{\n"); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); p_store_string_func(p_store_string_ud, ": "); - write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); if (E->next()) { p_store_string_func(p_store_string_ud, ",\n"); } else { @@ -2096,7 +2096,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } else { p_store_string_func(p_store_string_ud, ", "); } - write(var, p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(var, p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); } p_store_string_func(p_store_string_ud, "]"); @@ -2110,7 +2110,16 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str case Variant::PACKED_BYTE_ARRAY: { p_store_string_func(p_store_string_ud, "PackedByteArray("); Vector<uint8_t> data = p_variant; - if (data.size() > 0) { + if (p_compat) { + int len = data.size(); + const uint8_t *ptr = data.ptr(); + for (int i = 0; i < len; i++) { + if (i > 0) { + p_store_string_func(p_store_string_ud, ", "); + } + p_store_string_func(p_store_string_ud, itos(ptr[i])); + } + } else if (data.size() > 0) { p_store_string_func(p_store_string_ud, "\""); p_store_string_func(p_store_string_ud, CryptoCore::b64_encode_str(data.ptr(), data.size())); p_store_string_func(p_store_string_ud, "\""); @@ -2255,8 +2264,8 @@ static Error _write_to_str(void *ud, const String &p_string) { return OK; } -Error VariantWriter::write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud) { +Error VariantWriter::write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, bool p_compat) { r_string = String(); - return write(p_variant, _write_to_str, &r_string, p_encode_res_func, p_encode_res_ud); + return write(p_variant, _write_to_str, &r_string, p_encode_res_func, p_encode_res_ud, 0, p_compat); } diff --git a/core/variant/variant_parser.h b/core/variant/variant_parser.h index 2f8974849f..b0ac07170d 100644 --- a/core/variant/variant_parser.h +++ b/core/variant/variant_parser.h @@ -161,8 +161,8 @@ public: typedef Error (*StoreStringFunc)(void *ud, const String &p_string); typedef String (*EncodeResourceFunc)(void *ud, const Ref<Resource> &p_resource); - static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count = 0); - static Error write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func = nullptr, void *p_encode_res_ud = nullptr); + static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count = 0, bool p_compat = true); + static Error write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func = nullptr, void *p_encode_res_ud = nullptr, bool p_compat = true); }; #endif // VARIANT_PARSER_H diff --git a/doc/classes/TabBar.xml b/doc/classes/TabBar.xml index a2beef81eb..d080294c1a 100644 --- a/doc/classes/TabBar.xml +++ b/doc/classes/TabBar.xml @@ -111,6 +111,13 @@ Returns the title of the tab at index [param tab_idx]. </description> </method> + <method name="get_tab_tooltip" qualifiers="const"> + <return type="String" /> + <param index="0" name="tab_idx" type="int" /> + <description> + Returns the tooltip text of the tab at index [param tab_idx]. + </description> + </method> <method name="is_tab_disabled" qualifiers="const"> <return type="bool" /> <param index="0" name="tab_idx" type="int" /> @@ -224,6 +231,15 @@ Sets a [param title] for the tab at index [param tab_idx]. </description> </method> + <method name="set_tab_tooltip"> + <return type="void" /> + <param index="0" name="tab_idx" type="int" /> + <param index="1" name="tooltip" type="String" /> + <description> + Sets a [param tooltip] for tab at index [param tab_idx]. + [b]Note:[/b] By default, if the [param tooltip] is empty and the tab text is truncated (not all characters fit into the tab), the title will be displayed as a tooltip. To hide the tooltip, assign [code]" "[/code] as the [param tooltip] text. + </description> + </method> </methods> <members> <member name="clip_tabs" type="bool" setter="set_clip_tabs" getter="get_clip_tabs" default="true"> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index b0e3f67791..f4d69c3076 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -92,6 +92,13 @@ Returns the title of the tab at index [param tab_idx]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title]. </description> </method> + <method name="get_tab_tooltip" qualifiers="const"> + <return type="String" /> + <param index="0" name="tab_idx" type="int" /> + <description> + Returns the tooltip text of the tab at index [param tab_idx]. + </description> + </method> <method name="is_tab_disabled" qualifiers="const"> <return type="bool" /> <param index="0" name="tab_idx" type="int" /> @@ -173,6 +180,15 @@ Sets a custom title for the tab at index [param tab_idx] (tab titles default to the name of the indexed child node). Set it back to the child's name to make the tab default to it again. </description> </method> + <method name="set_tab_tooltip"> + <return type="void" /> + <param index="0" name="tab_idx" type="int" /> + <param index="1" name="tooltip" type="String" /> + <description> + Sets a custom tooltip text for tab at index [param tab_idx]. + [b]Note:[/b] By default, if the [param tooltip] is empty and the tab text is truncated (not all characters fit into the tab), the title will be displayed as a tooltip. To hide the tooltip, assign [code]" "[/code] as the [param tooltip] text. + </description> + </method> </methods> <members> <member name="all_tabs_in_front" type="bool" setter="set_all_tabs_in_front" getter="is_all_tabs_in_front" default="false"> diff --git a/doc/classes/WorkerThreadPool.xml b/doc/classes/WorkerThreadPool.xml index fa1f08b149..2b1bc136cb 100644 --- a/doc/classes/WorkerThreadPool.xml +++ b/doc/classes/WorkerThreadPool.xml @@ -58,6 +58,7 @@ Adds [param action] as a group task to be executed by the worker threads. The [Callable] will be called a number of times based on [param elements], with the first thread calling it with the value [code]0[/code] as a parameter, and each consecutive execution incrementing this value by 1 until it reaches [code]element - 1[/code]. The number of threads the task is distributed to is defined by [param tasks_needed], where the default value [code]-1[/code] means it is distributed to all worker threads. [param high_priority] determines if the task has a high priority or a low priority (default). You can optionally provide a [param description] to help with debugging. Returns a group task ID that can be used by other methods. + [b]Warning:[/b] Every task must be waited for completion using [method wait_for_task_completion] or [method wait_for_group_task_completion] at some point so that any allocated resources inside the task can be cleaned up. </description> </method> <method name="add_task"> @@ -68,6 +69,7 @@ <description> Adds [param action] as a task to be executed by a worker thread. [param high_priority] determines if the task has a high priority or a low priority (default). You can optionally provide a [param description] to help with debugging. Returns a task ID that can be used by other methods. + [b]Warning:[/b] Every task must be waited for completion using [method wait_for_task_completion] or [method wait_for_group_task_completion] at some point so that any allocated resources inside the task can be cleaned up. </description> </method> <method name="get_group_processed_element_count" qualifiers="const"> @@ -83,6 +85,7 @@ <param index="0" name="group_id" type="int" /> <description> Returns [code]true[/code] if the group task with the given ID is completed. + [b]Note:[/b] You should only call this method between adding the group task and awaiting its completion. </description> </method> <method name="is_task_completed" qualifiers="const"> @@ -90,6 +93,7 @@ <param index="0" name="task_id" type="int" /> <description> Returns [code]true[/code] if the task with the given ID is completed. + [b]Note:[/b] You should only call this method between adding the task and awaiting its completion. </description> </method> <method name="wait_for_group_task_completion"> diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 9fa95a93f8..5fabeb94f5 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1922,7 +1922,7 @@ void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstanc while (instance) { OccluderPolygon *oc = occluder_polygon_owner.get_or_null(instance->occluder); - if (!oc || oc->sdf_vertex_array == 0) { + if (!oc || oc->sdf_vertex_array == 0 || !instance->sdf_collision) { instance = instance->next; continue; } diff --git a/editor/editor_dock_manager.cpp b/editor/editor_dock_manager.cpp index 06dd33d8ab..b6250671ee 100644 --- a/editor/editor_dock_manager.cpp +++ b/editor/editor_dock_manager.cpp @@ -147,6 +147,7 @@ void EditorDockManager::_update_layout() { if (!dock_context_popup->is_inside_tree() || EditorNode::get_singleton()->is_exiting()) { return; } + EditorNode::get_singleton()->edit_current(); dock_context_popup->docks_updated(); _update_docks_menu(); EditorNode::get_singleton()->save_editor_layout_delayed(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 98feba38fd..50cc89c618 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -3477,9 +3477,7 @@ void EditorInspector::edit(Object *p_object) { next_object = p_object; // Some plugins need to know the next edited object when clearing the inspector. if (object) { _clear(); - if (object->is_connected("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback))) { - object->disconnect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); - } + object->disconnect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); } per_array_page.clear(); @@ -4021,13 +4019,14 @@ void EditorInspector::_notification(int p_what) { } break; case NOTIFICATION_PREDELETE: { - edit(nullptr); + edit(nullptr); //just in case } break; case NOTIFICATION_EXIT_TREE: { if (!sub_inspector) { get_tree()->disconnect("node_removed", callable_mp(this, &EditorInspector::_node_removed)); } + edit(nullptr); } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index ca6be130f9..752b060513 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -990,6 +990,29 @@ EditorSettings *EditorSettings::get_singleton() { return singleton.ptr(); } +String EditorSettings::get_existing_settings_path() { + const String config_dir = EditorPaths::get_singleton()->get_config_dir(); + int minor = VERSION_MINOR; + String filename; + + do { + if (VERSION_MAJOR == 4 && minor < 3) { + // Minor version is used since 4.3, so special case to load older settings. + filename = vformat("editor_settings-%d.tres", VERSION_MAJOR); + minor = -1; + } else { + filename = vformat("editor_settings-%d.%d.tres", VERSION_MAJOR, minor); + minor--; + } + } while (minor >= 0 && !FileAccess::exists(config_dir.path_join(filename))); + return config_dir.path_join(filename); +} + +String EditorSettings::get_newest_settings_path() { + const String config_file_name = vformat("editor_settings-%d.%d.tres", VERSION_MAJOR, VERSION_MINOR); + return EditorPaths::get_singleton()->get_config_dir().path_join(config_file_name); +} + void EditorSettings::create() { // IMPORTANT: create() *must* create a valid EditorSettings singleton, // as the rest of the engine code will assume it. As such, it should never @@ -1017,16 +1040,16 @@ void EditorSettings::create() { if (EditorPaths::get_singleton()->are_paths_valid()) { // Validate editor config file. - Ref<DirAccess> dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir()); - ERR_FAIL_COND(dir.is_null()); + ERR_FAIL_COND(!DirAccess::dir_exists_absolute(EditorPaths::get_singleton()->get_config_dir())); - String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres"; - config_file_path = EditorPaths::get_singleton()->get_config_dir().path_join(config_file_name); - if (!dir->file_exists(config_file_name)) { + config_file_path = get_existing_settings_path(); + if (!FileAccess::exists(config_file_path)) { + config_file_path = get_newest_settings_path(); goto fail; } singleton = ResourceLoader::load(config_file_path, "EditorSettings"); + singleton->set_path(get_newest_settings_path()); // Settings can be loaded from older version file, so make sure it's newest. if (singleton.is_null()) { ERR_PRINT("Could not load editor settings from path: " + config_file_path); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index a058f91be8..6a329f6979 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -124,6 +124,8 @@ public: }; static EditorSettings *get_singleton(); + static String get_existing_settings_path(); + static String get_newest_settings_path(); static void create(); void setup_language(); diff --git a/main/main.cpp b/main/main.cpp index 801e8934b0..78a539fff7 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2518,12 +2518,10 @@ Error Main::setup2() { // Editor setting class is not available, load config directly. if (!init_use_custom_screen && (editor || project_manager) && EditorPaths::get_singleton()->are_paths_valid()) { - Ref<DirAccess> dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir()); - ERR_FAIL_COND_V(dir.is_null(), FAILED); + ERR_FAIL_COND_V(!DirAccess::dir_exists_absolute(EditorPaths::get_singleton()->get_config_dir()), FAILED); - String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres"; - String config_file_path = EditorPaths::get_singleton()->get_config_dir().path_join(config_file_name); - if (dir->file_exists(config_file_name)) { + String config_file_path = EditorSettings::get_existing_settings_path(); + if (FileAccess::exists(config_file_path)) { Error err; Ref<FileAccess> f = FileAccess::open(config_file_path, FileAccess::READ, &err); if (f.is_valid()) { diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp index 601db5414b..35b69fab8c 100644 --- a/modules/gdscript/editor/gdscript_docgen.cpp +++ b/modules/gdscript/editor/gdscript_docgen.cpp @@ -229,6 +229,36 @@ String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_re } } +String GDScriptDocGen::_docvalue_from_expression(const GDP::ExpressionNode *p_expression) { + ERR_FAIL_NULL_V(p_expression, String()); + + if (p_expression->is_constant) { + return _docvalue_from_variant(p_expression->reduced_value); + } + + switch (p_expression->type) { + case GDP::Node::ARRAY: { + const GDP::ArrayNode *array = static_cast<const GDP::ArrayNode *>(p_expression); + return array->elements.is_empty() ? "[]" : "[...]"; + } break; + case GDP::Node::CALL: { + const GDP::CallNode *call = static_cast<const GDP::CallNode *>(p_expression); + return call->function_name.operator String() + (call->arguments.is_empty() ? "()" : "(...)"); + } break; + case GDP::Node::DICTIONARY: { + const GDP::DictionaryNode *dict = static_cast<const GDP::DictionaryNode *>(p_expression); + return dict->elements.is_empty() ? "{}" : "{...}"; + } break; + case GDP::Node::IDENTIFIER: { + const GDP::IdentifierNode *id = static_cast<const GDP::IdentifierNode *>(p_expression); + return id->name; + } break; + default: { + return "<unknown>"; + } break; + } +} + void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_class) { p_script->_clear_doc(); @@ -328,16 +358,12 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_ method_doc.return_type = "Variant"; } - for (const GDScriptParser::ParameterNode *p : m_func->parameters) { + for (const GDP::ParameterNode *p : m_func->parameters) { DocData::ArgumentDoc arg_doc; arg_doc.name = p->identifier->name; _doctype_from_gdtype(p->get_datatype(), arg_doc.type, arg_doc.enumeration); if (p->initializer != nullptr) { - if (p->initializer->is_constant) { - arg_doc.default_value = _docvalue_from_variant(p->initializer->reduced_value); - } else { - arg_doc.default_value = "<unknown>"; - } + arg_doc.default_value = _docvalue_from_expression(p->initializer); } method_doc.arguments.push_back(arg_doc); } @@ -359,7 +385,7 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_ signal_doc.is_experimental = m_signal->doc_data.is_experimental; signal_doc.experimental_message = m_signal->doc_data.experimental_message; - for (const GDScriptParser::ParameterNode *p : m_signal->parameters) { + for (const GDP::ParameterNode *p : m_signal->parameters) { DocData::ArgumentDoc arg_doc; arg_doc.name = p->identifier->name; _doctype_from_gdtype(p->get_datatype(), arg_doc.type, arg_doc.enumeration); @@ -405,12 +431,8 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_ break; } - if (m_var->initializer) { - if (m_var->initializer->is_constant) { - prop_doc.default_value = _docvalue_from_variant(m_var->initializer->reduced_value); - } else { - prop_doc.default_value = "<unknown>"; - } + if (m_var->initializer != nullptr) { + prop_doc.default_value = _docvalue_from_expression(m_var->initializer); } prop_doc.overridden = false; diff --git a/modules/gdscript/editor/gdscript_docgen.h b/modules/gdscript/editor/gdscript_docgen.h index 651a4fb198..0ae37c4133 100644 --- a/modules/gdscript/editor/gdscript_docgen.h +++ b/modules/gdscript/editor/gdscript_docgen.h @@ -45,6 +45,7 @@ class GDScriptDocGen { static String _get_class_name(const GDP::ClassNode &p_class); static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return = false); static String _docvalue_from_variant(const Variant &p_variant, int p_recursion_level = 1); + static String _docvalue_from_expression(const GDP::ExpressionNode *p_expression); static void _generate_docs(GDScript *p_script, const GDP::ClassNode *p_class); public: diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md index 72b5316532..714e38397f 100644 --- a/modules/gdscript/tests/README.md +++ b/modules/gdscript/tests/README.md @@ -13,6 +13,12 @@ The `script/completion` folder contains test for the GDScript autocompletion. Each test case consists of at least one `.gd` file, which contains the code, and one `.cfg` file, which contains expected results and configuration. Inside of the GDScript file the character `➡` represents the cursor position, at which autocompletion is invoked. +The script files won't be parsable GDScript since it contains an invalid char and and often the code is not complete during autocompletion. To allow for a valid base when used with a scene, the +runner will remove the line which contains `➡`. Therefore the scripts need to be valid if this line is removed, otherwise the test might behave in unexpected ways. This may for example require +adding an additional `pass` statement. + +This also means, that the runner will add the script to its owner node, so the script should not be loaded through the scene file. + The config file contains two section: `[input]` contains keys that configure the test environment. The following keys are possible: @@ -20,6 +26,7 @@ The config file contains two section: - `cs: boolean = false`: If `true`, the test will be skipped when running a non C# build. - `use_single_quotes: boolean = false`: Configures the corresponding editor setting for the test. - `scene: String`: Allows to specify a scene which is opened while autocompletion is performed. If this is not set the test runner will search for a `.tscn` file with the same basename as the GDScript file. If that isn't found either, autocompletion will behave as if no scene was opened. +- `node_path: String`: The node path of the node which holds the current script inside of the scene. Defaults to the scene root node. `[output]` specifies the expected results for the test. The following key are supported: diff --git a/modules/gdscript/tests/scripts/completion/argument_options/argument_options.tscn b/modules/gdscript/tests/scripts/completion/argument_options/argument_options.tscn new file mode 100644 index 0000000000..d3dea6b12b --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/argument_options.tscn @@ -0,0 +1,3 @@ +[gd_scene load_steps=1 format=3 uid="uid://dl28pdkxcjvym"] + +[node name="GetNode" type="Node"] diff --git a/modules/gdscript/tests/scripts/completion/argument_options/connect.cfg b/modules/gdscript/tests/scripts/completion/argument_options/connect.cfg new file mode 100644 index 0000000000..8c3fbf90da --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/connect.cfg @@ -0,0 +1,8 @@ +[input] +scene="res://completion/argument_options/argument_options.tscn" +[output] +include=[ + ; Node + {"display": "\"signal_a\""}, + {"display": "\"child_entered_tree\""}, +] diff --git a/modules/gdscript/tests/scripts/completion/argument_options/connect.gd b/modules/gdscript/tests/scripts/completion/argument_options/connect.gd new file mode 100644 index 0000000000..1137070c4e --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/argument_options/connect.gd @@ -0,0 +1,7 @@ +extends Node + +signal signal_a() + +func _ready(): + connect(➡) + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd index df458a9435..dc7cc99554 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd @@ -2,3 +2,4 @@ extends Node func a(): %AnimationPlayer.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd index 7050761b86..5586317938 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd @@ -2,3 +2,4 @@ extends Node func a(): $UniqueAnimationPlayer.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd index a84283a1de..69fcac4471 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd @@ -2,3 +2,4 @@ extends Node func a(): $A.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd index 6e3fee1696..0b13a207ff 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd @@ -2,3 +2,4 @@ extends Node func a(): $AnimationPlayer.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd index 27f059c944..48945db38e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd @@ -2,3 +2,4 @@ extends Node func a(): %UniqueA.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd index 07068fc5a4..3684edc73b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd @@ -2,3 +2,4 @@ extends Node func a(): %UniqueAnimationPlayer.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local/local.gd b/modules/gdscript/tests/scripts/completion/get_node/local/local.gd index 596ad80ef2..dcd232d82d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local/local.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local/local.gd @@ -3,3 +3,4 @@ extends Node func a(): var test = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd index 6f87af3c85..7710c2d13b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd @@ -3,3 +3,4 @@ extends Node func a(): var test := $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd index a710c8bbd7..6b29bf5526 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test := $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd index 6f87af3c85..7710c2d13b 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test := $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd index 2fc88f93dd..02c0d2dceb 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd index 596ad80ef2..dcd232d82d 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd index b6d2074939..3277beccab 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd @@ -5,3 +5,4 @@ const A := preload("res://completion/class_a.notest.gd") func a(): var test: A = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd index 13b541a35d..e6d22af296 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd @@ -3,3 +3,4 @@ extends Node func a(): var test: AnimationPlayer = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd index b6d2074939..3277beccab 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd @@ -5,3 +5,4 @@ const A := preload("res://completion/class_a.notest.gd") func a(): var test: A = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd index 13b541a35d..e6d22af296 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd @@ -3,3 +3,4 @@ extends Node func a(): var test: AnimationPlayer = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd index 5c785b3ddc..3266679a6a 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd @@ -4,3 +4,4 @@ extends Node func a(): var test: Node = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd index 57f4e16e3c..f637fc3d0f 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd @@ -4,3 +4,4 @@ extends Node func a(): var test: Node = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd index c6adfe0dd3..8ce75d9996 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd @@ -3,3 +3,4 @@ extends Node func a(): var test: Area2D = $A test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd index f53fce9bfe..2b39e3ada7 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd @@ -3,3 +3,4 @@ extends Node func a(): var test: Area2D = $AnimationPlayer test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member/member.gd b/modules/gdscript/tests/scripts/completion/get_node/member/member.gd index 6bcc0a0298..80b4943953 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member/member.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member/member.gd @@ -4,3 +4,4 @@ var test = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd b/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd index 542197e643..97b288334e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd @@ -4,3 +4,4 @@ var test := $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd index da0b1b11d4..402fd1d275 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd @@ -4,3 +4,4 @@ var test := $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd index 542197e643..97b288334e 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd @@ -4,3 +4,4 @@ var test := $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd index 4a35661e94..6188a6e843 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd @@ -4,3 +4,4 @@ var test = $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd index 6bcc0a0298..80b4943953 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd @@ -4,3 +4,4 @@ var test = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd index e4edc3a4e4..01b7c76dbf 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd @@ -6,3 +6,4 @@ var test: A = $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd index eda94ae34d..7f2cb4e8cc 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd @@ -4,3 +4,4 @@ var test: AnimationPlayer = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd index 8f68f54072..43d8b666ab 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd @@ -6,3 +6,4 @@ const A := preload("res://completion/class_a.notest.gd") func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd index eda94ae34d..7f2cb4e8cc 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd @@ -4,3 +4,4 @@ var test: AnimationPlayer = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd index 7b0ed4ecd8..aac450be9f 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd @@ -4,3 +4,4 @@ var test: Node = $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd index 87342f9a21..9eb10e4933 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd @@ -4,3 +4,4 @@ var test: Node = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd index 5f78bcdf04..ff8214c463 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd @@ -4,3 +4,4 @@ var test: Area2D = $A func a(): test.➡ + pass diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd index c14df5cd1b..30cd7d6a21 100644 --- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd +++ b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd @@ -4,3 +4,4 @@ var test: Area2D = $AnimationPlayer func a(): test.➡ + pass diff --git a/modules/gdscript/tests/test_completion.h b/modules/gdscript/tests/test_completion.h index ac9ffcd915..327446acee 100644 --- a/modules/gdscript/tests/test_completion.h +++ b/modules/gdscript/tests/test_completion.h @@ -33,6 +33,7 @@ #ifdef TOOLS_ENABLED +#include "core/config/project_settings.h" #include "core/io/config_file.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" @@ -111,7 +112,10 @@ static void test_directory(const String &p_dir) { // For ease of reading ➡ (0x27A1) acts as sentinel char instead of 0xFFFF in the files. code = code.replace_first(String::chr(0x27A1), String::chr(0xFFFF)); // Require pointer sentinel char in scripts. - CHECK(code.find_char(0xFFFF) != -1); + int location = code.find_char(0xFFFF); + CHECK(location != -1); + + String res_path = ProjectSettings::get_singleton()->localize_path(path.path_join(next)); ConfigFile conf; if (conf.load(path.path_join(next.get_basename() + ".cfg")) != OK) { @@ -137,20 +141,46 @@ static void test_directory(const String &p_dir) { String call_hint; bool forced; - Node *owner = nullptr; + Node *scene = nullptr; if (conf.has_section_key("input", "scene")) { - Ref<PackedScene> scene = ResourceLoader::load(conf.get_value("input", "scene"), "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP); - if (scene.is_valid()) { - owner = scene->instantiate(); + Ref<PackedScene> packed_scene = ResourceLoader::load(conf.get_value("input", "scene"), "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP); + if (packed_scene.is_valid()) { + scene = packed_scene->instantiate(); } } else if (dir->file_exists(next.get_basename() + ".tscn")) { - Ref<PackedScene> scene = ResourceLoader::load(path.path_join(next.get_basename() + ".tscn"), "PackedScene"); - if (scene.is_valid()) { - owner = scene->instantiate(); + Ref<PackedScene> packed_scene = ResourceLoader::load(path.path_join(next.get_basename() + ".tscn"), "PackedScene"); + if (packed_scene.is_valid()) { + scene = packed_scene->instantiate(); + } + } + Node *owner = nullptr; + if (scene != nullptr) { + owner = scene->get_node(conf.get_value("input", "node_path", ".")); + } + + if (owner != nullptr) { + // Remove the line which contains the sentinel char, to get a valid script. + Ref<GDScript> scr; + scr.instantiate(); + int start = location; + int end = location; + for (; start >= 0; --start) { + if (code.get(start) == '\n') { + break; + } + } + for (; end < code.size(); ++end) { + if (code.get(end) == '\n') { + break; + } } + scr->set_source_code(code.erase(start, end - start)); + scr->reload(); + scr->set_path(res_path); + owner->set_script(scr); } - GDScriptLanguage::get_singleton()->complete_code(code, path.path_join(next), owner, &options, forced, call_hint); + GDScriptLanguage::get_singleton()->complete_code(code, res_path, owner, &options, forced, call_hint); String contains_excluded; for (ScriptLanguage::CodeCompletionOption &option : options) { for (const Dictionary &E : exclude) { @@ -179,8 +209,8 @@ static void test_directory(const String &p_dir) { CHECK(expected_call_hint == call_hint); CHECK(expected_forced == forced); - if (owner) { - memdelete(owner); + if (scene) { + memdelete(scene); } } next = dir->get_next(); diff --git a/modules/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp index 33b05d4cc2..c08ccbe4cc 100644 --- a/modules/multiplayer/scene_cache_interface.cpp +++ b/modules/multiplayer/scene_cache_interface.cpp @@ -52,6 +52,9 @@ void SceneCacheInterface::_remove_node_cache(ObjectID p_oid) { if (!nc) { return; } + if (nc->cache_id) { + assigned_ids.erase(nc->cache_id); + } for (KeyValue<int, int> &E : nc->recv_ids) { PeerInfo *pinfo = peers_info.getptr(E.key); ERR_CONTINUE(!pinfo); @@ -117,16 +120,12 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac NodeCache &cache = _track(node); cache.recv_ids.insert(p_from, id); - // Encode path to send ack. - CharString pname = String(path).utf8(); - int len = encode_cstring(pname.get_data(), nullptr); - + // Send ack. Vector<uint8_t> packet; - - packet.resize(1 + 1 + len); + packet.resize(1 + 1 + 4); packet.write[0] = SceneMultiplayer::NETWORK_COMMAND_CONFIRM_PATH; packet.write[1] = valid_rpc_checksum; - encode_cstring(pname.get_data(), &packet.write[2]); + encode_uint32(id, &packet.write[2]); Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); ERR_FAIL_COND(multiplayer_peer.is_null()); @@ -137,26 +136,26 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac } void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); + ERR_FAIL_COND_MSG(p_packet_len != 6, "Invalid packet received. Size too small."); Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); ERR_FAIL_NULL(root_node); const bool valid_rpc_checksum = p_packet[1]; + int id = decode_uint32(&p_packet[2]); - String paths; - paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); - - const NodePath path = paths; + const ObjectID *oid = assigned_ids.getptr(id); + if (oid == nullptr) { + return; // May be trying to confirm a node that was removed. + } if (valid_rpc_checksum == false) { - ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); + const Node *node = Object::cast_to<Node>(ObjectDB::get_instance(*oid)); + ERR_FAIL_NULL(node); // Bug. + ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + node->get_path()); } - Node *node = root_node->get_node(path); - ERR_FAIL_NULL(node); - - NodeCache *cache = nodes_cache.getptr(node->get_instance_id()); - ERR_FAIL_NULL_MSG(cache, "Invalid packet received. Tries to confirm a node which was not requested."); + NodeCache *cache = nodes_cache.getptr(*oid); + ERR_FAIL_NULL(cache); // Bug. bool *confirmed = cache->confirmed_peers.getptr(p_from); ERR_FAIL_NULL_MSG(confirmed, "Invalid packet received. Tries to confirm a node which was not requested."); @@ -216,6 +215,7 @@ int SceneCacheInterface::make_object_cache(Object *p_obj) { NodeCache &cache = _track(node); if (cache.cache_id == 0) { cache.cache_id = last_send_cache_id++; + assigned_ids[cache.cache_id] = p_obj->get_instance_id(); } return cache.cache_id; } @@ -227,6 +227,7 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r NodeCache &cache = _track(node); if (cache.cache_id == 0) { cache.cache_id = last_send_cache_id++; + assigned_ids[cache.cache_id] = p_obj->get_instance_id(); } r_id = cache.cache_id; @@ -289,5 +290,6 @@ void SceneCacheInterface::clear() { } peers_info.clear(); nodes_cache.clear(); + assigned_ids.clear(); last_send_cache_id = 1; } diff --git a/modules/multiplayer/scene_cache_interface.h b/modules/multiplayer/scene_cache_interface.h index ab4a20c078..73d6bde6ef 100644 --- a/modules/multiplayer/scene_cache_interface.h +++ b/modules/multiplayer/scene_cache_interface.h @@ -44,7 +44,7 @@ private: //path sent caches struct NodeCache { - int cache_id; + int cache_id = 0; HashMap<int, int> recv_ids; // peer id, remote cache id HashMap<int, bool> confirmed_peers; // peer id, confirmed }; @@ -55,6 +55,7 @@ private: }; HashMap<ObjectID, NodeCache> nodes_cache; + HashMap<int, ObjectID> assigned_ids; HashMap<int, PeerInfo> peers_info; int last_send_cache_id = 1; diff --git a/platform/windows/rendering_context_driver_vulkan_windows.h b/platform/windows/rendering_context_driver_vulkan_windows.h index 34546c9ea6..1bb70cb0ff 100644 --- a/platform/windows/rendering_context_driver_vulkan_windows.h +++ b/platform/windows/rendering_context_driver_vulkan_windows.h @@ -52,7 +52,7 @@ public: }; RenderingContextDriverVulkanWindows(); - ~RenderingContextDriverVulkanWindows() override final; + ~RenderingContextDriverVulkanWindows() override; }; #endif // VULKAN_ENABLED diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index e898dc7fab..f8a8aa487c 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -353,7 +353,20 @@ void LineBuilder::build() { } else if (current_joint_mode == Line2D::LINE_JOINT_ROUND && !(wrap_around && i == segments_count)) { Vector2 vbegin = cbegin - pos1; Vector2 vend = cend - pos1; - strip_add_arc(pos1, vbegin.angle_to(vend), orientation); + // We want to use vbegin.angle_to(vend) below, which evaluates to + // Math::atan2(vbegin.cross(vend), vbegin.dot(vend)) but we need to + // calculate this ourselves as we need to check if the cross product + // in that calculation ends up being -0.f and flip it if so, effectively + // flipping the resulting angle_delta to not return -PI but +PI instead + float cross_product = vbegin.cross(vend); + float dot_product = vbegin.dot(vend); + // Note that we're comparing against -0.f for clarity but 0.f would + // match as well, therefore we need the explicit signbit check too. + if (cross_product == -0.f && signbit(cross_product)) { + cross_product = 0.f; + } + float angle_delta = Math::atan2(cross_product, dot_product); + strip_add_arc(pos1, angle_delta, orientation); } if (!is_intersecting) { diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index 2d687eb201..0e130d60af 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -323,6 +323,19 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { } } +String TabBar::get_tooltip(const Point2 &p_pos) const { + int tab_idx = get_tab_idx_at_point(p_pos); + if (tab_idx < 0) { + return Control::get_tooltip(p_pos); + } + + if (tabs[tab_idx].tooltip.is_empty() && tabs[tab_idx].truncated) { + return tabs[tab_idx].text; + } + + return tabs[tab_idx].tooltip; +} + void TabBar::_shape(int p_tab) { tabs.write[p_tab].text_buf->clear(); tabs.write[p_tab].text_buf->set_width(-1); @@ -757,6 +770,16 @@ String TabBar::get_tab_title(int p_tab) const { return tabs[p_tab].text; } +void TabBar::set_tab_tooltip(int p_tab, const String &p_tooltip) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs.write[p_tab].tooltip = p_tooltip; +} + +String TabBar::get_tab_tooltip(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), ""); + return tabs[p_tab].tooltip; +} + void TabBar::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) { ERR_FAIL_INDEX(p_tab, tabs.size()); ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); @@ -998,7 +1021,8 @@ void TabBar::_update_cache(bool p_update_hover) { tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x); tabs.write[i].size_cache = get_tab_width(i); - if (max_width > 0 && tabs[i].size_cache > max_width) { + tabs.write[i].truncated = max_width > 0 && tabs[i].size_cache > max_width; + if (tabs[i].truncated) { int size_textless = tabs[i].size_cache - tabs[i].size_text; int mw = MAX(size_textless, max_width); @@ -1730,6 +1754,8 @@ void TabBar::_bind_methods() { ClassDB::bind_method(D_METHOD("select_next_available"), &TabBar::select_next_available); ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &TabBar::set_tab_title); ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabBar::get_tab_title); + ClassDB::bind_method(D_METHOD("set_tab_tooltip", "tab_idx", "tooltip"), &TabBar::set_tab_tooltip); + ClassDB::bind_method(D_METHOD("get_tab_tooltip", "tab_idx"), &TabBar::get_tab_tooltip); ClassDB::bind_method(D_METHOD("set_tab_text_direction", "tab_idx", "direction"), &TabBar::set_tab_text_direction); ClassDB::bind_method(D_METHOD("get_tab_text_direction", "tab_idx"), &TabBar::get_tab_text_direction); ClassDB::bind_method(D_METHOD("set_tab_language", "tab_idx", "language"), &TabBar::set_tab_language); @@ -1843,6 +1869,7 @@ void TabBar::_bind_methods() { base_property_helper.set_prefix("tab_"); base_property_helper.register_property(PropertyInfo(Variant::STRING, "title"), defaults.text, &TabBar::set_tab_title, &TabBar::get_tab_title); + base_property_helper.register_property(PropertyInfo(Variant::STRING, "tooltip"), defaults.tooltip, &TabBar::set_tab_tooltip, &TabBar::get_tab_tooltip); base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &TabBar::set_tab_icon, &TabBar::get_tab_icon); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &TabBar::set_tab_disabled, &TabBar::is_tab_disabled); } diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index 6c09e960f1..52f1da5ec8 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -56,6 +56,7 @@ public: private: struct Tab { String text; + String tooltip; String language; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; @@ -66,6 +67,8 @@ private: bool disabled = false; bool hidden = false; + bool truncated = false; + Variant metadata; int ofs_cache = 0; int size_cache = 0; @@ -168,6 +171,7 @@ private: protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; + virtual String get_tooltip(const Point2 &p_pos) const override; bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); } bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); } @@ -192,6 +196,9 @@ public: void set_tab_title(int p_tab, const String &p_title); String get_tab_title(int p_tab) const; + void set_tab_tooltip(int p_tab, const String &p_tooltip); + String get_tab_tooltip(int p_tab) const; + void set_tab_text_direction(int p_tab, TextDirection p_text_direction); TextDirection get_tab_text_direction(int p_tab) const; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 16358ac21f..05f44891f6 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -400,6 +400,7 @@ void TabContainer::move_tab_from_tab_container(TabContainer *p_from, int p_from_ // Get the tab properties before they get erased by the child removal. String tab_title = p_from->get_tab_title(p_from_index); + String tab_tooltip = p_from->get_tab_tooltip(p_from_index); Ref<Texture2D> tab_icon = p_from->get_tab_icon(p_from_index); Ref<Texture2D> tab_button_icon = p_from->get_tab_button_icon(p_from_index); bool tab_disabled = p_from->is_tab_disabled(p_from_index); @@ -417,6 +418,7 @@ void TabContainer::move_tab_from_tab_container(TabContainer *p_from, int p_from_ move_child(moving_tabc, get_tab_control(p_to_index)->get_index(false)); set_tab_title(p_to_index, tab_title); + set_tab_tooltip(p_to_index, tab_tooltip); set_tab_icon(p_to_index, tab_icon); set_tab_button_icon(p_to_index, tab_button_icon); set_tab_disabled(p_to_index, tab_disabled); @@ -760,16 +762,22 @@ void TabContainer::set_tab_title(int p_tab, const String &p_title) { child->set_meta("_tab_name", p_title); } - _update_margins(); - if (!get_clip_tabs()) { - update_minimum_size(); - } + _repaint(); + queue_redraw(); } String TabContainer::get_tab_title(int p_tab) const { return tab_bar->get_tab_title(p_tab); } +void TabContainer::set_tab_tooltip(int p_tab, const String &p_tooltip) { + tab_bar->set_tab_tooltip(p_tab, p_tooltip); +} + +String TabContainer::get_tab_tooltip(int p_tab) const { + return tab_bar->get_tab_tooltip(p_tab); +} + void TabContainer::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { if (tab_bar->get_tab_icon(p_tab) == p_icon) { return; @@ -978,6 +986,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_all_tabs_in_front"), &TabContainer::is_all_tabs_in_front); ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &TabContainer::set_tab_title); ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabContainer::get_tab_title); + ClassDB::bind_method(D_METHOD("set_tab_tooltip", "tab_idx", "tooltip"), &TabContainer::set_tab_tooltip); + ClassDB::bind_method(D_METHOD("get_tab_tooltip", "tab_idx"), &TabContainer::get_tab_tooltip); ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabContainer::set_tab_icon); ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon); ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 8645a6d14e..c11d9824e7 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -155,6 +155,9 @@ public: void set_tab_title(int p_tab, const String &p_title); String get_tab_title(int p_tab) const; + void set_tab_tooltip(int p_tab, const String &p_tooltip); + String get_tab_tooltip(int p_tab) const; + void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon); Ref<Texture2D> get_tab_icon(int p_tab) const; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 98a12f1400..f70a00a9f6 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -40,6 +40,7 @@ // Version 2: changed names for Basis, AABB, Vectors, etc. // Version 3: new string ID for ext/subresources, breaks forward compat. // Version 4: PackedByteArray is now stored as base64 encoded. +#define FORMAT_VERSION_COMPAT 3 #define FORMAT_VERSION 4 #define BINARY_FORMAT_VERSION 4 @@ -845,7 +846,7 @@ void ResourceLoaderText::set_translation_remapped(bool p_remapped) { } ResourceLoaderText::ResourceLoaderText() : - stream(false) {} + stream(false), format_version(FORMAT_VERSION) {} void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types) { open(p_f); @@ -954,13 +955,13 @@ Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String } if (is_scene) { - fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + uid_text + "]\n"); + fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(format_version) + uid_text + "]\n"); } else { String script_res_text; if (!script_class.is_empty()) { script_res_text = "script_class=\"" + script_class + "\" "; } - fw->store_line("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + uid_text + "]\n"); + fw->store_line("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(format_version) + uid_text + "]\n"); } } @@ -1063,13 +1064,15 @@ void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) { } if (tag.fields.has("format")) { - int fmt = tag.fields["format"]; - if (fmt > FORMAT_VERSION) { + format_version = tag.fields["format"]; + if (format_version > FORMAT_VERSION) { error_text = "Saved with newer format version"; _printerr(); error = ERR_FILE_UNRECOGNIZED; return; } + } else { + format_version = FORMAT_VERSION; } if (tag.name == "gd_scene") { @@ -1970,6 +1973,12 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, _find_resources(v); } } break; + case Variant::PACKED_BYTE_ARRAY: { + // Balance between compatibility and performance. + if (use_compat && p_variant.operator PackedByteArray().size() > 64) { + use_compat = false; + } + } break; default: { } } @@ -2005,6 +2014,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso } // Save resources. + use_compat = true; // _find_resources() changes this. _find_resources(p_resource, true); if (packed_scene.is_valid()) { @@ -2037,7 +2047,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso if (load_steps > 1) { title += "load_steps=" + itos(load_steps) + " "; } - title += "format=" + itos(FORMAT_VERSION) + ""; + title += "format=" + itos(use_compat ? FORMAT_VERSION_COMPAT : FORMAT_VERSION) + ""; ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(local_path, true); @@ -2223,7 +2233,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso } String vars; - VariantWriter::write_to_string(value, vars, _write_resources, this); + VariantWriter::write_to_string(value, vars, _write_resources, this, use_compat); f->store_string(name.property_name_encode() + " = " + vars + "\n"); } } @@ -2287,14 +2297,14 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso if (!instance_placeholder.is_empty()) { String vars; f->store_string(" instance_placeholder="); - VariantWriter::write_to_string(instance_placeholder, vars, _write_resources, this); + VariantWriter::write_to_string(instance_placeholder, vars, _write_resources, this, use_compat); f->store_string(vars); } if (instance.is_valid()) { String vars; f->store_string(" instance="); - VariantWriter::write_to_string(instance, vars, _write_resources, this); + VariantWriter::write_to_string(instance, vars, _write_resources, this, use_compat); f->store_string(vars); } @@ -2302,7 +2312,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso for (int j = 0; j < state->get_node_property_count(i); j++) { String vars; - VariantWriter::write_to_string(state->get_node_property_value(i, j), vars, _write_resources, this); + VariantWriter::write_to_string(state->get_node_property_value(i, j), vars, _write_resources, this, use_compat); f->store_string(String(state->get_node_property_name(i, j)).property_name_encode() + " = " + vars + "\n"); } @@ -2336,7 +2346,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso f->store_string(connstr); if (binds.size()) { String vars; - VariantWriter::write_to_string(binds, vars, _write_resources, this); + VariantWriter::write_to_string(binds, vars, _write_resources, this, use_compat); f->store_string(" binds= " + vars); } @@ -2368,14 +2378,14 @@ Error ResourceLoaderText::set_uid(Ref<FileAccess> p_f, ResourceUID::ID p_uid) { fw = FileAccess::open(local_path + ".uidren", FileAccess::WRITE); if (is_scene) { - fw->store_string("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); + fw->store_string("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(format_version) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); } else { String script_res_text; if (!script_class.is_empty()) { script_res_text = "script_class=\"" + script_class + "\" "; } - fw->store_string("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); + fw->store_string("[gd_resource type=\"" + res_type + "\" " + script_res_text + "load_steps=" + itos(resources_total) + " format=" + itos(format_version) + " uid=\"" + ResourceUID::get_singleton()->id_to_text(p_uid) + "\"]"); } uint8_t c = f->get_8(); diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index c05b7a24e1..41363fd975 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -54,6 +54,7 @@ class ResourceLoaderText { }; bool is_scene = false; + int format_version; String res_type; bool ignore_resource_parsing = false; @@ -178,6 +179,7 @@ class ResourceFormatSaverTextInstance { List<Ref<Resource>> saved_resources; HashMap<Ref<Resource>, String> external_resources; HashMap<Ref<Resource>, String> internal_resources; + bool use_compat = true; struct ResourceSort { Ref<Resource> resource; diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index 46c84fd230..34f9069649 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -1970,6 +1970,8 @@ void RendererCanvasCull::canvas_light_occluder_set_polygon(RID p_occluder, RID p void RendererCanvasCull::canvas_light_occluder_set_as_sdf_collision(RID p_occluder, bool p_enable) { RendererCanvasRender::LightOccluderInstance *occluder = canvas_light_occluder_owner.get_or_null(p_occluder); ERR_FAIL_NULL(occluder); + + occluder->sdf_collision = p_enable; } void RendererCanvasCull::canvas_light_occluder_set_transform(RID p_occluder, const Transform2D &p_xform) { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 6f56711151..fa8cf9c028 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1931,7 +1931,7 @@ void RendererCanvasRenderRD::render_sdf(RID p_render_target, LightOccluderInstan while (instance) { OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); - if (!co || co->sdf_index_array.is_null()) { + if (!co || co->sdf_index_array.is_null() || !instance->sdf_collision) { instance = instance->next; continue; } diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 2b6644e893..962531c866 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -1350,6 +1350,9 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye thread_local LocalVector<RDD::BufferTextureCopyRegion> command_buffer_texture_copy_regions_vector; command_buffer_texture_copy_regions_vector.clear(); + uint32_t block_w = 0, block_h = 0; + get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); + uint32_t w = tex->width; uint32_t h = tex->height; uint32_t d = tex->depth; @@ -1365,8 +1368,8 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye copy_region.texture_region_size.z = d; command_buffer_texture_copy_regions_vector.push_back(copy_region); - w = MAX(1u, w >> 1); - h = MAX(1u, h >> 1); + w = MAX(block_w, w >> 1); + h = MAX(block_h, h >> 1); d = MAX(1u, d >> 1); } @@ -1395,8 +1398,6 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye for (uint32_t i = 0; i < tex->mipmaps; i++) { uint32_t width = 0, height = 0, depth = 0; uint32_t tight_mip_size = get_image_format_required_size(tex->format, w, h, d, 1, &width, &height, &depth); - uint32_t block_w = 0, block_h = 0; - get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); uint32_t tight_row_pitch = tight_mip_size / ((height / block_h) * depth); // Copy row-by-row to erase padding due to alignments. @@ -1408,8 +1409,8 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye wp += tight_row_pitch; } - w = MAX(1u, w >> 1); - h = MAX(1u, h >> 1); + w = MAX(block_w, w >> 1); + h = MAX(block_h, h >> 1); d = MAX(1u, d >> 1); read_ptr += mip_layouts[i].size; write_ptr += tight_mip_size; @@ -6400,11 +6401,11 @@ Vector<int64_t> RenderingDevice::_draw_list_switch_to_next_pass_split(uint32_t p #endif void RenderingDevice::_draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { - ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); + ERR_FAIL_COND(p_data_size > (uint32_t)p_data.size()); draw_list_set_push_constant(p_list, p_data.ptr(), p_data_size); } void RenderingDevice::_compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { - ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); + ERR_FAIL_COND(p_data_size > (uint32_t)p_data.size()); compute_list_set_push_constant(p_list, p_data.ptr(), p_data_size); } diff --git a/tests/core/io/test_resource.h b/tests/core/io/test_resource.h index 9ddb51220b..a83e7f88ba 100644 --- a/tests/core/io/test_resource.h +++ b/tests/core/io/test_resource.h @@ -38,6 +38,8 @@ #include "thirdparty/doctest/doctest.h" +#include "tests/test_macros.h" + namespace TestResource { TEST_CASE("[Resource] Duplication") { @@ -124,9 +126,12 @@ TEST_CASE("[Resource] Breaking circular references on save") { const String save_path_binary = OS::get_singleton()->get_cache_path().path_join("resource.res"); const String save_path_text = OS::get_singleton()->get_cache_path().path_join("resource.tres"); ResourceSaver::save(resource_a, save_path_binary); + // Suppress expected errors caused by the resources above being uncached. + ERR_PRINT_OFF; ResourceSaver::save(resource_a, save_path_text); const Ref<Resource> &loaded_resource_a_binary = ResourceLoader::load(save_path_binary); + ERR_PRINT_ON; CHECK_MESSAGE( loaded_resource_a_binary->get_name() == "A", "The loaded resource name should be equal to the expected value."); |