summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SConstruct18
-rw-r--r--core/variant/variant.cpp224
-rw-r--r--core/variant/variant.h9
-rw-r--r--doc/classes/TabBar.xml16
-rw-r--r--doc/classes/TabContainer.xml16
-rw-r--r--doc/classes/WorkerThreadPool.xml4
-rw-r--r--editor/editor_dock_manager.cpp1
-rw-r--r--editor/editor_inspector.cpp7
-rw-r--r--editor/editor_settings.cpp33
-rw-r--r--editor/editor_settings.h2
-rw-r--r--main/main.cpp8
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp48
-rw-r--r--modules/gdscript/editor/gdscript_docgen.h1
-rw-r--r--modules/gdscript/tests/README.md7
-rw-r--r--modules/gdscript/tests/scripts/completion/argument_options/argument_options.tscn3
-rw-r--r--modules/gdscript/tests/scripts/completion/argument_options/connect.cfg8
-rw-r--r--modules/gdscript/tests/scripts/completion/argument_options/connect.gd7
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_class_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/dollar_native_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_class_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/literal_scene/percent_native_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local/local.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_interfered/local_interfered.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/class_local_interfered_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_interfered_scene/native_local_interfered_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_scene/class_local_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_scene/native_local_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint/class_local_typehint.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint/native_local_typehint.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/class_local_typehint_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene/native_local_typehint_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/class_local_typehint_scene_broad.notest.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_broad/native_local_typehint_scene_broad.notest.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/class_local_typehint_scene_incompatible.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/local_typehint_scene_incompatible/native_local_typehint_scene_incompatible.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member/member.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_interfered/member_interfered.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/class_member_interfered_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_interfered_scene/native_member_interfered_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint/class_member_typehint.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint/native_member_typehint.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/class_member_typehint_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd1
-rw-r--r--modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd1
-rw-r--r--modules/gdscript/tests/test_completion.h52
-rw-r--r--modules/multiplayer/scene_cache_interface.cpp38
-rw-r--r--modules/multiplayer/scene_cache_interface.h3
-rw-r--r--platform/windows/rendering_context_driver_vulkan_windows.h2
-rw-r--r--scene/2d/line_builder.cpp15
-rw-r--r--scene/gui/tab_bar.cpp29
-rw-r--r--scene/gui/tab_bar.h7
-rw-r--r--scene/gui/tab_container.cpp12
-rw-r--r--scene/gui/tab_container.h3
-rw-r--r--servers/rendering/rendering_device.cpp4
61 files changed, 425 insertions, 186 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/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/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..4750d2f63e 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);
@@ -770,6 +772,14 @@ 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 +988,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/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 2b6644e893..82e0737ecf 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -6400,11 +6400,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);
}