summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/bullet/shape_bullet.h10
-rw-r--r--modules/csg/csg_shape.cpp10
-rw-r--r--modules/csg/csg_shape.h2
-rw-r--r--modules/csg/doc_classes/CSGBox3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGCylinder3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGMesh3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGPolygon3D.xml5
-rw-r--r--modules/csg/doc_classes/CSGTorus3D.xml4
-rw-r--r--modules/cvtt/SCsub11
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp34
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp67
-rw-r--r--modules/fbx/editor_scene_importer_fbx.cpp2
-rw-r--r--modules/fbx/fbx_parser/FBXAnimation.cpp2
-rw-r--r--modules/fbx/fbx_parser/FBXDeformer.cpp4
-rw-r--r--modules/fbx/fbx_parser/FBXParser.cpp2
-rw-r--r--modules/fbx/fbx_parser/FBXProperties.cpp3
-rw-r--r--modules/gdnative/gdnative/packed_arrays.cpp2
-rw-r--r--modules/gdnative/gdnative/rect2.cpp2
-rw-r--r--modules/gdnative/gdnative/transform2d.cpp1
-rw-r--r--modules/gdnative/gdnative/vector2.cpp2
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h1
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp8
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp22
-rw-r--r--modules/gdscript/gdscript.cpp9
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp134
-rw-r--r--modules/gdscript/gdscript_analyzer.h2
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp1
-rw-r--r--modules/gdscript/gdscript_cache.cpp5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp18
-rw-r--r--modules/gdscript/gdscript_editor.cpp16
-rw-r--r--modules/gdscript/gdscript_function.cpp2
-rw-r--r--modules/gdscript/gdscript_parser.cpp13
-rw-r--r--modules/gdscript/gdscript_parser.h5
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp116
-rw-r--r--modules/gdscript/gdscript_vm.cpp4
-rw-r--r--modules/gdscript/gdscript_warning.cpp4
-rw-r--r--modules/gdscript/gdscript_warning.h1
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp8
-rw-r--r--modules/gdscript/language_server/lsp.hpp2
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp42
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd21
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out7
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd15
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out21
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd1
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/params_default_values.gd35
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/params_default_values.out6
-rw-r--r--modules/glslang/glslang_resource_limits.h18
-rw-r--r--modules/gltf/gltf_document.cpp38
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml2
-rw-r--r--modules/gridmap/grid_map.cpp23
-rw-r--r--modules/gridmap/grid_map.h4
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp10
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp6
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h3
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py8
-rw-r--r--modules/mono/csharp_script.cpp4
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs25
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs2
-rw-r--r--modules/mono/editor/bindings_generator.cpp4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs4
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp121
-rw-r--r--modules/navigation/navigation_mesh_generator.h2
-rw-r--r--modules/ogg/ogg_packet_sequence.h2
-rw-r--r--modules/text_server_adv/SCsub1
-rw-r--r--modules/text_server_adv/text_server_adv.cpp544
-rw-r--r--modules/text_server_adv/text_server_adv.h32
-rw-r--r--modules/text_server_fb/text_server_fb.cpp347
-rw-r--r--modules/text_server_fb/text_server_fb.h36
-rw-r--r--modules/theora/video_stream_theora.cpp1
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp406
-rw-r--r--modules/visual_script/editor/visual_script_editor.h78
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp1628
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h174
-rw-r--r--modules/visual_script/icons/VisualScript.svg2
-rw-r--r--modules/visual_script/icons/VisualScriptInternal.svg2
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp2
-rw-r--r--modules/visual_script/visual_script_expression.cpp59
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp2
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml8
-rw-r--r--modules/webrtc/register_types.cpp8
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp2
-rw-r--r--modules/websocket/wsl_client.cpp86
-rw-r--r--modules/websocket/wsl_client.h5
-rw-r--r--modules/websocket/wsl_server.cpp16
-rw-r--r--modules/webxr/webxr_interface_js.cpp2
-rw-r--r--modules/webxr/webxr_interface_js.h2
114 files changed, 3029 insertions, 1544 deletions
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 6377f8915d..dffcadbcdc 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -105,7 +105,7 @@ private:
};
class SphereShapeBullet : public ShapeBullet {
- real_t radius;
+ real_t radius = 0.0;
public:
SphereShapeBullet();
@@ -137,8 +137,8 @@ private:
};
class CapsuleShapeBullet : public ShapeBullet {
- real_t height;
- real_t radius;
+ real_t height = 0.0;
+ real_t radius = 0.0;
public:
CapsuleShapeBullet();
@@ -155,8 +155,8 @@ private:
};
class CylinderShapeBullet : public ShapeBullet {
- real_t height;
- real_t radius;
+ real_t height = 0.0;
+ real_t radius = 0.0;
public:
CylinderShapeBullet();
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 41b4682c84..fbddedbe55 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -1451,8 +1451,8 @@ Ref<Material> CSGCylinder3D::get_material() const {
CSGCylinder3D::CSGCylinder3D() {
// defaults
- radius = 1.0;
- height = 1.0;
+ radius = 0.5;
+ height = 2.0;
sides = 8;
cone = false;
smooth_faces = true;
@@ -1671,8 +1671,8 @@ Ref<Material> CSGTorus3D::get_material() const {
CSGTorus3D::CSGTorus3D() {
// defaults
- inner_radius = 2.0;
- outer_radius = 3.0;
+ inner_radius = 0.5;
+ outer_radius = 1.0;
sides = 8;
ring_sides = 6;
smooth_faces = true;
@@ -1694,7 +1694,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
int shape_sides = shape_polygon.size();
Vector<int> shape_faces = Geometry2D::triangulate_polygon(shape_polygon);
- ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon");
+ ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon. Make sure the polygon doesn't have any intersecting edges.");
// Get polygon enclosing Rect2.
Rect2 shape_rect(shape_polygon[0], Vector2());
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index eed995a40e..6da9893368 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -239,7 +239,7 @@ class CSGBox3D : public CSGPrimitive3D {
virtual CSGBrush *_build_brush() override;
Ref<Material> material;
- Vector3 size = Vector3(2, 2, 2);
+ Vector3 size = Vector3(1, 1, 1);
protected:
static void _bind_methods();
diff --git a/modules/csg/doc_classes/CSGBox3D.xml b/modules/csg/doc_classes/CSGBox3D.xml
index d64e58ae4d..4b479ed42e 100644
--- a/modules/csg/doc_classes/CSGBox3D.xml
+++ b/modules/csg/doc_classes/CSGBox3D.xml
@@ -12,7 +12,7 @@
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the box.
</member>
- <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(2, 2, 2)">
+ <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(1, 1, 1)">
The box's width, height and depth.
</member>
</members>
diff --git a/modules/csg/doc_classes/CSGCylinder3D.xml b/modules/csg/doc_classes/CSGCylinder3D.xml
index 40e989bfb3..1fe2025bab 100644
--- a/modules/csg/doc_classes/CSGCylinder3D.xml
+++ b/modules/csg/doc_classes/CSGCylinder3D.xml
@@ -12,13 +12,13 @@
<member name="cone" type="bool" setter="set_cone" getter="is_cone" default="false">
If [code]true[/code] a cone is created, the [member radius] will only apply to one side.
</member>
- <member name="height" type="float" setter="set_height" getter="get_height" default="1.0">
+ <member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
The height of the cylinder.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the cylinder.
</member>
- <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
The radius of the cylinder.
</member>
<member name="sides" type="int" setter="set_sides" getter="get_sides" default="8">
diff --git a/modules/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml
index 2810343139..42fcb7bd2b 100644
--- a/modules/csg/doc_classes/CSGMesh3D.xml
+++ b/modules/csg/doc_classes/CSGMesh3D.xml
@@ -4,7 +4,7 @@
A CSG Mesh shape that uses a mesh resource.
</brief_description>
<description>
- This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces.
+ This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces. See also [CSGPolygon3D] for drawing 2D extruded polygons to be used as CSG nodes.
</description>
<tutorials>
</tutorials>
diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml
index ecbb7962d1..5a49eebc7b 100644
--- a/modules/csg/doc_classes/CSGPolygon3D.xml
+++ b/modules/csg/doc_classes/CSGPolygon3D.xml
@@ -4,7 +4,7 @@
Extrudes a 2D polygon shape to create a 3D mesh.
</brief_description>
<description>
- An array of 2D points is extruded to quickly and easily create a variety of 3D meshes.
+ An array of 2D points is extruded to quickly and easily create a variety of 3D meshes. See also [CSGMesh3D] for using 3D meshes as CSG nodes.
</description>
<tutorials>
</tutorials>
@@ -46,7 +46,8 @@
When [member mode] is [constant MODE_PATH], this is the distance along the path, in meters, the texture coordinates will tile. When set to 0, texture coordinates will match geometry exactly with no tiling.
</member>
<member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array(0, 0, 0, 1, 1, 1, 1, 0)">
- The point array that defines the 2D polygon that is extruded.
+ The point array that defines the 2D polygon that is extruded. This can be a convex or concave polygon with 3 or more points. The polygon must [i]not[/i] have any intersecting edges. Otherwise, triangulation will fail and no mesh will be generated.
+ [b]Note:[/b] If only 1 or 2 points are defined in [member polygon], no mesh will be generated.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="false">
If [code]true[/code], applies smooth shading to the extrusions.
diff --git a/modules/csg/doc_classes/CSGTorus3D.xml b/modules/csg/doc_classes/CSGTorus3D.xml
index 91ee63a4c9..2c0eef8f09 100644
--- a/modules/csg/doc_classes/CSGTorus3D.xml
+++ b/modules/csg/doc_classes/CSGTorus3D.xml
@@ -9,13 +9,13 @@
<tutorials>
</tutorials>
<members>
- <member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius" default="2.0">
+ <member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius" default="0.5">
The inner radius of the torus.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the torus.
</member>
- <member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius" default="3.0">
+ <member name="outer_radius" type="float" setter="set_outer_radius" getter="get_outer_radius" default="1.0">
The outer radius of the torus.
</member>
<member name="ring_sides" type="int" setter="set_ring_sides" getter="get_ring_sides" default="6">
diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub
index e56177d6e9..1d5a7ff6a3 100644
--- a/modules/cvtt/SCsub
+++ b/modules/cvtt/SCsub
@@ -11,7 +11,16 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/cvtt/"
thirdparty_sources = [
- "ConvectionKernels.cpp",
+ "ConvectionKernels_API.cpp",
+ "ConvectionKernels_ETC.cpp",
+ "ConvectionKernels_BC67.cpp",
+ "ConvectionKernels_IndexSelector.cpp",
+ "ConvectionKernels_BC6H_IO.cpp",
+ "ConvectionKernels_S3TC.cpp",
+ "ConvectionKernels_BC7_PrioData.cpp",
+ "ConvectionKernels_SingleFile.cpp",
+ "ConvectionKernels_BCCommon.cpp",
+ "ConvectionKernels_Util.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index 9e0579740b..d18340a2c8 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -41,7 +41,7 @@ struct CVTTCompressionJobParams {
bool is_hdr = false;
bool is_signed = false;
int bytes_per_pixel = 0;
-
+ cvtt::BC7EncodingPlan bc7_plan;
cvtt::Options options;
};
@@ -116,7 +116,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const
cvtt::Kernels::EncodeBC6HU(output_blocks, input_blocks_hdr, p_job_params.options);
}
} else {
- cvtt::Kernels::EncodeBC7(output_blocks, input_blocks_ldr, p_job_params.options);
+ cvtt::Kernels::EncodeBC7(output_blocks, input_blocks_ldr, p_job_params.options, p_job_params.bc7_plan);
}
unsigned int num_real_blocks = ((w - x_start) + 3) / 4;
@@ -141,7 +141,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) {
return; //do not compress, already compressed
}
-
int w = p_image->get_width();
int h = p_image->get_height();
@@ -153,22 +152,8 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
}
cvtt::Options options;
- uint32_t flags = cvtt::Flags::Fastest;
-
- if (p_lossy_quality > 0.85) {
- flags = cvtt::Flags::Ultra;
- } else if (p_lossy_quality > 0.75) {
- flags = cvtt::Flags::Better;
- } else if (p_lossy_quality > 0.55) {
- flags = cvtt::Flags::Default;
- } else if (p_lossy_quality > 0.35) {
- flags = cvtt::Flags::Fast;
- } else if (p_lossy_quality > 0.15) {
- flags = cvtt::Flags::Faster;
- }
-
+ uint32_t flags = cvtt::Flags::Default;
flags |= cvtt::Flags::BC7_RespectPunchThrough;
-
if (p_channels == Image::USED_CHANNELS_RG) { //guessing this is a normal map
flags |= cvtt::Flags::Uniform;
}
@@ -215,12 +200,15 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
job_queue.job_params.is_signed = is_signed;
job_queue.job_params.options = options;
job_queue.job_params.bytes_per_pixel = is_hdr ? 6 : 4;
+ cvtt::Kernels::ConfigureBC7EncodingPlanFromQuality(job_queue.job_params.bc7_plan, 5);
-#ifdef NO_THREADS
int num_job_threads = 0;
-#else
- int num_job_threads = OS::get_singleton()->can_use_threads() ? (OS::get_singleton()->get_processor_count() - 1) : 0;
-#endif
+ // Amdahl's law (Wikipedia)
+ // If a program needs 20 hours to complete using a single thread, but a one-hour portion of the program cannot be parallelized,
+ // therefore only the remaining 19 hours (p = 0.95) of execution time can be parallelized, then regardless of how many threads are devoted
+ // to a parallelized execution of this program, the minimum execution time cannot be less than one hour.
+ //
+ // The number of executions with different inputs can be increased while the latency is the same.
Vector<CVTTCompressionRowTask> tasks;
@@ -278,7 +266,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
memdelete(threads_wb[i]);
}
}
-
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
@@ -388,6 +375,5 @@ void image_decompress_cvtt(Image *p_image) {
w >>= 1;
h >>= 1;
}
-
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index c79d449d41..7d5557d197 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -132,8 +132,39 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
// Compress image data and (if required) mipmaps.
const bool mipmaps = r_img->has_mipmaps();
- const int width = r_img->get_width();
- const int height = r_img->get_height();
+ int width = r_img->get_width();
+ int height = r_img->get_height();
+
+ /*
+ The first mipmap level of a compressed texture must be a multiple of 4. Quote from D3D11.3 spec:
+
+ BC format surfaces are always multiples of full blocks, each block representing 4x4 pixels.
+ For mipmaps, the top level map is required to be a multiple of 4 size in all dimensions.
+ The sizes for the lower level maps are computed as they are for all mipmapped surfaces,
+ and thus may not be a multiple of 4, for example a top level map of 20 results in a second level
+ map size of 10. For these cases, there is a differing 'physical' size and a 'virtual' size.
+ The virtual size is that computed for each mip level without adjustment, which is 10 for the example.
+ The physical size is the virtual size rounded up to the next multiple of 4, which is 12 for the example,
+ and this represents the actual memory size. The sampling hardware will apply texture address
+ processing based on the virtual size (using, for example, border color if specified for accesses
+ beyond 10), and thus for the example case will not access the 11th and 12th row of the resource.
+ So for mipmap chains when an axis becomes < 4 in size, only texels 'a','b','e','f'
+ are used for a 2x2 map, and texel 'a' is used for 1x1. Note that this is similar to, but distinct from,
+ the surface pitch, which can encompass additional padding beyond the physical surface size.
+ */
+ int next_width = width <= 2 ? width : (width + 3) & ~3;
+ int next_height = height <= 2 ? height : (height + 3) & ~3;
+ if (next_width != width || next_height != height) {
+ r_img->resize(next_width, next_height, Image::INTERPOLATE_LANCZOS);
+ width = r_img->get_width();
+ height = r_img->get_height();
+ }
+ // ERR_FAIL_COND(width % 4 != 0 || height % 4 != 0); // FIXME: No longer guaranteed.
+ // Multiple-of-4 should be guaranteed by above.
+ // However, power-of-two 3d textures will create Nx2 and Nx1 mipmap levels,
+ // which are individually compressed Image objects that violate the above rule.
+ // Hence, we allow Nx1 and Nx2 images through without forcing to multiple-of-4.
+
const uint8_t *src_read = r_img->get_data().ptr();
print_verbose(vformat("ETCPAK: Encoding image size %dx%d to format %s.", width, height, Image::get_format_name(target_format)));
@@ -144,24 +175,48 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
uint8_t *dest_write = dest_data.ptrw();
int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
+ Vector<uint32_t> padded_src;
for (int i = 0; i < mip_count + 1; i++) {
// Get write mip metrics for target image.
- int mip_w, mip_h;
- int mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, mip_w, mip_h);
+ int orig_mip_w, orig_mip_h;
+ int mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, orig_mip_w, orig_mip_h);
// Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
ERR_FAIL_COND(mip_ofs % 8 != 0);
uint64_t *dest_mip_write = (uint64_t *)&dest_write[mip_ofs];
// Block size. Align stride to multiple of 4 (RGBA8).
- mip_w = (mip_w + 3) & ~3;
- mip_h = (mip_h + 3) & ~3;
+ int mip_w = (orig_mip_w + 3) & ~3;
+ int mip_h = (orig_mip_h + 3) & ~3;
const uint32_t blocks = mip_w * mip_h / 16;
// Get mip data from source image for reading.
int src_mip_ofs = r_img->get_mipmap_offset(i);
const uint32_t *src_mip_read = (const uint32_t *)&src_read[src_mip_ofs];
+ // Pad textures to nearest block by smearing.
+ if (mip_w != orig_mip_w || mip_h != orig_mip_h) {
+ padded_src.resize(mip_w * mip_h);
+ uint32_t *ptrw = padded_src.ptrw();
+ int x = 0, y = 0;
+ for (y = 0; y < orig_mip_h; y++) {
+ for (x = 0; x < orig_mip_w; x++) {
+ ptrw[mip_w * y + x] = src_mip_read[orig_mip_w * y + x];
+ }
+ // First, smear in x.
+ for (; x < mip_w; x++) {
+ ptrw[mip_w * y + x] = ptrw[mip_w * y + x - 1];
+ }
+ }
+ // Then, smear in y.
+ for (; y < mip_h; y++) {
+ for (x = 0; x < mip_w; x++) {
+ ptrw[mip_w * y + x] = ptrw[mip_w * y + x - mip_w];
+ }
+ }
+ // Override the src_mip_read pointer to our temporary Vector.
+ src_mip_read = padded_src.ptr();
+ }
if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2 || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp
index 4cca907bf2..758c47eecc 100644
--- a/modules/fbx/editor_scene_importer_fbx.cpp
+++ b/modules/fbx/editor_scene_importer_fbx.cpp
@@ -123,7 +123,7 @@ Node3D *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_
bool corrupt = false;
// safer to check this way as there can be different formatted headers
- if (fbx_header_string.find("Kaydara FBX Binary", 0) != -1) {
+ if (fbx_header_string.contains("Kaydara FBX Binary")) {
is_binary = true;
print_verbose("[doc] is binary");
diff --git a/modules/fbx/fbx_parser/FBXAnimation.cpp b/modules/fbx/fbx_parser/FBXAnimation.cpp
index 8c43aac8f6..8627c95012 100644
--- a/modules/fbx/fbx_parser/FBXAnimation.cpp
+++ b/modules/fbx/fbx_parser/FBXAnimation.cpp
@@ -130,7 +130,7 @@ AnimationCurve::~AnimationCurve() {
AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name,
const Document &doc, const char *const *target_prop_whitelist /*= nullptr*/,
size_t whitelist_size /*= 0*/) :
- Object(id, element, name), target(), doc(doc) {
+ Object(id, element, name), doc(doc) {
// find target node
const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" };
const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3);
diff --git a/modules/fbx/fbx_parser/FBXDeformer.cpp b/modules/fbx/fbx_parser/FBXDeformer.cpp
index b888afd90e..a2b216ab09 100644
--- a/modules/fbx/fbx_parser/FBXDeformer.cpp
+++ b/modules/fbx/fbx_parser/FBXDeformer.cpp
@@ -104,7 +104,7 @@ Constraint::~Constraint() {
// ------------------------------------------------------------------------------------------------
Cluster::Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
- Deformer(id, element, doc, name), valid_transformAssociateModel(false) {
+ Deformer(id, element, doc, name) {
const ScopePtr sc = GetRequiredScope(element);
// for( auto element : sc.Elements())
// {
@@ -177,7 +177,7 @@ Cluster::~Cluster() {
// ------------------------------------------------------------------------------------------------
Skin::Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
- Deformer(id, element, doc, name), accuracy(0.0f) {
+ Deformer(id, element, doc, name) {
const ScopePtr sc = GetRequiredScope(element);
// keep this it is used for debugging and any FBX format changes
diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp
index d8ccb4179c..e345b7fc18 100644
--- a/modules/fbx/fbx_parser/FBXParser.cpp
+++ b/modules/fbx/fbx_parser/FBXParser.cpp
@@ -235,7 +235,7 @@ Scope::~Scope() {
// ------------------------------------------------------------------------------------------------
Parser::Parser(const TokenList &tokens, bool is_binary) :
- corrupt(false), tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) {
+ tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) {
root = new_Scope(*this, true);
scopes.push_back(root);
}
diff --git a/modules/fbx/fbx_parser/FBXProperties.cpp b/modules/fbx/fbx_parser/FBXProperties.cpp
index 531f0743d6..7cbb3a2eda 100644
--- a/modules/fbx/fbx_parser/FBXProperties.cpp
+++ b/modules/fbx/fbx_parser/FBXProperties.cpp
@@ -145,8 +145,7 @@ std::string PeekPropertyName(const Element &element) {
} // namespace
// ------------------------------------------------------------------------------------------------
-PropertyTable::PropertyTable() :
- element(nullptr) {
+PropertyTable::PropertyTable() {
}
// Is used when dealing with FBX Objects not metadata.
diff --git a/modules/gdnative/gdnative/packed_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp
index bb6f0324a8..0c49694e0b 100644
--- a/modules/gdnative/gdnative/packed_arrays.cpp
+++ b/modules/gdnative/gdnative/packed_arrays.cpp
@@ -32,7 +32,7 @@
#include "core/variant/variant.h"
-#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
#include "core/math/vector3i.h"
static_assert(sizeof(godot_packed_byte_array) == sizeof(PackedByteArray), "PackedByteArray size mismatch");
diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp
index f4674850e3..7e0ce76c26 100644
--- a/modules/gdnative/gdnative/rect2.cpp
+++ b/modules/gdnative/gdnative/rect2.cpp
@@ -31,6 +31,8 @@
#include "gdnative/rect2.h"
#include "core/math/rect2.h"
+#include "core/math/rect2i.h"
+#include "core/os/memory.h"
static_assert(sizeof(godot_rect2) == sizeof(Rect2), "Rect2 size mismatch");
static_assert(sizeof(godot_rect2i) == sizeof(Rect2i), "Rect2i size mismatch");
diff --git a/modules/gdnative/gdnative/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp
index 45ba790dc1..7dc07024e5 100644
--- a/modules/gdnative/gdnative/transform2d.cpp
+++ b/modules/gdnative/gdnative/transform2d.cpp
@@ -31,6 +31,7 @@
#include "gdnative/transform2d.h"
#include "core/math/transform_2d.h"
+#include "core/os/memory.h"
static_assert(sizeof(godot_transform2d) == sizeof(Transform2D), "Transform2D size mismatch");
diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp
index eb8ffd74cd..a8d4281d25 100644
--- a/modules/gdnative/gdnative/vector2.cpp
+++ b/modules/gdnative/gdnative/vector2.cpp
@@ -31,6 +31,8 @@
#include "gdnative/vector2.h"
#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
+#include "core/os/memory.h"
static_assert(sizeof(godot_vector2) == sizeof(Vector2), "Vector2 size mismatch");
static_assert(sizeof(godot_vector2i) == sizeof(Vector2i), "Vector2i size mismatch");
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index eea898475b..879291c2e0 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -79,6 +79,7 @@ typedef enum {
GODOT_PROPERTY_HINT_PROPERTY_OF_BASE_TYPE, ///< a property of a base type
GODOT_PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance
GODOT_PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base
+ GODOT_PROPERTY_HINT_LOCALE_ID,
GODOT_PROPERTY_HINT_MAX,
} godot_nativescript_property_hint;
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index ddde28811c..0309d1d9c7 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -288,7 +288,7 @@ List<ClassAPI> generate_c_api_classes() {
String type;
String name = argument.name;
- if (argument.name.find(":") != -1) {
+ if (argument.name.contains(":")) {
type = argument.name.get_slice(":", 1);
name = argument.name.get_slice(":", 0);
} else {
@@ -324,7 +324,7 @@ List<ClassAPI> generate_c_api_classes() {
property_api.getter = ClassDB::get_property_getter(class_name, p->get().name);
property_api.setter = ClassDB::get_property_setter(class_name, p->get().name);
- if (p->get().name.find(":") != -1) {
+ if (p->get().name.contains(":")) {
property_api.type = p->get().name.get_slice(":", 1);
property_api.name = p->get().name.get_slice(":", 0);
} else {
@@ -355,7 +355,7 @@ List<ClassAPI> generate_c_api_classes() {
//method name
method_api.method_name = method_info.name;
//method return type
- if (method_api.method_name.find(":") != -1) {
+ if (method_api.method_name.contains(":")) {
method_api.return_type = method_api.method_name.get_slice(":", 1);
method_api.method_name = method_api.method_name.get_slice(":", 0);
} else {
@@ -388,7 +388,7 @@ List<ClassAPI> generate_c_api_classes() {
arg_name = arg_info.name;
- if (arg_info.name.find(":") != -1) {
+ if (arg_info.name.contains(":")) {
arg_type = arg_info.name.get_slice(":", 1);
arg_name = arg_info.name.get_slice(":", 0);
} else if (arg_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 5cc295bbab..ac6684a29c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -33,18 +33,6 @@
#include "../gdscript_tokenizer.h"
#include "editor/editor_settings.h"
-static bool _is_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
-}
-
-static bool _is_hex_symbol(char32_t c) {
- return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
-static bool _is_bin_symbol(char32_t c) {
- return (c == '0' || c == '1');
-}
-
Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
Dictionary color_map;
@@ -102,7 +90,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
color = font_color;
bool is_char = !is_symbol(str[j]);
bool is_a_symbol = is_symbol(str[j]);
- bool is_number = (str[j] >= '0' && str[j] <= '9');
+ bool is_number = is_digit(str[j]);
/* color regions */
if (is_a_symbol || in_region != -1) {
@@ -241,14 +229,14 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
// allow ABCDEF in hex notation
- if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ if (is_hex_notation && (is_hex_digit(str[j]) || is_number)) {
is_number = true;
} else {
is_hex_notation = false;
}
// disallow anything not a 0 or 1
- if (is_bin_notation && (_is_bin_symbol(str[j]))) {
+ if (is_bin_notation && (is_binary_digit(str[j]))) {
is_number = true;
} else if (is_bin_notation) {
is_bin_notation = false;
@@ -270,7 +258,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
}
- if (!in_word && _is_char(str[j]) && !is_number) {
+ if (!in_word && (is_ascii_char(str[j]) || is_underscore(str[j])) && !is_number) {
in_word = true;
}
@@ -585,7 +573,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
continue;
}
- if (name.find("/") != -1) {
+ if (name.contains("/")) {
continue;
}
member_keywords[name] = member_variable_color;
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 84db97625b..a80874d785 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -827,7 +827,7 @@ Error GDScript::reload(bool p_keep_state) {
return OK;
}
#else
- if (source.find("_BASE_") != -1) {
+ if (source.contains("_BASE_")) {
return OK;
}
#endif
@@ -2022,8 +2022,6 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"preload",
"signal",
"super",
- "trait",
- "yield",
// var
"const",
"enum",
@@ -2040,6 +2038,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"return",
"match",
"while",
+ // These keywords are not implemented currently, but reserved for (potential) future use.
+ // We highlight them as keywords to make errors easier to understand.
+ "trait",
+ "namespace",
+ "yield",
nullptr
};
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 3a79190149..9ff52347e9 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -108,7 +108,7 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::ENUM;
- type.builtin_type = Variant::OBJECT;
+ type.builtin_type = Variant::INT;
type.is_constant = true;
type.is_meta_type = true;
@@ -650,9 +650,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
datatype = specified_type;
if (member.variable->initializer != nullptr) {
- if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) {
+ if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true)) {
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
} else {
// TODO: Add warning.
@@ -1400,9 +1400,9 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
type.is_meta_type = false;
if (p_variable->initializer != nullptr) {
- if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true)) {
+ if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true, p_variable->initializer)) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true)) {
+ if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true, p_variable->initializer)) {
push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer);
} else {
// TODO: Add warning.
@@ -1586,7 +1586,7 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
if (p_match_pattern->dictionary[i].key) {
reduce_expression(p_match_pattern->dictionary[i].key);
if (!p_match_pattern->dictionary[i].key->is_constant) {
- push_error(R"(Expression in dictionary pattern key must be a constant.)", p_match_pattern->expression);
+ push_error(R"(Expression in dictionary pattern key must be a constant.)", p_match_pattern->dictionary[i].key);
}
}
@@ -1877,11 +1877,11 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
if (compatible) {
- compatible = is_type_compatible(assignee_type, op_type, true);
+ compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value);
if (!compatible) {
if (assignee_type.is_hard_type()) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(op_type, assignee_type, true)) {
+ if (!is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
} else {
// TODO: Add warning.
@@ -2416,6 +2416,11 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
+ if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) {
+ // Enum type is treated as a dictionary value for function calls.
+ base_type.is_meta_type = false;
+ }
+
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
} else if (!is_self && base_type.is_meta_type && !is_static) {
@@ -2474,17 +2479,24 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type);
if (!cast_type.is_set()) {
+ mark_node_unsafe(p_cast);
return;
}
- cast_type.is_meta_type = false; // The casted value won't be a type name.
+ cast_type = type_from_metatype(cast_type); // The casted value won't be a type name.
p_cast->set_datatype(cast_type);
if (!cast_type.is_variant()) {
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
if (!op_type.is_variant()) {
bool valid = false;
- if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
+ if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Enum types are compatible between each other, so it's a safe cast.
+ valid = true;
+ } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Convertint int to enum is always valid.
+ valid = true;
+ } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type);
@@ -2586,6 +2598,34 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
const StringName &name = p_identifier->name;
+ if (base.kind == GDScriptParser::DataType::ENUM) {
+ if (base.is_meta_type) {
+ if (base.enum_values.has(name)) {
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = base.enum_values[name];
+
+ GDScriptParser::DataType result;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::ENUM;
+ result.is_constant = true;
+ result.builtin_type = Variant::INT;
+ result.native_type = base.native_type;
+ result.enum_type = base.enum_type;
+ p_identifier->set_datatype(result);
+ return;
+ } else {
+ // Consider as a Dictionary, so it can be anything.
+ // This will be evaluated in the next if block.
+ base.kind = GDScriptParser::DataType::BUILTIN;
+ base.builtin_type = Variant::DICTIONARY;
+ base.is_meta_type = false;
+ }
+ } else {
+ push_error(R"(Cannot get property from enum value.)", p_identifier);
+ return;
+ }
+ }
+
if (base.kind == GDScriptParser::DataType::BUILTIN) {
if (base.is_meta_type) {
bool valid = true;
@@ -2632,31 +2672,6 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
}
- if (base.kind == GDScriptParser::DataType::ENUM) {
- if (base.is_meta_type) {
- if (base.enum_values.has(name)) {
- p_identifier->is_constant = true;
- p_identifier->reduced_value = base.enum_values[name];
-
- GDScriptParser::DataType result;
- result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- result.kind = GDScriptParser::DataType::ENUM_VALUE;
- result.builtin_type = base.builtin_type;
- result.native_type = base.native_type;
- result.enum_type = name;
- p_identifier->set_datatype(result);
- } else {
- // Consider as a Dictionary
- GDScriptParser::DataType dummy;
- dummy.kind = GDScriptParser::DataType::VARIANT;
- p_identifier->set_datatype(dummy);
- }
- } else {
- push_error(R"(Cannot get property from enum value.)", p_identifier);
- }
- return;
- }
-
GDScriptParser::ClassNode *base_class = base.class_type;
// TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
@@ -2792,7 +2807,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (element.identifier->name == p_identifier->name) {
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM_VALUE : GDScriptParser::DataType::BUILTIN;
+ type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM : GDScriptParser::DataType::BUILTIN;
type.builtin_type = Variant::INT;
type.is_constant = true;
if (element.parent_enum->identifier) {
@@ -3492,6 +3507,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptPars
GDScriptParser::DataType result = p_meta_type;
result.is_meta_type = false;
result.is_constant = false;
+ if (p_meta_type.kind == GDScriptParser::DataType::ENUM) {
+ result.builtin_type = Variant::INT;
+ }
return result;
}
@@ -3548,6 +3566,18 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source
r_default_arg_count = 0;
StringName function_name = p_function;
+ if (p_base_type.kind == GDScriptParser::DataType::ENUM) {
+ if (p_base_type.is_meta_type) {
+ // Enum type can be treated as a dictionary value.
+ p_base_type.kind = GDScriptParser::DataType::BUILTIN;
+ p_base_type.builtin_type = Variant::DICTIONARY;
+ p_base_type.is_meta_type = false;
+ } else {
+ push_error("Cannot call function on enum value.", p_source);
+ return false;
+ }
+ }
+
if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) {
// Construct a base type to get methods.
Callable::CallError err;
@@ -3798,6 +3828,22 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
+
+ if (p_a.kind == GDScriptParser::DataType::ENUM) {
+ if (p_a.is_meta_type) {
+ a_type = Variant::DICTIONARY;
+ } else {
+ a_type = Variant::INT;
+ }
+ }
+ if (p_b.kind == GDScriptParser::DataType::ENUM) {
+ if (p_b.is_meta_type) {
+ b_type = Variant::DICTIONARY;
+ } else {
+ b_type = Variant::INT;
+ }
+ }
+
Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
@@ -3827,7 +3873,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
// TODO: Add safe/unsafe return variable (for variant cases)
-bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion) const {
+bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion, const GDScriptParser::Node *p_source_node) {
// These return "true" so it doesn't affect users negatively.
ERR_FAIL_COND_V_MSG(!p_target.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset target type");
ERR_FAIL_COND_V_MSG(!p_source.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset value type");
@@ -3847,7 +3893,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (!valid && p_allow_implicit_conversion) {
valid = Variant::can_convert_strict(p_source.builtin_type, p_target.builtin_type);
}
- if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
+ if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM && !p_source.is_meta_type) {
// Enum value is also integer.
valid = true;
}
@@ -3868,6 +3914,11 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (p_target.kind == GDScriptParser::DataType::ENUM) {
if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
+#ifdef DEBUG_ENABLED
+ if (p_source_node) {
+ parser->push_warning(p_source_node, GDScriptWarning::INT_ASSIGNED_TO_ENUM);
+ }
+#endif
return true;
}
if (p_source.kind == GDScriptParser::DataType::ENUM) {
@@ -3875,11 +3926,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
return true;
}
}
- if (p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
- if (p_source.native_type == p_target.native_type && p_target.enum_values.has(p_source.enum_type)) {
- return true;
- }
- }
return false;
}
@@ -3934,7 +3980,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
- case GDScriptParser::DataType::ENUM_VALUE:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
@@ -3971,7 +4016,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
- case GDScriptParser::DataType::ENUM_VALUE:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 4cee5cb44a..2697a6ec2b 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -112,7 +112,7 @@ class GDScriptAnalyzer {
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
- bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
+ bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
bool class_exists(const StringName &p_class) const;
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 8623122edc..82aa14795e 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -688,6 +688,7 @@ void GDScriptByteCodeGenerator::write_ternary_false_expr(const Address &p_expr)
void GDScriptByteCodeGenerator::write_end_ternary() {
patch_jump(ternary_jump_skip_pos.back()->get());
ternary_jump_skip_pos.pop_back();
+ ternary_result.pop_back();
}
void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 4ac5a4a60e..6ada7d36f5 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -122,6 +122,10 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP
}
if (singleton->parser_map.has(p_path)) {
ref = Ref<GDScriptParserRef>(singleton->parser_map[p_path]);
+ if (ref.is_null()) {
+ r_error = ERR_INVALID_DATA;
+ return ref;
+ }
} else {
if (!FileAccess::exists(p_path)) {
r_error = ERR_FILE_NOT_FOUND;
@@ -133,7 +137,6 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP
ref->path = p_path;
singleton->parser_map[p_path] = ref.ptr();
}
-
r_error = ref->raise_status(p_status);
return ref;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 117ca68c18..108c988add 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -141,10 +141,13 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
} break;
case GDScriptParser::DataType::ENUM:
- case GDScriptParser::DataType::ENUM_VALUE:
result.has_type = true;
result.kind = GDScriptDataType::BUILTIN;
- result.builtin_type = Variant::INT;
+ if (p_datatype.is_meta_type) {
+ result.builtin_type = Variant::DICTIONARY;
+ } else {
+ result.builtin_type = Variant::INT;
+ }
break;
case GDScriptParser::DataType::UNRESOLVED: {
ERR_PRINT("Parser bug: converting unresolved type.");
@@ -469,7 +472,14 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} break;
case GDScriptParser::Node::CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
- GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type->get_datatype());
+ GDScriptParser::DataType og_cast_type = cn->cast_type->get_datatype();
+ GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type);
+
+ if (og_cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Enum types are usually treated as dictionaries, but in this case we want to cast to an integer.
+ cast_type.kind = GDScriptDataType::BUILTIN;
+ cast_type.builtin_type = Variant::INT;
+ }
// Create temporary for result first since it will be deleted last.
GDScriptCodeGenerator::Address result = codegen.add_temporary(cast_type);
@@ -2019,7 +2029,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
codegen.generator->start_parameters();
for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
- GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value, true);
+ GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value);
if (r_error) {
memdelete(codegen.generator);
return nullptr;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index d10e120410..33a88dd2dd 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -486,7 +486,7 @@ struct GDScriptCompletionIdentifier {
static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
String enum_name = p_info.class_name;
- if (enum_name.find(".") == -1) {
+ if (!enum_name.contains(".")) {
return enum_name;
}
return enum_name.get_slice(".", 1);
@@ -610,7 +610,7 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
case GDScriptParser::Node::SUBSCRIPT: {
const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->default_value);
if (sub->is_constant) {
- if (sub->datatype.kind == GDScriptParser::DataType::ENUM_VALUE) {
+ if (sub->datatype.kind == GDScriptParser::DataType::ENUM) {
def_val = sub->get_datatype().to_string();
} else if (sub->reduced) {
const Variant::Type vt = sub->reduced_value.get_type();
@@ -955,7 +955,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
continue;
}
- if (E.name.find("/") != -1) {
+ if (E.name.contains("/")) {
continue;
}
ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
@@ -1000,7 +1000,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
for (const PropertyInfo &E : members) {
- if (String(E.name).find("/") == -1) {
+ if (!String(E.name).contains("/")) {
ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
r_result.insert(option.display, option);
}
@@ -2182,7 +2182,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
}
static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, Map<String, ScriptCodeCompletionOption> &r_result) {
- if (p_enum_hint.find(".") == -1) {
+ if (!p_enum_hint.contains(".")) {
// Global constant or in the current class.
StringName current_enum = p_enum_hint;
if (p_context.current_class && p_context.current_class->has_member(current_enum) && p_context.current_class->get_member(current_enum).type == GDScriptParser::ClassNode::Member::ENUM) {
@@ -2291,7 +2291,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
}
- if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, "InputEvent") && p_method.operator String().find("action") != -1) {
+ if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, "InputEvent") && p_method.operator String().contains("action")) {
// Get input actions
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2639,7 +2639,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
ClassDB::get_virtual_methods(class_name, &virtual_methods);
for (const MethodInfo &mi : virtual_methods) {
String method_hint = mi.name;
- if (method_hint.find(":") != -1) {
+ if (method_hint.contains(":")) {
method_hint = method_hint.get_slice(":", 0);
}
method_hint += "(";
@@ -2650,7 +2650,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
method_hint += ", ";
}
String arg = mi.arguments[i].name;
- if (arg.find(":") != -1) {
+ if (arg.contains(":")) {
arg = arg.substr(0, arg.find(":"));
}
method_hint += arg;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index f1877df326..9424de9d22 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -248,7 +248,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
// If the return value is a GDScriptFunctionState reference,
// then the function did await again after resuming.
- if (ret.is_ref()) {
+ if (ret.is_ref_counted()) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
completed = false;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 10f1dd0a41..cfad832a6c 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -2105,7 +2105,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
while (p_precedence <= get_rule(current.type)->precedence) {
- if (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) {
+ if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL)) {
return previous_operand;
}
// Also switch multiline mode on here for infix operators.
@@ -2415,6 +2415,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
push_error("Assignment is not allowed inside an expression.");
return parse_expression(false); // Return the following expression.
}
+ if (p_previous_operand == nullptr) {
+ return parse_expression(false); // Return the following expression.
+ }
#ifdef DEBUG_ENABLED
VariableNode *source_variable = nullptr;
@@ -3737,8 +3740,6 @@ String GDScriptParser::DataType::to_string() const {
}
case ENUM:
return enum_type.operator String() + " (enum)";
- case ENUM_VALUE:
- return enum_type.operator String() + " (enum value)";
case UNRESOLVED:
return "<unresolved type>";
}
@@ -4236,7 +4237,11 @@ void GDScriptParser::TreePrinter::print_get_node(GetNodeNode *p_get_node) {
}
void GDScriptParser::TreePrinter::print_identifier(IdentifierNode *p_identifier) {
- push_text(p_identifier->name);
+ if (p_identifier != nullptr) {
+ push_text(p_identifier->name);
+ } else {
+ push_text("<invalid identifier>");
+ }
}
void GDScriptParser::TreePrinter::print_if(IfNode *p_if, bool p_is_elif) {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index e4311d2d5e..c09b07282f 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -106,8 +106,7 @@ public:
NATIVE,
SCRIPT,
CLASS, // GDScript.
- ENUM, // Full enumeration.
- ENUM_VALUE, // Value from enumeration.
+ ENUM, // Enumeration.
VARIANT, // Can be any type.
UNRESOLVED,
};
@@ -185,8 +184,6 @@ public:
return builtin_type == p_other.builtin_type;
case NATIVE:
case ENUM:
- return native_type == p_other.native_type;
- case ENUM_VALUE:
return native_type == p_other.native_type && enum_type == p_other.enum_type;
case SCRIPT:
return script_type == p_other.script_type;
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 05ea061798..d3287ab345 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -312,22 +312,6 @@ GDScriptTokenizer::Token GDScriptTokenizer::pop_error() {
return error;
}
-static bool _is_alphanumeric(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
-}
-
-static bool _is_digit(char32_t c) {
- return (c >= '0' && c <= '9');
-}
-
-static bool _is_hex_digit(char32_t c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-
-static bool _is_binary_digit(char32_t c) {
- return (c == '0' || c == '1');
-}
-
GDScriptTokenizer::Token GDScriptTokenizer::make_token(Token::Type p_type) {
Token token(p_type);
token.start_line = start_line;
@@ -448,10 +432,10 @@ GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, To
}
GDScriptTokenizer::Token GDScriptTokenizer::annotation() {
- if (!_is_alphanumeric(_peek())) {
+ if (!is_ascii_identifier_char(_peek())) {
push_error("Expected annotation identifier after \"@\".");
}
- while (_is_alphanumeric(_peek())) {
+ while (is_ascii_identifier_char(_peek())) {
// Consume all identifier characters.
_advance();
}
@@ -526,7 +510,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
#define MAX_KEYWORD_LENGTH 10
// Consume all alphanumeric characters.
- while (_is_alphanumeric(_peek())) {
+ while (is_ascii_identifier_char(_peek())) {
_advance();
}
@@ -612,7 +596,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
bool has_decimal = false;
bool has_exponent = false;
bool has_error = false;
- bool (*digit_check_func)(char32_t) = _is_digit;
+ bool (*digit_check_func)(char32_t) = is_digit;
if (_peek(-1) == '.') {
has_decimal = true;
@@ -620,20 +604,20 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
if (_peek() == 'x') {
// Hexadecimal.
base = 16;
- digit_check_func = _is_hex_digit;
+ digit_check_func = is_hex_digit;
_advance();
} else if (_peek() == 'b') {
// Binary.
base = 2;
- digit_check_func = _is_binary_digit;
+ digit_check_func = is_binary_digit;
_advance();
}
}
// Allow '_' to be used in a number, for readability.
bool previous_was_underscore = false;
- while (digit_check_func(_peek()) || _peek() == '_') {
- if (_peek() == '_') {
+ while (digit_check_func(_peek()) || is_underscore(_peek())) {
+ if (is_underscore(_peek())) {
if (previous_was_underscore) {
Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
error.start_column = column;
@@ -682,7 +666,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
_advance();
// Consume decimal digits.
- while (_is_digit(_peek()) || _peek() == '_') {
+ while (is_digit(_peek()) || is_underscore(_peek())) {
_advance();
}
}
@@ -696,7 +680,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
_advance();
}
// Consume exponent digits.
- if (!_is_digit(_peek())) {
+ if (!is_digit(_peek())) {
Token error = make_error(R"(Expected exponent value after "e".)");
error.start_column = column;
error.leftmost_column = column;
@@ -705,8 +689,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
push_error(error);
}
previous_was_underscore = false;
- while (_is_digit(_peek()) || _peek() == '_') {
- if (_peek() == '_') {
+ while (is_digit(_peek()) || is_underscore(_peek())) {
+ if (is_underscore(_peek())) {
if (previous_was_underscore) {
Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
error.start_column = column;
@@ -733,7 +717,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
error.rightmost_column = column + 1;
push_error(error);
has_error = true;
- } else if (_is_alphanumeric(_peek())) {
+ } else if (is_ascii_identifier_char(_peek())) {
// Letter at the end of the number.
push_error("Invalid numeric notation.");
}
@@ -786,6 +770,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
}
String result;
+ char32_t prev = 0;
+ int prev_pos = 0;
for (;;) {
// Consume actual string.
@@ -852,16 +838,18 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
case '\\':
escaped = '\\';
break;
- case 'u':
+ case 'U':
+ case 'u': {
// Hexadecimal sequence.
- for (int i = 0; i < 4; i++) {
+ int hex_len = (code == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
if (_is_at_end()) {
return make_error("Unterminated string.");
}
char32_t digit = _peek();
char32_t value = 0;
- if (digit >= '0' && digit <= '9') {
+ if (is_digit(digit)) {
value = digit - '0';
} else if (digit >= 'a' && digit <= 'f') {
value = digit - 'a';
@@ -886,7 +874,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
_advance();
}
- break;
+ } break;
case '\r':
if (_peek() != '\n') {
// Carriage return without newline in string. (???)
@@ -909,11 +897,53 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
valid_escape = false;
break;
}
+ // Parse UTF-16 pair.
+ if (valid_escape) {
+ if ((escaped & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = escaped;
+ prev_pos = column - 2;
+ continue;
+ } else {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = column - 2;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ valid_escape = false;
+ prev = 0;
+ }
+ } else if ((escaped & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired trail surrogate");
+ error.start_column = column - 2;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ valid_escape = false;
+ } else {
+ escaped = (prev << 10UL) + escaped - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
+ }
if (valid_escape) {
result += escaped;
}
} else if (ch == quote_char) {
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
_advance();
if (is_multiline) {
if (_peek() == quote_char && _peek(1) == quote_char) {
@@ -930,6 +960,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
break;
}
} else {
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
result += ch;
_advance();
if (ch == '\n') {
@@ -937,6 +974,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
}
}
}
+ if (prev != 0) {
+ Token error = make_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ error.start_column = prev_pos;
+ error.leftmost_column = error.start_column;
+ push_error(error);
+ prev = 0;
+ }
// Make the literal.
Variant string;
@@ -1262,9 +1306,9 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
line_continuation = false;
- if (_is_digit(c)) {
+ if (is_digit(c)) {
return number();
- } else if (_is_alphanumeric(c)) {
+ } else if (is_ascii_identifier_char(c)) {
return potential_identifier();
}
@@ -1332,7 +1376,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
if (_peek() == '.') {
_advance();
return make_token(Token::PERIOD_PERIOD);
- } else if (_is_digit(_peek())) {
+ } else if (is_digit(_peek())) {
// Number starting with '.'.
return number();
} else {
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index e0facaf61d..95122714f9 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -1532,7 +1532,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
} else if (methodstr == "free") {
if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- if (base->is_ref()) {
+ if (base->is_ref_counted()) {
err_text = "Attempted to free a reference.";
OPCODE_BREAK;
} else if (base->get_type() == Variant::OBJECT) {
@@ -1620,7 +1620,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
} else if (methodstr == "free") {
if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- if (base->is_ref()) {
+ if (base->is_ref_counted()) {
err_text = "Attempted to free a reference.";
OPCODE_BREAK;
} else if (base->get_type() == Variant::OBJECT) {
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 73536f5f8e..ad96e36640 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -152,6 +152,9 @@ String GDScriptWarning::get_message() const {
CHECK_SYMBOLS(3);
return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
}
+ case INT_ASSIGNED_TO_ENUM: {
+ return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type.";
+ }
case WARNING_MAX:
break; // Can't happen, but silences warning
}
@@ -199,6 +202,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"REDUNDANT_AWAIT",
"EMPTY_FILE",
"SHADOWED_GLOBAL_IDENTIFIER",
+ "INT_ASSIGNED_TO_ENUM",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 112b40781a..82efe3568f 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -70,6 +70,7 @@ public:
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
EMPTY_FILE, // A script file is empty.
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
+ INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting.
WARNING_MAX,
};
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 49f5303ae6..17886181d5 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -541,7 +541,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
for (int c = p_position.character; c >= 0; c--) {
start_pos = c;
char32_t ch = line[c];
- bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
+ bool valid_char = is_ascii_identifier_char(ch);
if (!valid_char) {
break;
}
@@ -550,7 +550,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
int end_pos = p_position.character;
for (int c = p_position.character; c < line.length(); c++) {
char32_t ch = line[c];
- bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
+ bool valid_char = is_ascii_identifier_char(ch);
if (!valid_char) {
break;
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index a944844226..d20b243616 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -63,7 +63,7 @@ void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStr
String function_signature = "func " + function;
String source = script->get_source_code();
- if (source.find(function_signature) != -1) {
+ if (source.contains(function_signature)) {
return;
}
@@ -269,7 +269,7 @@ Array GDScriptWorkspace::symbol(const Dictionary &p_params) {
Vector<lsp::DocumentedSymbolInformation> script_symbols;
E.value->get_symbols().symbol_tree_as_list(E.key, script_symbols);
for (int i = 0; i < script_symbols.size(); ++i) {
- if (query.is_subsequence_ofi(script_symbols[i].name)) {
+ if (query.is_subsequence_ofn(script_symbols[i].name)) {
lsp::DocumentedSymbolInformation symbol = script_symbols[i];
symbol.location.uri = get_file_uri(symbol.location.uri);
arr.push_back(symbol.to_json());
@@ -380,7 +380,7 @@ Error GDScriptWorkspace::initialize() {
symbol.children.push_back(symbol_arg);
}
- if (data.qualifiers.find("vararg") != -1) {
+ if (data.qualifiers.contains("vararg")) {
params += params.is_empty() ? "..." : ", ...";
}
@@ -585,7 +585,7 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
stack.push_back(owner_scene_node);
while (!stack.is_empty()) {
- current = stack.pop_back();
+ current = Object::cast_to<Node>(stack.pop_back());
Ref<GDScript> script = current->get_script();
if (script.is_valid() && script->get_path() == path) {
break;
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 8e503a9df9..a63f9df918 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -1940,7 +1940,7 @@ static String marked_documentation(const String &p_bbcode) {
line = "\t" + line.substr(code_block_indent, line.length());
}
- if (in_code_block && line.find("[/codeblock]") != -1) {
+ if (in_code_block && line.contains("[/codeblock]")) {
line = "\n";
in_code_block = false;
}
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 47772b8039..73a424dae4 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -134,12 +134,14 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l
if (do_init_languages) {
init_language(p_source_dir);
}
+#ifdef DEBUG_ENABLED
// Enable all warnings for GDScript, so we can test them.
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
}
+#endif
// Enable printing to show results
_print_line_enabled = true;
@@ -153,6 +155,21 @@ GDScriptTestRunner::~GDScriptTestRunner() {
}
}
+#ifndef DEBUG_ENABLED
+static String strip_warnings(const String &p_expected) {
+ // On release builds we don't have warnings. Here we remove them from the output before comparison
+ // so it doesn't fail just because of difference in warnings.
+ String expected_no_warnings;
+ for (String line : p_expected.split("\n")) {
+ if (line.begins_with(">> ")) {
+ continue;
+ }
+ expected_no_warnings += line + "\n";
+ }
+ return expected_no_warnings.strip_edges() + "\n";
+}
+#endif
+
int GDScriptTestRunner::run_tests() {
if (!make_tests()) {
FAIL("An error occurred while making the tests.");
@@ -170,6 +187,9 @@ int GDScriptTestRunner::run_tests() {
GDScriptTest::TestResult result = test.run_test();
String expected = FileAccess::get_file_as_string(test.get_output_file());
+#ifndef DEBUG_ENABLED
+ expected = strip_warnings(expected);
+#endif
INFO(test.get_source_file());
if (!result.passed) {
INFO(expected);
@@ -233,6 +253,22 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
}
} else {
if (next.get_extension().to_lower() == "gd") {
+#ifndef DEBUG_ENABLED
+ // On release builds, skip tests marked as debug only.
+ Error open_err = OK;
+ FileAccessRef script_file(FileAccess::open(current_dir.plus_file(next), FileAccess::READ, &open_err));
+ if (open_err != OK) {
+ ERR_PRINT(vformat(R"(Couldn't open test file "%s".)", next));
+ next = dir->get_next();
+ continue;
+ } else {
+ if (script_file->get_line() == "#debug-only") {
+ next = dir->get_next();
+ continue;
+ }
+ }
+#endif
+
String out_file = next.get_basename() + ".out";
if (!is_generating && !dir->file_exists(out_file)) {
ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
@@ -387,6 +423,10 @@ bool GDScriptTest::check_output(const String &p_output) const {
String got = p_output.strip_edges(); // TODO: may be hacky.
got += "\n"; // Make sure to insert newline for CI static checks.
+#ifndef DEBUG_ENABLED
+ expected = strip_warnings(expected);
+#endif
+
return got == expected;
}
@@ -469,6 +509,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
return result;
}
+#ifdef DEBUG_ENABLED
StringBuilder warning_string;
for (const GDScriptWarning &E : parser.get_warnings()) {
const GDScriptWarning warning = E;
@@ -482,6 +523,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
warning_string.append("\n");
}
result.output += warning_string.as_string();
+#endif
// Test compiling.
GDScriptCompiler compiler;
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..928c886650
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd
@@ -0,0 +1,10 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+# Different enum types can't be assigned without casting.
+var class_var: MyEnum = MyEnum.ENUM_VALUE_1
+
+func test():
+ print(class_var)
+ class_var = MyOtherEnum.OTHER_ENUM_VALUE_2
+ print(class_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
new file mode 100644
index 0000000000..fde7e92f8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..03a1711d7b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+# Different enum types can't be assigned without casting.
+var class_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1
+
+func test():
+ print(class_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
new file mode 100644
index 0000000000..b1710c798d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..d08d3dd7b2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = MyOtherEnum.OTHER_ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
new file mode 100644
index 0000000000..fde7e92f8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..ca6d892218
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd
@@ -0,0 +1,6 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
new file mode 100644
index 0000000000..b1710c798d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.gd b/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.gd
new file mode 100644
index 0000000000..05d9bd6a3d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.gd
@@ -0,0 +1,9 @@
+# https://github.com/godotengine/godot/issues/56702
+
+func test():
+ # somewhat obscure feature: referencing parameters in defaults, but only earlier ones!
+ ref_default("non-optional")
+
+
+func ref_default(nondefault1, defa=nondefault1, defb=defc, defc=1):
+ prints(nondefault1, nondefault2, defa, defb, defc)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.out b/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.out
new file mode 100644
index 0000000000..1d5b5bf393
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/params_default_forward_reference.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Identifier "defc" not declared in the current scope.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd
new file mode 100644
index 0000000000..edb785c8b6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd
@@ -0,0 +1,13 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+var class_var: int = MyEnum.ENUM_VALUE_1
+
+func test():
+ print(class_var)
+ class_var = MyEnum.ENUM_VALUE_2
+ print(class_var)
+
+ var local_var: int = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = MyEnum.ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd
new file mode 100644
index 0000000000..726e4fd413
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd
@@ -0,0 +1,13 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+var class_var: MyEnum = 0 as MyEnum
+
+func test():
+ print(class_var)
+ class_var = 1 as MyEnum
+ print(class_var)
+
+ var local_var: MyEnum = 0 as MyEnum
+ print(local_var)
+ local_var = 1 as MyEnum
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd
new file mode 100644
index 0000000000..798912c987
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd
@@ -0,0 +1,14 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+var class_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1 as MyEnum
+
+func test():
+ print(class_var)
+ class_var = MyOtherEnum.OTHER_ENUM_VALUE_2 as MyEnum
+ print(class_var)
+
+ var local_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1 as MyEnum
+ print(local_var)
+ local_var = MyOtherEnum.OTHER_ENUM_VALUE_2 as MyEnum
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd
new file mode 100644
index 0000000000..2bfb318c3c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd
@@ -0,0 +1,13 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+var class_var: MyEnum = MyEnum.ENUM_VALUE_1
+
+func test():
+ print(class_var)
+ class_var = MyEnum.ENUM_VALUE_2
+ print(class_var)
+
+ var local_var: MyEnum = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = MyEnum.ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd
new file mode 100644
index 0000000000..7022d14566
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd
@@ -0,0 +1,21 @@
+# Enum is equivalent to int for comparisons and operations.
+enum MyEnum {
+ ZERO,
+ ONE,
+ TWO,
+}
+
+enum OtherEnum {
+ ZERO,
+ ONE,
+ TWO,
+}
+
+func test():
+ print(MyEnum.ZERO == OtherEnum.ZERO)
+ print(MyEnum.ZERO == 1)
+ print(MyEnum.ZERO != OtherEnum.ONE)
+ print(MyEnum.ZERO != 0)
+
+ print(MyEnum.ONE + OtherEnum.TWO)
+ print(2 - MyEnum.ONE)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out
new file mode 100644
index 0000000000..c8f34c11db
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+true
+false
+true
+false
+3
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd
new file mode 100644
index 0000000000..885d70408a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd
@@ -0,0 +1,13 @@
+enum MyEnum {
+ ZERO,
+ ONE,
+ TWO,
+}
+
+func test():
+ for key in MyEnum.keys():
+ prints(key, MyEnum[key])
+
+ # https://github.com/godotengine/godot/issues/55491
+ for key in MyEnum:
+ prints(key, MyEnum[key])
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out
new file mode 100644
index 0000000000..d29f53109c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+ZERO 0
+ONE 1
+TWO 2
+ZERO 0
+ONE 1
+TWO 2
diff --git a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.gd b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.gd
new file mode 100644
index 0000000000..e9690ee93d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.gd
@@ -0,0 +1,2 @@
+func test():
+ $=$
diff --git a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
new file mode 100644
index 0000000000..b3dc181a22
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expect node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
index 447d7e223c..5b0ea9df43 100644
--- a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
@@ -1,2 +1,2 @@
GDTEST_OK
-{2:4, a:1, b:2, with spaces:3}
+{a:1, b:2, with spaces:3, 2:4}
diff --git a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd
new file mode 100644
index 0000000000..2be1024214
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd
@@ -0,0 +1,15 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+# Assigning int value to enum-typed variable without explicit cast causes a warning.
+# While it is valid it may be a mistake in the assignment.
+var class_var: MyEnum = 0
+
+func test():
+ print(class_var)
+ class_var = 1
+ print(class_var)
+
+ var local_var: MyEnum = 0
+ print(local_var)
+ local_var = 1
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out
new file mode 100644
index 0000000000..eef13bbff8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out
@@ -0,0 +1,21 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+>> WARNING
+>> Line: 9
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+>> WARNING
+>> Line: 12
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+>> WARNING
+>> Line: 14
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
index 10780b5379..7b3c112fe9 100644
--- a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
@@ -1,3 +1,4 @@
+#debug-only
func test():
var node := Node.new()
var inside_tree = node.is_inside_tree
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
index e585c374e2..fe48ade26b 100644
--- a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
@@ -2,5 +2,5 @@ GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/callable_call_after_free_object.gd
->> 5
+>> 6
>> Attempt to call function 'null::is_inside_tree (Callable)' on a null instance.
diff --git a/modules/gdscript/tests/scripts/runtime/features/params_default_values.gd b/modules/gdscript/tests/scripts/runtime/features/params_default_values.gd
new file mode 100644
index 0000000000..8156b4ec68
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/params_default_values.gd
@@ -0,0 +1,35 @@
+# https://github.com/godotengine/godot/issues/56702
+
+func test():
+ const_default()
+ func_result_default()
+ # calling again will run the initializer again,
+ # as the default is not evaluated at time of defining the function (as in python)
+ # but every time the function is called (as in C++)
+ func_result_default()
+ lots_of_defaults("non-optional")
+ # somewhat obscure feature: referencing earlier parameters
+ ref_default("non-optional", 42)
+
+
+func const_default(param=42):
+ print(param)
+
+
+var default_val := 0
+
+func get_default():
+ default_val += 1
+ return default_val
+
+
+func func_result_default(param=get_default()):
+ print(param)
+
+
+func lots_of_defaults(nondefault, one=1, two=2, three=get_default()):
+ prints(nondefault, one, two, three)
+
+
+func ref_default(nondefault1, nondefault2, defa=nondefault1, defb=nondefault2 - 1):
+ prints(nondefault1, nondefault2, defa, defb)
diff --git a/modules/gdscript/tests/scripts/runtime/features/params_default_values.out b/modules/gdscript/tests/scripts/runtime/features/params_default_values.out
new file mode 100644
index 0000000000..50e0885ae5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/params_default_values.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+42
+1
+2
+non-optional 1 2 3
+non-optional 42 non-optional 41
diff --git a/modules/glslang/glslang_resource_limits.h b/modules/glslang/glslang_resource_limits.h
index 05390f95ad..02d3daff07 100644
--- a/modules/glslang/glslang_resource_limits.h
+++ b/modules/glslang/glslang_resource_limits.h
@@ -132,15 +132,15 @@ const TBuiltInResource DefaultTBuiltInResource = {
/* .maxDualSourceDrawBuffersEXT = */ 1,
/* .limits = */ {
- /* .nonInductiveForLoops = */ 1,
- /* .whileLoops = */ 1,
- /* .doWhileLoops = */ 1,
- /* .generalUniformIndexing = */ 1,
- /* .generalAttributeMatrixVectorIndexing = */ 1,
- /* .generalVaryingIndexing = */ 1,
- /* .generalSamplerIndexing = */ 1,
- /* .generalVariableIndexing = */ 1,
- /* .generalConstantMatrixVectorIndexing = */ 1,
+ /* .nonInductiveForLoops = */ true,
+ /* .whileLoops = */ true,
+ /* .doWhileLoops = */ true,
+ /* .generalUniformIndexing = */ true,
+ /* .generalAttributeMatrixVectorIndexing = */ true,
+ /* .generalVaryingIndexing = */ true,
+ /* .generalSamplerIndexing = */ true,
+ /* .generalVariableIndexing = */ true,
+ /* .generalConstantMatrixVectorIndexing = */ true,
}
};
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 5a931ed839..baa39a3b80 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -50,6 +50,7 @@
#include "core/io/file_access.h"
#include "core/io/file_access_memory.h"
#include "core/io/json.h"
+#include "core/io/stream_peer.h"
#include "core/math/disjoint_set.h"
#include "core/math/vector2.h"
#include "core/variant/dictionary.h"
@@ -6277,7 +6278,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
p_track.rotation_track.values.write[key_i] = rotation;
}
} else if (track_type == Animation::TYPE_VALUE) {
- if (path.find(":position") != -1) {
+ if (path.contains(":position")) {
p_track.position_track.times = times;
p_track.position_track.interpolation = gltf_interpolation;
@@ -6288,7 +6289,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
Vector3 position = p_animation->track_get_key_value(p_track_i, key_i);
p_track.position_track.values.write[key_i] = position;
}
- } else if (path.find(":rotation") != -1) {
+ } else if (path.contains(":rotation")) {
p_track.rotation_track.times = times;
p_track.rotation_track.interpolation = gltf_interpolation;
@@ -6299,7 +6300,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i);
p_track.rotation_track.values.write[key_i] = Quaternion(rotation_radian);
}
- } else if (path.find(":scale") != -1) {
+ } else if (path.contains(":scale")) {
p_track.scale_track.times = times;
p_track.scale_track.interpolation = gltf_interpolation;
@@ -6312,7 +6313,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
}
}
} else if (track_type == Animation::TYPE_BEZIER) {
- if (path.find("/scale") != -1) {
+ if (path.contains("/scale")) {
const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS;
if (!p_track.scale_track.times.size()) {
Vector<real_t> new_times;
@@ -6333,16 +6334,16 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.scale_track.values[key_i];
- if (path.find("/scale:x") != -1) {
+ if (path.contains("/scale:x")) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/scale:y") != -1) {
+ } else if (path.contains("/scale:y")) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/scale:z") != -1) {
+ } else if (path.contains("/scale:z")) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
}
p_track.scale_track.values.write[key_i] = bezier_track;
}
- } else if (path.find("/position") != -1) {
+ } else if (path.contains("/position")) {
const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS;
if (!p_track.position_track.times.size()) {
Vector<real_t> new_times;
@@ -6359,11 +6360,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.position_track.values[key_i];
- if (path.find("/position:x") != -1) {
+ if (path.contains("/position:x")) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/position:y") != -1) {
+ } else if (path.contains("/position:y")) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/position:z") != -1) {
+ } else if (path.contains("/position:z")) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
}
p_track.position_track.values.write[key_i] = bezier_track;
@@ -6384,7 +6385,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
continue;
}
String orig_track_path = animation->track_get_path(track_i);
- if (String(orig_track_path).find(":position") != -1) {
+ if (String(orig_track_path).contains(":position")) {
const Vector<String> node_suffix = String(orig_track_path).split(":position");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6400,7 +6401,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(node_index, track);
}
}
- } else if (String(orig_track_path).find(":rotation_degrees") != -1) {
+ } else if (String(orig_track_path).contains(":rotation_degrees")) {
const Vector<String> node_suffix = String(orig_track_path).split(":rotation_degrees");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6416,7 +6417,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(node_index, track);
}
}
- } else if (String(orig_track_path).find(":scale") != -1) {
+ } else if (String(orig_track_path).contains(":scale")) {
const Vector<String> node_suffix = String(orig_track_path).split(":scale");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6432,7 +6433,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(node_index, track);
}
}
- } else if (String(orig_track_path).find(":transform") != -1) {
+ } else if (String(orig_track_path).contains(":transform")) {
const Vector<String> node_suffix = String(orig_track_path).split(":transform");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6443,7 +6444,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(transform_track_i.key, track);
}
}
- } else if (String(orig_track_path).find(":blend_shapes/") != -1) {
+ } else if (String(orig_track_path).contains(":blend_shapes/")) {
const Vector<String> node_suffix = String(orig_track_path).split(":blend_shapes/");
const NodePath path = node_suffix[0];
const String suffix = node_suffix[1];
@@ -6499,7 +6500,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
tracks[mesh_index] = track;
}
- } else if (String(orig_track_path).find(":") != -1) {
+ } else if (String(orig_track_path).contains(":")) {
//Process skeleton
const Vector<String> node_suffix = String(orig_track_path).split(":");
const String node = node_suffix[0];
@@ -6529,7 +6530,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks()[node_i] = track;
}
}
- } else if (String(orig_track_path).find(":") == -1) {
+ } else if (!String(orig_track_path).contains(":")) {
ERR_CONTINUE(!ap->get_parent());
Node *godot_node = ap->get_parent()->get_node_or_null(orig_track_path);
for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : state->scene_nodes) {
@@ -6843,6 +6844,7 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_pa
}
Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
+ ERR_FAIL_NULL_V(state, nullptr);
ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr);
GLTFNodeIndex gltf_root = state->root_nodes.write[0];
Node *gltf_root_node = state->get_scene_node(gltf_root);
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 885817caf1..9c28421a46 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -67,7 +67,7 @@
Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
- <method name="get_meshes">
+ <method name="get_meshes" qualifiers="const">
<return type="Array" />
<description>
Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space.
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index a861efcbf4..67deedf839 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -35,6 +35,7 @@
#include "scene/3d/light_3d.h"
#include "scene/resources/mesh_library.h"
#include "scene/resources/physics_material.h"
+#include "scene/resources/primitive_meshes.h"
#include "scene/resources/surface_tool.h"
#include "scene/scene_string_names.h"
#include "servers/navigation_server_3d.h"
@@ -197,6 +198,24 @@ bool GridMap::get_collision_mask_value(int p_layer_number) const {
return get_collision_mask() & (1 << (p_layer_number - 1));
}
+Array GridMap::get_collision_shapes() const {
+ Array shapes;
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+ Octant *g = E.value;
+ RID body = g->static_body;
+ Transform3D body_xform = PhysicsServer3D::get_singleton()->body_get_state(body, PhysicsServer3D::BODY_STATE_TRANSFORM);
+ int nshapes = PhysicsServer3D::get_singleton()->body_get_shape_count(body);
+ for (int i = 0; i < nshapes; i++) {
+ RID shape = PhysicsServer3D::get_singleton()->body_get_shape(body, i);
+ Transform3D xform = PhysicsServer3D::get_singleton()->body_get_shape_transform(body, i);
+ shapes.push_back(body_xform * xform);
+ shapes.push_back(shape);
+ }
+ }
+
+ return shapes;
+}
+
void GridMap::set_bake_navigation(bool p_bake_navigation) {
bake_navigation = p_bake_navigation;
_recreate_octant_data();
@@ -930,7 +949,7 @@ Array GridMap::get_used_cells() const {
return a;
}
-Array GridMap::get_meshes() {
+Array GridMap::get_meshes() const {
if (mesh_library.is_null()) {
return Array();
}
@@ -938,7 +957,7 @@ Array GridMap::get_meshes() {
Vector3 ofs = _get_offset();
Array meshes;
- for (KeyValue<IndexKey, Cell> &E : cell_map) {
+ for (const KeyValue<IndexKey, Cell> &E : cell_map) {
int id = E.value.item;
if (!mesh_library->has_item(id)) {
continue;
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index 546b530148..6cdc3b178d 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -229,6 +229,8 @@ public:
void set_physics_material(Ref<PhysicsMaterial> p_material);
Ref<PhysicsMaterial> get_physics_material() const;
+ Array get_collision_shapes() const;
+
void set_bake_navigation(bool p_bake_navigation);
bool is_baking_navigation();
@@ -265,7 +267,7 @@ public:
Array get_used_cells() const;
- Array get_meshes();
+ Array get_meshes() const;
void clear_baked_meshes();
void make_baked_meshes(bool p_gen_lightmap_uv = false, float p_lightmap_uv_texel_size = 0.1);
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 5ef48f8645..84510fc71e 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -889,7 +889,7 @@ void GridMapEditor::update_palette() {
name = "#" + itos(id);
}
- if (!filter.is_empty() && !filter.is_subsequence_ofi(name)) {
+ if (!filter.is_empty() && !filter.is_subsequence_ofn(name)) {
continue;
}
@@ -1459,10 +1459,10 @@ void GridMapEditorPlugin::_notification(int p_what) {
if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) {
case 0: { // Left.
- Node3DEditor::get_singleton()->get_palette_split()->move_child(grid_map_editor, 0);
+ Node3DEditor::get_singleton()->move_control_to_left_panel(grid_map_editor);
} break;
case 1: { // Right.
- Node3DEditor::get_singleton()->get_palette_split()->move_child(grid_map_editor, 1);
+ Node3DEditor::get_singleton()->move_control_to_right_panel(grid_map_editor);
} break;
}
}
@@ -1498,10 +1498,10 @@ GridMapEditorPlugin::GridMapEditorPlugin(EditorNode *p_node) {
grid_map_editor = memnew(GridMapEditor(editor));
switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) {
case 0: { // Left.
- add_control_to_container(CONTAINER_SPATIAL_EDITOR_SIDE_LEFT, grid_map_editor);
+ Node3DEditor::get_singleton()->add_control_to_left_panel(grid_map_editor);
} break;
case 1: { // Right.
- add_control_to_container(CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT, grid_map_editor);
+ Node3DEditor::get_singleton()->add_control_to_right_panel(grid_map_editor);
} break;
}
grid_map_editor->hide();
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index 59854ad527..8cd23ffb24 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -181,6 +181,7 @@ void MobileVRInterface::set_position_from_sensors() {
orientation = rotate * orientation;
tracking_state = XRInterface::XR_NORMAL_TRACKING;
+ tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
};
///@TODO improve this, the magnetometer is very fidgety sometimes flipping the axis for no apparent reason (probably a bug on my part)
@@ -193,6 +194,7 @@ void MobileVRInterface::set_position_from_sensors() {
orientation = Basis(transform_quat);
tracking_state = XRInterface::XR_NORMAL_TRACKING;
+ tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
} else if (has_grav) {
// use gravity vector to make sure down is down...
// transform gravity into our world space
@@ -461,7 +463,7 @@ CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, double
return eye;
};
-Vector<BlitToScreen> MobileVRInterface::commit_views(RID p_render_target, const Rect2 &p_screen_rect) {
+Vector<BlitToScreen> MobileVRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
_THREAD_SAFE_METHOD_
Vector<BlitToScreen> blit_to_screen;
@@ -512,7 +514,7 @@ void MobileVRInterface::process() {
if (head.is_valid()) {
// Set our head position, note in real space, reference frame and world scale is applied later
- head->set_pose("default", head_transform, Vector3(), Vector3());
+ head->set_pose("default", head_transform, Vector3(), Vector3(), tracking_confidence);
}
};
};
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index ac04763569..8ecca3a2ae 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -51,6 +51,7 @@ class MobileVRInterface : public XRInterface {
private:
bool initialized = false;
XRInterface::TrackingStatus tracking_state;
+ XRPose::TrackingConfidence tracking_confidence = XRPose::XR_TRACKING_CONFIDENCE_NONE;
// Just set some defaults for these. At some point we need to look at adding a lookup table for common device + headset combos and/or support reading cardboard QR codes
double eye_height = 1.85;
@@ -150,7 +151,7 @@ public:
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
- virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override;
+ virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override;
diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
index 93a66ebf6f..43c1ec8f8a 100644
--- a/modules/mono/build_scripts/mono_reg_utils.py
+++ b/modules/mono/build_scripts/mono_reg_utils.py
@@ -96,10 +96,10 @@ def find_msbuild_tools_path_reg():
raise ValueError("Cannot find `installationPath` entry")
except ValueError as e:
print("Error reading output from vswhere: " + e.message)
- except OSError:
- pass # Fine, vswhere not found
- except (subprocess.CalledProcessError, OSError):
- pass
+ except subprocess.CalledProcessError as e:
+ print(e.output)
+ except OSError as e:
+ print(e)
# Try to find 14.0 in the Registry
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 9d416dcfce..26436e3ec0 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1168,8 +1168,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#ifdef TOOLS_ENABLED
// FIXME: Hack to refresh editor in order to display new properties and signals. See if there is a better alternative.
if (Engine::get_singleton()->is_editor_hint()) {
- EditorNode::get_singleton()->get_inspector()->update_tree();
- NodeDock::singleton->update_lists();
+ InspectorDock::get_inspector_singleton()->update_tree();
+ NodeDock::get_singleton()->update_lists();
}
#endif
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 2be588cac4..2de923c125 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -103,8 +103,6 @@ private:
bool valid = false;
bool reload_invalidated = false;
- bool builtin;
-
GDMonoClass *base = nullptr;
GDMonoClass *native = nullptr;
GDMonoClass *script_class = nullptr;
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
index ce2b378623..7a4641dbbc 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -47,9 +47,13 @@ namespace GodotTools.OpenVisualStudio
if (dte == null)
{
// Open a new instance
+ dte = TryVisualStudioLaunch("VisualStudio.DTE.17.0");
- var visualStudioDteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", throwOnError: true);
- dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+ if (dte == null)
+ {
+ // Launch of VS 2022 failed, fallback to 2019
+ dte = TryVisualStudioLaunch("VisualStudio.DTE.16.0");
+ }
dte.UserControl = true;
@@ -133,6 +137,21 @@ namespace GodotTools.OpenVisualStudio
return 0;
}
+ private static DTE TryVisualStudioLaunch(string version)
+ {
+ try
+ {
+ var visualStudioDteType = Type.GetTypeFromProgID(version, throwOnError: true);
+ var dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+
+ return dte;
+ }
+ catch (COMException)
+ {
+ return null;
+ }
+ }
+
private static DTE FindInstanceEditingSolution(string solutionPath)
{
if (GetRunningObjectTable(0, out IRunningObjectTable pprot) != 0)
@@ -164,7 +183,7 @@ namespace GodotTools.OpenVisualStudio
continue;
// The digits after the colon are the process ID
- if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.16.0:[0-9]"))
+ if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.1[6-7].0:[0-9]"))
continue;
if (pprot.GetObject(moniker[0], out object ppunkObject) == 0)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index e9cf7911be..2dbc78ab77 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -147,7 +147,7 @@ namespace GodotTools.Build
Icon = GetThemeIcon("StatusError", "EditorIcons"),
ExpandIcon = false,
ToggleMode = true,
- Pressed = true,
+ ButtonPressed = true,
FocusMode = FocusModeEnum.None
};
_errorsBtn.Toggled += ErrorsToggled;
@@ -159,7 +159,7 @@ namespace GodotTools.Build
Icon = GetThemeIcon("NodeWarning", "EditorIcons"),
ExpandIcon = false,
ToggleMode = true,
- Pressed = true,
+ ButtonPressed = true,
FocusMode = FocusModeEnum.None
};
_warningsBtn.Toggled += WarningsToggled;
@@ -169,7 +169,7 @@ namespace GodotTools.Build
{
Text = "Show Output".TTR(),
ToggleMode = true,
- Pressed = true,
+ ButtonPressed = true,
FocusMode = FocusModeEnum.None
};
_viewLogBtn.Toggled += ViewLogToggled;
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 98c6881166..69960bdbeb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -428,7 +428,7 @@ namespace GodotTools
Shortcut = buildSolutionShortcut,
ShortcutInTooltip = true
};
- _toolBarBuildButton.PressedSignal += BuildSolutionPressed;
+ _toolBarBuildButton.Pressed += BuildSolutionPressed;
AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton);
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 1b4ab0ef4b..1de41821f9 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -690,11 +690,11 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI
continue;
}
- if (parts[curr_prefix_length][0] >= '0' && parts[curr_prefix_length][0] <= '9') {
+ if (is_digit(parts[curr_prefix_length][0])) {
// The name of enum constants may begin with a numeric digit when strip from the enum prefix,
// so we make the prefix for this constant one word shorter in those cases.
for (curr_prefix_length = curr_prefix_length - 1; curr_prefix_length > 0; curr_prefix_length--) {
- if (parts[curr_prefix_length][0] < '0' || parts[curr_prefix_length][0] > '9') {
+ if (!is_digit(parts[curr_prefix_length][0])) {
break;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index a89dca6c34..eba0ea9a79 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -727,7 +727,7 @@ namespace Godot
/// <summary>
/// Check whether this string is a subsequence of the given string.
/// </summary>
- /// <seealso cref="IsSubsequenceOfI(string, string)"/>
+ /// <seealso cref="IsSubsequenceOfN(string, string)"/>
/// <param name="instance">The subsequence to search.</param>
/// <param name="text">The string that contains the subsequence.</param>
/// <param name="caseSensitive">If <see langword="true"/>, the check is case sensitive.</param>
@@ -779,7 +779,7 @@ namespace Godot
/// <param name="instance">The subsequence to search.</param>
/// <param name="text">The string that contains the subsequence.</param>
/// <returns>If the string is a subsequence of the given string.</returns>
- public static bool IsSubsequenceOfI(this string instance, string text)
+ public static bool IsSubsequenceOfN(this string instance, string text)
{
return instance.IsSubsequenceOf(text, caseSensitive: false);
}
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 6cbdb645ca..52d5379e8b 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -140,12 +140,12 @@ void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, cons
}
}
-void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
+void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
if (Object::cast_to<MeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node);
Ref<Mesh> mesh = mesh_instance->get_mesh();
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * mesh_instance->get_global_transform(), p_vertices, p_indices);
}
}
@@ -159,7 +159,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
n = multimesh->get_instance_count();
}
for (int i = 0; i < n; i++) {
- _add_mesh(mesh, p_accumulated_transform * multimesh->get_instance_transform(i), p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices);
}
}
}
@@ -171,7 +171,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
if (!meshes.is_empty()) {
Ref<Mesh> mesh = meshes[1];
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_vertices, p_indices);
+ _add_mesh(mesh, p_navmesh_transform * csg_shape->get_global_transform(), p_vertices, p_indices);
}
}
}
@@ -186,7 +186,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
if (Object::cast_to<CollisionShape3D>(child)) {
CollisionShape3D *col_shape = Object::cast_to<CollisionShape3D>(child);
- Transform3D transform = p_accumulated_transform * static_body->get_transform() * col_shape->get_transform();
+ Transform3D transform = p_navmesh_transform * static_body->get_global_transform() * col_shape->get_transform();
Ref<Mesh> mesh;
Ref<Shape3D> s = col_shape->get_shape();
@@ -265,27 +265,108 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
}
#ifdef MODULE_GRIDMAP_ENABLED
- if (Object::cast_to<GridMap>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
- GridMap *gridmap_instance = Object::cast_to<GridMap>(p_node);
- Array meshes = gridmap_instance->get_meshes();
- Transform3D xform = gridmap_instance->get_transform();
- for (int i = 0; i < meshes.size(); i += 2) {
- Ref<Mesh> mesh = meshes[i + 1];
- if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
+ GridMap *gridmap = Object::cast_to<GridMap>(p_node);
+
+ if (gridmap) {
+ if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
+ Array meshes = gridmap->get_meshes();
+ Transform3D xform = gridmap->get_global_transform();
+ for (int i = 0; i < meshes.size(); i += 2) {
+ Ref<Mesh> mesh = meshes[i + 1];
+ if (mesh.is_valid()) {
+ _add_mesh(mesh, p_navmesh_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
+ }
}
}
- }
-#endif
- if (Object::cast_to<Node3D>(p_node)) {
- Node3D *spatial = Object::cast_to<Node3D>(p_node);
- p_accumulated_transform = p_accumulated_transform * spatial->get_transform();
+ if (p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES && (gridmap->get_collision_layer() & p_collision_mask)) {
+ Array shapes = gridmap->get_collision_shapes();
+ for (int i = 0; i < shapes.size(); i += 2) {
+ RID shape = shapes[i + 1];
+ PhysicsServer3D::ShapeType type = PhysicsServer3D::get_singleton()->shape_get_type(shape);
+ Variant data = PhysicsServer3D::get_singleton()->shape_get_data(shape);
+ Ref<Mesh> mesh;
+
+ switch (type) {
+ case PhysicsServer3D::SHAPE_SPHERE: {
+ real_t radius = data;
+ Ref<SphereMesh> sphere_mesh;
+ sphere_mesh.instantiate();
+ sphere_mesh->set_radius(radius);
+ sphere_mesh->set_height(radius * 2.0);
+ mesh = sphere_mesh;
+ } break;
+ case PhysicsServer3D::SHAPE_BOX: {
+ Vector3 extents = data;
+ Ref<BoxMesh> box_mesh;
+ box_mesh.instantiate();
+ box_mesh->set_size(2.0 * extents);
+ mesh = box_mesh;
+ } break;
+ case PhysicsServer3D::SHAPE_CAPSULE: {
+ Dictionary dict = data;
+ real_t radius = dict["radius"];
+ real_t height = dict["height"];
+ Ref<CapsuleMesh> capsule_mesh;
+ capsule_mesh.instantiate();
+ capsule_mesh->set_radius(radius);
+ capsule_mesh->set_height(height);
+ mesh = capsule_mesh;
+ } break;
+ case PhysicsServer3D::SHAPE_CYLINDER: {
+ Dictionary dict = data;
+ real_t radius = dict["radius"];
+ real_t height = dict["height"];
+ Ref<CylinderMesh> cylinder_mesh;
+ cylinder_mesh.instantiate();
+ cylinder_mesh->set_height(height);
+ cylinder_mesh->set_bottom_radius(radius);
+ cylinder_mesh->set_top_radius(radius);
+ mesh = cylinder_mesh;
+ } break;
+ case PhysicsServer3D::SHAPE_CONVEX_POLYGON: {
+ PackedVector3Array vertices = data;
+ Geometry3D::MeshData md;
+
+ Error err = ConvexHullComputer::convex_hull(vertices, md);
+
+ if (err == OK) {
+ PackedVector3Array faces;
+
+ for (int j = 0; j < md.faces.size(); ++j) {
+ Geometry3D::MeshData::Face face = md.faces[j];
+
+ for (int k = 2; k < face.indices.size(); ++k) {
+ faces.push_back(md.vertices[face.indices[0]]);
+ faces.push_back(md.vertices[face.indices[k - 1]]);
+ faces.push_back(md.vertices[face.indices[k]]);
+ }
+ }
+
+ _add_faces(faces, shapes[i], p_vertices, p_indices);
+ }
+ } break;
+ case PhysicsServer3D::SHAPE_CONCAVE_POLYGON: {
+ Dictionary dict = data;
+ PackedVector3Array faces = Variant(dict["faces"]);
+ _add_faces(faces, shapes[i], p_vertices, p_indices);
+ } break;
+ default: {
+ WARN_PRINT("Unsupported collision shape type.");
+ } break;
+ }
+
+ if (mesh.is_valid()) {
+ _add_mesh(mesh, shapes[i], p_vertices, p_indices);
+ }
+ }
+ }
}
+#endif
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
- _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
+ _parse_geometry(p_navmesh_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
}
}
}
@@ -530,7 +611,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
p_node->get_tree()->get_nodes_in_group(p_nav_mesh->get_source_group_name(), &parse_nodes);
}
- Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_transform().affine_inverse();
+ Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_global_transform().affine_inverse();
for (Node *E : parse_nodes) {
NavigationMesh::ParsedGeometryType geometry_type = p_nav_mesh->get_parsed_geometry_type();
uint32_t collision_mask = p_nav_mesh->get_collision_mask();
diff --git a/modules/navigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h
index 1ffdd39aee..21f7a4941b 100644
--- a/modules/navigation/navigation_mesh_generator.h
+++ b/modules/navigation/navigation_mesh_generator.h
@@ -52,7 +52,7 @@ protected:
static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices);
static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
- static void _parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
+ static void _parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);
static void _build_recast_navigation_mesh(
diff --git a/modules/ogg/ogg_packet_sequence.h b/modules/ogg/ogg_packet_sequence.h
index 7e56b14a24..73e3cb4fff 100644
--- a/modules/ogg/ogg_packet_sequence.h
+++ b/modules/ogg/ogg_packet_sequence.h
@@ -104,7 +104,7 @@ class OGGPacketSequencePlayback : public RefCounted {
mutable ogg_packet *packet;
- uint64_t data_version;
+ uint64_t data_version = 0;
mutable int64_t packetno = 0;
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index d6a96282f3..6a06619840 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -62,7 +62,6 @@ if env["builtin_harfbuzz"]:
#'src/hb-gobject-structs.cc',
"src/hb-icu.cc",
"src/hb-map.cc",
- "src/hb-ms-feature-ranges.cc",
"src/hb-number.cc",
"src/hb-ot-cff1-table.cc",
"src/hb-ot-cff2-table.cc",
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 6002dc80da..c7511f587e 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -303,26 +303,10 @@ _FORCE_INLINE_ bool is_connected_to_prev(char32_t p_chr, char32_t p_pchr) {
return (prop != U_JT_RIGHT_JOINING) && (prop != U_JT_NON_JOINING) ? !is_ligature(p_pchr, p_chr) : false;
}
-_FORCE_INLINE_ bool is_control(char32_t p_char) {
- return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
-}
-
-_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
- return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
-}
-
-_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
- return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
-}
-
-_FORCE_INLINE_ bool is_underscore(char32_t p_char) {
- return (p_char == 0x005F);
-}
-
/*************************************************************************/
String TextServerAdvanced::interface_name = "ICU / HarfBuzz / Graphite";
-uint32_t TextServerAdvanced::interface_features = FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_USE_SUPPORT_DATA | FEATURE_FONT_VARIABLE;
+uint32_t TextServerAdvanced::interface_features = FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_USE_SUPPORT_DATA | FEATURE_FONT_VARIABLE | FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION;
bool TextServerAdvanced::has_feature(Feature p_feature) const {
return (interface_features & p_feature) == p_feature;
@@ -2901,7 +2885,7 @@ void TextServerAdvanced::font_set_global_oversampling(float p_oversampling) {
List<RID> text_bufs;
shaped_owner.get_owned_list(&text_bufs);
for (const RID &E : text_bufs) {
- invalidate(shaped_owner.get_or_null(E));
+ invalidate(shaped_owner.get_or_null(E), false);
}
}
}
@@ -2936,7 +2920,7 @@ int TextServerAdvanced::_convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int
return limit;
}
-void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped) {
+void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped, bool p_text) {
p_shaped->valid = false;
p_shaped->sort_valid = false;
p_shaped->line_breaks_valid = false;
@@ -2951,27 +2935,32 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *
p_shaped->glyphs_logical.clear();
p_shaped->overrun_trim_data = TrimData();
p_shaped->utf16 = Char16String();
- if (p_shaped->script_iter != nullptr) {
- memdelete(p_shaped->script_iter);
- p_shaped->script_iter = nullptr;
- }
for (int i = 0; i < p_shaped->bidi_iter.size(); i++) {
ubidi_close(p_shaped->bidi_iter[i]);
}
p_shaped->bidi_iter.clear();
+
+ if (p_text) {
+ if (p_shaped->script_iter != nullptr) {
+ memdelete(p_shaped->script_iter);
+ p_shaped->script_iter = nullptr;
+ }
+ p_shaped->break_ops_valid = false;
+ p_shaped->js_ops_valid = false;
+ }
}
void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
ShapedTextDataAdvanced *parent = shaped_owner.get_or_null(p_shaped->parent);
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) {
+ for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : parent->objects) {
if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) {
p_shaped->objects[E.key] = E.value;
}
}
- for (int k = 0; k < parent->spans.size(); k++) {
- ShapedTextDataAdvanced::Span span = parent->spans[k];
+ for (int i = 0; i < parent->spans.size(); i++) {
+ ShapedTextDataAdvanced::Span span = parent->spans[i];
if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
continue;
}
@@ -3004,7 +2993,7 @@ void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
sd->spans.clear();
sd->objects.clear();
sd->bidi_override.clear();
- invalidate(sd);
+ invalidate(sd, true);
}
void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
@@ -3017,7 +3006,7 @@ void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Dir
full_copy(sd);
}
sd->direction = p_direction;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3047,7 +3036,7 @@ void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const
full_copy(sd);
}
sd->custom_punct = p_punct;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3070,7 +3059,7 @@ void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Array
for (int i = 0; i < p_override.size(); i++) {
sd->bidi_override.push_back(p_override[i]);
}
- invalidate(sd);
+ invalidate(sd, false);
}
void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
@@ -3083,7 +3072,7 @@ void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::O
full_copy(sd);
}
sd->orientation = p_orientation;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3095,7 +3084,7 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
ERR_FAIL_COND(sd->parent != RID());
if (sd->preserve_invalid != p_enabled) {
sd->preserve_invalid = p_enabled;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3117,7 +3106,7 @@ void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_e
full_copy(sd);
}
sd->preserve_control = p_enabled;
- invalidate(sd);
+ invalidate(sd, false);
}
}
@@ -3137,7 +3126,41 @@ TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_sh
return sd->orientation;
}
-bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
+int TextServerAdvanced::shaped_get_span_count(RID p_shaped) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
+ return sd->spans.size();
+}
+
+Variant TextServerAdvanced::shaped_get_span_meta(RID p_shaped, int p_index) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Variant());
+ ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
+ return sd->spans[p_index].meta;
+}
+
+void TextServerAdvanced::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+ ERR_FAIL_INDEX(p_index, sd->spans.size());
+
+ ShapedTextDataAdvanced::Span &span = sd->spans.write[p_index];
+ bool changed = (span.font_size != p_size) || (span.features != p_opentype_features) || (p_fonts.size() != span.fonts.size());
+ if (!changed) {
+ for (int i = 0; i < p_fonts.size(); i++) {
+ changed = changed || (span.fonts[i] != p_fonts[i]);
+ }
+ }
+ if (changed) {
+ span.fonts = p_fonts;
+ span.font_size = p_size;
+ span.features = p_opentype_features;
+
+ invalidate(sd, false);
+ }
+}
+
+bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
ERR_FAIL_COND_V(p_size <= 0, false);
@@ -3162,11 +3185,12 @@ bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_te
span.font_size = p_size;
span.language = p_language;
span.features = p_opentype_features;
+ span.meta = p_meta;
sd->spans.push_back(span);
sd->text += p_text;
sd->end += p_text.length();
- invalidate(sd);
+ invalidate(sd, true);
return true;
}
@@ -3196,13 +3220,13 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con
sd->text += String::chr(0xfffc).repeat(p_length);
sd->end += p_length;
sd->objects[p_key] = obj;
- invalidate(sd);
+ invalidate(sd, true);
return true;
}
bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3222,7 +3246,7 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
Glyph gl = sd->glyphs[i];
Variant key;
if (gl.count == 1) {
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) {
if (E.value.pos == gl.start) {
key = E.key;
break;
@@ -3262,75 +3286,78 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
sd->width += gl.advance * gl.repeat;
}
}
+ _realign(sd);
+ }
+ return true;
+}
- // Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
- if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) {
- if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.y = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.y = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.y = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.y -= E.value.rect.size.y;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.y -= E.value.rect.size.y / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.y);
- full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
- } else {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.x = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.x = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.x = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.x -= E.value.rect.size.x;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.x -= E.value.rect.size.x / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.x);
- full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
+void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
+ // Align embedded objects to baseline.
+ float full_ascent = p_sd->ascent;
+ float full_descent = p_sd->descent;
+ for (KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) {
+ if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) {
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -p_sd->ascent;
+ } break;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = p_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
+ } break;
+ }
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
+ } else {
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -p_sd->ascent;
+ } break;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = p_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
+ } break;
}
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
- sd->ascent = full_ascent;
- sd->descent = full_descent;
}
- return true;
+ p_sd->ascent = full_ascent;
+ p_sd->descent = full_descent;
}
RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
@@ -3423,7 +3450,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) {
if (E.value.pos == gl.start) {
find_embedded = true;
key = E.key;
@@ -3466,72 +3493,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
}
}
- // Align embedded objects to baseline.
- float full_ascent = p_new_sd->ascent;
- float full_descent = p_new_sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_new_sd->objects) {
- if ((E.value.pos >= p_new_sd->start) && (E.value.pos < p_new_sd->end)) {
- if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.y = -p_new_sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.y = (-p_new_sd->ascent + p_new_sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.y = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.y = p_new_sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.y -= E.value.rect.size.y;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.y -= E.value.rect.size.y / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.y);
- full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
- } else {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.x = -p_new_sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.x = (-p_new_sd->ascent + p_new_sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.x = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.x = p_new_sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.x -= E.value.rect.size.x;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.x -= E.value.rect.size.x / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.x);
- full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
- }
- }
- }
- p_new_sd->ascent = full_ascent;
- p_new_sd->descent = full_descent;
+ _realign(p_new_sd);
}
p_new_sd->valid = true;
@@ -3968,40 +3930,43 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
const UChar *data = sd->utf16.ptr();
- HashMap<int, bool> breaks;
- UErrorCode err = U_ZERO_ERROR;
- int i = 0;
- while (i < sd->spans.size()) {
- String language = sd->spans[i].language;
- int r_start = sd->spans[i].start;
- while (i + 1 < sd->spans.size() && language == sd->spans[i + 1].language) {
- i++;
- }
- int r_end = sd->spans[i].end;
- UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
- if (U_FAILURE(err)) {
- // No data loaded - use fallback.
- for (int j = r_start; j < r_end; j++) {
- char32_t c = sd->text[j - sd->start];
- if (is_whitespace(c)) {
- breaks[j + 1] = false;
- }
- if (is_linebreak(c)) {
- breaks[j + 1] = true;
- }
+ if (!sd->break_ops_valid) {
+ sd->breaks.clear();
+ UErrorCode err = U_ZERO_ERROR;
+ int i = 0;
+ while (i < sd->spans.size()) {
+ String language = sd->spans[i].language;
+ int r_start = sd->spans[i].start;
+ while (i + 1 < sd->spans.size() && language == sd->spans[i + 1].language) {
+ i++;
}
- } else {
- while (ubrk_next(bi) != UBRK_DONE) {
- int pos = _convert_pos(sd, ubrk_current(bi)) + r_start;
- if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
- breaks[pos] = true;
- } else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
- breaks[pos] = false;
+ int r_end = sd->spans[i].end;
+ UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
+ if (U_FAILURE(err)) {
+ // No data loaded - use fallback.
+ for (int j = r_start; j < r_end; j++) {
+ char32_t c = sd->text[j - sd->start];
+ if (is_whitespace(c)) {
+ sd->breaks[j + 1] = false;
+ }
+ if (is_linebreak(c)) {
+ sd->breaks[j + 1] = true;
+ }
+ }
+ } else {
+ while (ubrk_next(bi) != UBRK_DONE) {
+ int pos = _convert_pos(sd, ubrk_current(bi)) + r_start;
+ if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
+ sd->breaks[pos] = true;
+ } else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
+ sd->breaks[pos] = false;
+ }
}
}
+ ubrk_close(bi);
+ i++;
}
- ubrk_close(bi);
- i++;
+ sd->break_ops_valid = true;
}
sd->sort_valid = false;
@@ -4013,7 +3978,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
int c_punct_size = sd->custom_punct.length();
const char32_t *c_punct = sd->custom_punct.ptr();
- for (i = 0; i < sd_size; i++) {
+ for (int i = 0; i < sd_size; i++) {
if (sd_glyphs[i].count > 0) {
char32_t c = ch[sd_glyphs[i].start - sd->start];
if (c == 0xfffc) {
@@ -4040,8 +4005,8 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
if (is_underscore(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
}
- if (breaks.has(sd_glyphs[i].end)) {
- if (breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
+ if (sd->breaks.has(sd_glyphs[i].end)) {
+ if (sd->breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
} else if (is_whitespace(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
@@ -4186,41 +4151,45 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
const UChar *data = sd->utf16.ptr();
int32_t data_size = sd->utf16.length();
- Map<int, bool> jstops;
+ if (!sd->js_ops_valid) {
+ sd->jstops.clear();
- // Use ICU word iterator and custom kashida detection.
- UErrorCode err = U_ZERO_ERROR;
- UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
- if (U_FAILURE(err)) {
- // No data - use fallback.
- int limit = 0;
- for (int i = 0; i < sd->text.length(); i++) {
- if (is_whitespace(data[i])) {
- int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
- if (ks != -1) {
- jstops[ks] = true;
- }
- limit = i + 1;
+ // Use ICU word iterator and custom kashida detection.
+ UErrorCode err = U_ZERO_ERROR;
+ UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
+ if (U_FAILURE(err)) {
+ // No data - use fallback.
+ int limit = 0;
+ for (int i = 0; i < sd->text.length(); i++) {
+ if (is_whitespace(data[i])) {
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
+ if (ks != -1) {
+ sd->jstops[ks] = true;
+ }
+ limit = i + 1;
+ }
}
- }
- int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
- if (ks != -1) {
- jstops[ks] = true;
- }
- } else {
- int limit = 0;
- while (ubrk_next(bi) != UBRK_DONE) {
- if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
- int i = _convert_pos(sd, ubrk_current(bi));
- jstops[i + sd->start] = false;
- int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
- if (ks != -1) {
- jstops[ks + sd->start] = true;
- }
- limit = i;
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
+ if (ks != -1) {
+ sd->jstops[ks] = true;
}
+ } else {
+ int limit = 0;
+ while (ubrk_next(bi) != UBRK_DONE) {
+ if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
+ int i = _convert_pos(sd, ubrk_current(bi));
+ sd->jstops[i + sd->start] = false;
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
+ if (ks != -1) {
+ sd->jstops[ks + sd->start] = true;
+ }
+ limit = i;
+ }
+ }
+ ubrk_close(bi);
}
- ubrk_close(bi);
+
+ sd->js_ops_valid = true;
}
sd->sort_valid = false;
@@ -4228,18 +4197,18 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
Glyph *sd_glyphs = sd->glyphs.ptrw();
int sd_size = sd->glyphs.size();
- if (jstops.size() > 0) {
+ if (sd->jstops.size() > 0) {
for (int i = 0; i < sd_size; i++) {
if (sd_glyphs[i].count > 0) {
char32_t c = sd->text[sd_glyphs[i].start - sd->start];
if (c == 0x0640) {
sd_glyphs[i].flags |= GRAPHEME_IS_ELONGATION;
}
- if (jstops.has(sd_glyphs[i].start)) {
+ if (sd->jstops.has(sd_glyphs[i].start)) {
if (c == 0xfffc) {
continue;
}
- if (jstops[sd_glyphs[i].start]) {
+ if (sd->jstops[sd_glyphs[i].start]) {
if (c != 0x0640) {
if (sd_glyphs[i].font_rid != RID()) {
Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
@@ -4574,7 +4543,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
return true;
}
- invalidate(sd);
+ invalidate(sd, false);
if (sd->parent != RID()) {
shaped_text_shape(sd->parent);
ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
@@ -4733,70 +4702,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
}
- // Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
- if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.y = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.y = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.y = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.y -= E.value.rect.size.y;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.y -= E.value.rect.size.y / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.y);
- full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
- } else {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.x = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.x = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.x = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.x -= E.value.rect.size.x;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.x -= E.value.rect.size.x / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.x);
- full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
- }
- }
- sd->ascent = full_ascent;
- sd->descent = full_descent;
+ _realign(sd);
sd->valid = true;
return sd->valid;
}
@@ -4863,7 +4769,7 @@ Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const {
ERR_FAIL_COND_V(!sd, ret);
MutexLock lock(sd->mutex);
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) {
ret.push_back(E.key);
}
@@ -5229,6 +5135,40 @@ String TextServerAdvanced::strip_diacritics(const String &p_string) const {
return result;
}
+String TextServerAdvanced::string_to_upper(const String &p_string, const String &p_language) const {
+ // Convert to UTF-16.
+ Char16String utf16 = p_string.utf16();
+
+ Char16String upper;
+ UErrorCode err = U_ZERO_ERROR;
+ int32_t len = u_strToUpper(nullptr, 0, utf16.ptr(), -1, p_language.ascii().get_data(), &err);
+ ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, p_string, u_errorName(err));
+ upper.resize(len);
+ err = U_ZERO_ERROR;
+ u_strToUpper(upper.ptrw(), len, utf16.ptr(), -1, p_language.ascii().get_data(), &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), p_string, u_errorName(err));
+
+ // Convert back to UTF-32.
+ return String::utf16(upper.ptr(), len);
+}
+
+String TextServerAdvanced::string_to_lower(const String &p_string, const String &p_language) const {
+ // Convert to UTF-16.
+ Char16String utf16 = p_string.utf16();
+
+ Char16String lower;
+ UErrorCode err = U_ZERO_ERROR;
+ int32_t len = u_strToLower(nullptr, 0, utf16.ptr(), -1, p_language.ascii().get_data(), &err);
+ ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, p_string, u_errorName(err));
+ lower.resize(len);
+ err = U_ZERO_ERROR;
+ u_strToLower(lower.ptrw(), len, utf16.ptr(), -1, p_language.ascii().get_data(), &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), p_string, u_errorName(err));
+
+ // Convert back to UTF-32.
+ return String::utf16(lower.ptr(), len);
+}
+
TextServerAdvanced::TextServerAdvanced() {
_insert_num_systems_lang();
_insert_feature_sets();
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index d088219d91..145d740b68 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -242,6 +242,21 @@ class TextServerAdvanced : public TextServer {
// Shaped text cache data.
struct ShapedTextDataAdvanced : public ShapedTextData {
+ struct Span {
+ int start = -1;
+ int end = -1;
+
+ Vector<RID> fonts;
+ int font_size = 0;
+
+ Variant embedded_key;
+
+ String language;
+ Dictionary features;
+ Variant meta;
+ };
+ Vector<Span> spans;
+
/* Intermediate data */
Char16String utf16;
Vector<UBiDi *> bidi_iter;
@@ -249,6 +264,11 @@ class TextServerAdvanced : public TextServer {
ScriptIterator *script_iter = nullptr;
hb_buffer_t *hb_buffer = nullptr;
+ HashMap<int, bool> jstops;
+ HashMap<int, bool> breaks;
+ bool break_ops_valid = false;
+ bool js_ops_valid = false;
+
~ShapedTextDataAdvanced() {
for (int i = 0; i < bidi_iter.size(); i++) {
ubidi_close(bidi_iter[i]);
@@ -268,6 +288,7 @@ class TextServerAdvanced : public TextServer {
mutable RID_PtrOwner<FontDataAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
+ void _realign(ShapedTextDataAdvanced *p_sd) const;
int _convert_pos(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
int _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int p_start, int p_length) const;
@@ -302,7 +323,7 @@ protected:
static void _bind_methods(){};
void full_copy(ShapedTextDataAdvanced *p_shaped);
- void invalidate(ShapedTextDataAdvanced *p_shaped);
+ void invalidate(ShapedTextDataAdvanced *p_shaped, bool p_text = false);
public:
virtual bool has_feature(Feature p_feature) const override;
@@ -482,10 +503,14 @@ public:
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
- virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
+ virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
+ virtual int shaped_get_span_count(RID p_shaped) const override;
+ virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override;
+ virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override;
+
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
@@ -527,6 +552,9 @@ public:
virtual String strip_diacritics(const String &p_string) const override;
+ virtual String string_to_upper(const String &p_string, const String &p_language = "") const override;
+ virtual String string_to_lower(const String &p_string, const String &p_language = "") const override;
+
TextServerAdvanced();
~TextServerAdvanced();
};
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index c7a7c4aa70..182d2a02ad 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -32,6 +32,7 @@
#include "core/error/error_macros.h"
#include "core/string/print_string.h"
+#include "core/string/ucaps.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
@@ -43,30 +44,6 @@
#endif
/*************************************************************************/
-/* Character properties. */
-/*************************************************************************/
-
-_FORCE_INLINE_ bool is_control(char32_t p_char) {
- return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
-}
-
-_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
- return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
-}
-
-_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
- return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
-}
-
-_FORCE_INLINE_ bool is_punct(char32_t p_char) {
- return (p_char >= 0x0020 && p_char <= 0x002F) || (p_char >= 0x003A && p_char <= 0x0040) || (p_char >= 0x005B && p_char <= 0x005E) || (p_char == 0x0060) || (p_char >= 0x007B && p_char <= 0x007E) || (p_char >= 0x2000 && p_char <= 0x206F) || (p_char >= 0x3000 && p_char <= 0x303F);
-}
-
-_FORCE_INLINE_ bool is_underscore(char32_t p_char) {
- return (p_char == 0x005F);
-}
-
-/*************************************************************************/
String TextServerFallback::interface_name = "Fallback";
uint32_t TextServerFallback::interface_features = 0; // Nothing is supported.
@@ -90,7 +67,7 @@ void TextServerFallback::free(RID p_rid) {
font_owner.free(p_rid);
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_rid);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_rid);
shaped_owner.free(p_rid);
memdelete(sd);
}
@@ -126,8 +103,9 @@ _FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) {
char tag[4];
uint32_t i;
- if (!p_str || !p_len || !*p_str)
+ if (!p_str || !p_len || !*p_str) {
return OT_TAG(0, 0, 0, 0);
+ }
if (p_len < 0 || p_len > 4) {
p_len = 4;
@@ -2059,7 +2037,7 @@ void TextServerFallback::font_set_global_oversampling(float p_oversampling) {
/* Shaped text buffer interface */
/*************************************************************************/
-void TextServerFallback::invalidate(ShapedTextData *p_shaped) {
+void TextServerFallback::invalidate(ShapedTextDataFallback *p_shaped) {
p_shaped->valid = false;
p_shaped->sort_valid = false;
p_shaped->line_breaks_valid = false;
@@ -2073,17 +2051,17 @@ void TextServerFallback::invalidate(ShapedTextData *p_shaped) {
p_shaped->glyphs_logical.clear();
}
-void TextServerFallback::full_copy(ShapedTextData *p_shaped) {
- ShapedTextData *parent = shaped_owner.get_or_null(p_shaped->parent);
+void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
+ ShapedTextDataFallback *parent = shaped_owner.get_or_null(p_shaped->parent);
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) {
+ for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : parent->objects) {
if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) {
p_shaped->objects[E.key] = E.value;
}
}
for (int k = 0; k < parent->spans.size(); k++) {
- ShapedTextData::Span span = parent->spans[k];
+ ShapedTextDataFallback::Span span = parent->spans[k];
if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
continue;
}
@@ -2097,7 +2075,7 @@ void TextServerFallback::full_copy(ShapedTextData *p_shaped) {
RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
- ShapedTextData *sd = memnew(ShapedTextData);
+ ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
sd->direction = p_direction;
sd->orientation = p_orientation;
@@ -2105,7 +2083,7 @@ RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, Te
}
void TextServerFallback::shaped_text_clear(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2134,7 +2112,7 @@ TextServer::Direction TextServerFallback::shaped_text_get_inferred_direction(RID
void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
_THREAD_SAFE_METHOD_
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
if (sd->custom_punct != p_punct) {
@@ -2148,13 +2126,13 @@ void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const
String TextServerFallback::shaped_text_get_custom_punctuation(RID p_shaped) const {
_THREAD_SAFE_METHOD_
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, String());
return sd->custom_punct;
}
void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2172,7 +2150,7 @@ void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Array
}
TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
MutexLock lock(sd->mutex);
@@ -2180,7 +2158,7 @@ TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_sh
}
void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
MutexLock lock(sd->mutex);
ERR_FAIL_COND(!sd);
@@ -2194,7 +2172,7 @@ void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
}
bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2202,7 +2180,7 @@ bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
}
void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2216,15 +2194,52 @@ void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_e
}
bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
return sd->preserve_control;
}
-bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+int TextServerFallback::shaped_get_span_count(RID p_shaped) const {
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
+ return sd->spans.size();
+}
+
+Variant TextServerFallback::shaped_get_span_meta(RID p_shaped, int p_index) const {
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Variant());
+ ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
+ return sd->spans[p_index].meta;
+}
+
+void TextServerFallback::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) {
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+ ERR_FAIL_INDEX(p_index, sd->spans.size());
+
+ ShapedTextDataFallback::Span &span = sd->spans.write[p_index];
+ span.fonts.clear();
+ // Pre-sort fonts, push fonts with the language support first.
+ Vector<RID> fonts_no_match;
+ int font_count = p_fonts.size();
+ for (int i = 0; i < font_count; i++) {
+ if (font_is_language_supported(p_fonts[i], span.language)) {
+ span.fonts.push_back(p_fonts[i]);
+ } else {
+ fonts_no_match.push_back(p_fonts[i]);
+ }
+ }
+ span.fonts.append_array(fonts_no_match);
+ span.font_size = p_size;
+ span.features = p_opentype_features;
+
+ sd->valid = false;
+}
+
+bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2242,7 +2257,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
full_copy(sd);
}
- ShapedTextData::Span span;
+ ShapedTextDataFallback::Span span;
span.start = sd->text.length();
span.end = span.start + p_text.length();
@@ -2261,6 +2276,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
ERR_FAIL_COND_V(span.fonts.is_empty(), false);
span.font_size = p_size;
span.language = p_language;
+ span.meta = p_meta;
sd->spans.push_back(span);
sd->text += p_text;
@@ -2271,7 +2287,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
}
bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2282,12 +2298,12 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
full_copy(sd);
}
- ShapedTextData::Span span;
+ ShapedTextDataFallback::Span span;
span.start = sd->start + sd->text.length();
span.end = span.start + p_length;
span.embedded_key = p_key;
- ShapedTextData::EmbeddedObject obj;
+ ShapedTextDataFallback::EmbeddedObject obj;
obj.inline_align = p_inline_align;
obj.rect.size = p_size;
obj.pos = span.start;
@@ -2302,7 +2318,7 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
}
bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2322,7 +2338,7 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
Glyph gl = sd->glyphs[i];
Variant key;
if (gl.count == 1) {
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
if (E.value.pos == gl.start) {
key = E.key;
break;
@@ -2362,79 +2378,82 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
sd->width += gl.advance * gl.repeat;
}
}
+ _realign(sd);
+ }
+ return true;
+}
- // Align embedded objects to baseline.
- float full_ascent = sd->ascent;
- float full_descent = sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
- if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) {
- if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.y = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.y = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.y = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.y -= E.value.rect.size.y;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.y -= E.value.rect.size.y / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.y);
- full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
- } else {
- switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
- case INLINE_ALIGNMENT_TO_TOP: {
- E.value.rect.position.x = -sd->ascent;
- } break;
- case INLINE_ALIGNMENT_TO_CENTER: {
- E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
- } break;
- case INLINE_ALIGNMENT_TO_BASELINE: {
- E.value.rect.position.x = 0;
- } break;
- case INLINE_ALIGNMENT_TO_BOTTOM: {
- E.value.rect.position.x = sd->descent;
- } break;
- }
- switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
- case INLINE_ALIGNMENT_BOTTOM_TO: {
- E.value.rect.position.x -= E.value.rect.size.x;
- } break;
- case INLINE_ALIGNMENT_CENTER_TO: {
- E.value.rect.position.x -= E.value.rect.size.x / 2;
- } break;
- case INLINE_ALIGNMENT_TOP_TO: {
- // NOP
- } break;
- }
- full_ascent = MAX(full_ascent, -E.value.rect.position.x);
- full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
+void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {
+ // Align embedded objects to baseline.
+ float full_ascent = p_sd->ascent;
+ float full_descent = p_sd->descent;
+ for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : p_sd->objects) {
+ if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) {
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -p_sd->ascent;
+ } break;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = p_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
+ } break;
+ }
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
+ } else {
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -p_sd->ascent;
+ } break;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = p_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
+ } break;
}
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
- sd->ascent = full_ascent;
- sd->descent = full_descent;
}
- return true;
+ p_sd->ascent = full_ascent;
+ p_sd->descent = full_descent;
}
RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
MutexLock lock(sd->mutex);
@@ -2448,7 +2467,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());
ERR_FAIL_COND_V(sd->end < p_start + p_length, RID());
- ShapedTextData *new_sd = memnew(ShapedTextData);
+ ShapedTextDataFallback *new_sd = memnew(ShapedTextDataFallback);
new_sd->parent = p_shaped;
new_sd->start = p_start;
new_sd->end = p_start + p_length;
@@ -2474,7 +2493,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
if (E.value.pos == gl.start) {
find_embedded = true;
key = E.key;
@@ -2518,7 +2537,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
// Align embedded objects to baseline.
float full_ascent = new_sd->ascent;
float full_descent = new_sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : new_sd->objects) {
+ for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : new_sd->objects) {
if ((E.value.pos >= new_sd->start) && (E.value.pos < new_sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
@@ -2588,7 +2607,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
}
RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
MutexLock lock(sd->mutex);
@@ -2596,7 +2615,7 @@ RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
}
float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, uint16_t /*JustificationFlag*/ p_jst_flags) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -2705,7 +2724,7 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
}
float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -2761,7 +2780,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat3
}
bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2817,7 +2836,7 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
}
bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2833,7 +2852,7 @@ bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
}
void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint16_t p_trim_flags) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped_line);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped_line);
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
@@ -2861,9 +2880,9 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
return;
}
- Vector<ShapedTextData::Span> &spans = sd->spans;
+ Vector<ShapedTextDataFallback::Span> &spans = sd->spans;
if (sd->parent != RID()) {
- ShapedTextData *parent_sd = shaped_owner.get_or_null(sd->parent);
+ ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);
ERR_FAIL_COND(!parent_sd->valid);
spans = parent_sd->spans;
}
@@ -2985,39 +3004,39 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
}
int TextServerFallback::shaped_text_get_trim_pos(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid.");
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
return sd->overrun_trim_data.trim_pos;
}
int TextServerFallback::shaped_text_get_ellipsis_pos(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid.");
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
return sd->overrun_trim_data.ellipsis_pos;
}
const Glyph *TextServerFallback::shaped_text_get_ellipsis_glyphs(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextData invalid.");
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
return sd->overrun_trim_data.ellipsis_glyph_buf.ptr();
}
int TextServerFallback::shaped_text_get_ellipsis_glyph_count(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextData invalid.");
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
return sd->overrun_trim_data.ellipsis_glyph_buf.size();
}
bool TextServerFallback::shaped_text_shape(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3044,7 +3063,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
// "Shape" string.
for (int i = 0; i < sd->spans.size(); i++) {
- const ShapedTextData::Span &span = sd->spans[i];
+ const ShapedTextDataFallback::Span &span = sd->spans[i];
if (span.embedded_key != Variant()) {
// Embedded object.
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@@ -3144,7 +3163,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
// Align embedded objects to baseline.
float full_ascent = sd->ascent;
float full_descent = sd->descent;
- for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
case INLINE_ALIGNMENT_TO_TOP: {
@@ -3210,7 +3229,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
}
bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3218,7 +3237,7 @@ bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
}
const Glyph *TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
@@ -3229,7 +3248,7 @@ const Glyph *TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
}
int TextServerFallback::shaped_text_get_glyph_count(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0);
MutexLock lock(sd->mutex);
@@ -3240,7 +3259,7 @@ int TextServerFallback::shaped_text_get_glyph_count(RID p_shaped) const {
}
const Glyph *TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
@@ -3252,7 +3271,7 @@ const Glyph *TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
}
Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Vector2i());
MutexLock lock(sd->mutex);
@@ -3261,11 +3280,11 @@ Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
Array ret;
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, ret);
MutexLock lock(sd->mutex);
- for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
ret.push_back(E.key);
}
@@ -3273,7 +3292,7 @@ Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
}
Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Rect2());
MutexLock lock(sd->mutex);
@@ -3285,7 +3304,7 @@ Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_ke
}
Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Size2());
MutexLock lock(sd->mutex);
@@ -3300,7 +3319,7 @@ Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
}
float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3311,7 +3330,7 @@ float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
}
float TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3322,7 +3341,7 @@ float TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
}
float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3333,7 +3352,7 @@ float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
}
float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3345,7 +3364,7 @@ float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const
}
float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3356,6 +3375,34 @@ float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) cons
return sd->uthk;
}
+String TextServerFallback::string_to_upper(const String &p_string, const String &p_language) const {
+ String upper = p_string;
+
+ for (int i = 0; i < upper.size(); i++) {
+ const char32_t s = upper[i];
+ const char32_t t = _find_upper(s);
+ if (s != t) { // avoid copy on write
+ upper[i] = t;
+ }
+ }
+
+ return upper;
+}
+
+String TextServerFallback::string_to_lower(const String &p_string, const String &p_language) const {
+ String lower = p_string;
+
+ for (int i = 0; i < lower.size(); i++) {
+ const char32_t s = lower[i];
+ const char32_t t = _find_lower(s);
+ if (s != t) { // avoid copy on write
+ lower[i] = t;
+ }
+ }
+
+ return lower;
+}
+
TextServerFallback::TextServerFallback() {
_insert_feature_sets();
};
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index e8619e0825..be944cde58 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -203,17 +203,38 @@ class TextServerFallback : public TextServer {
}
}
+ // Shaped text cache data.
+
+ struct ShapedTextDataFallback : public ShapedTextData {
+ struct Span {
+ int start = -1;
+ int end = -1;
+
+ Vector<RID> fonts;
+ int font_size = 0;
+
+ Variant embedded_key;
+
+ String language;
+ Dictionary features;
+ Variant meta;
+ };
+ Vector<Span> spans;
+ };
+
// Common data.
float oversampling = 1.f;
mutable RID_PtrOwner<FontDataFallback> font_owner;
- mutable RID_PtrOwner<ShapedTextData> shaped_owner;
+ mutable RID_PtrOwner<ShapedTextDataFallback> shaped_owner;
+
+ void _realign(ShapedTextDataFallback *p_sd) const;
protected:
static void _bind_methods(){};
- void full_copy(ShapedTextData *p_shaped);
- void invalidate(ShapedTextData *p_shaped);
+ void full_copy(ShapedTextDataFallback *p_shaped);
+ void invalidate(ShapedTextDataFallback *p_shaped);
public:
virtual bool has_feature(Feature p_feature) const override;
@@ -391,10 +412,14 @@ public:
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
- virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
+ virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
+ virtual int shaped_get_span_count(RID p_shaped) const override;
+ virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override;
+ virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override;
+
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
@@ -430,6 +455,9 @@ public:
virtual float shaped_text_get_underline_position(RID p_shaped) const override;
virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
+ virtual String string_to_upper(const String &p_string, const String &p_language = "") const override;
+ virtual String string_to_lower(const String &p_string, const String &p_language = "") const override;
+
TextServerFallback();
~TextServerFallback();
};
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 9d297fcb93..a9652cbba1 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -337,6 +337,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
Ref<Image> img;
img.instantiate();
img->create(w, h, false, Image::FORMAT_RGBA8);
+ texture->create_from_image(img);
} else {
/* tear down the partial theora setup */
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index ec1a8a6b42..1a7d473bd4 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -42,6 +42,7 @@
#include "editor/editor_node.h"
#include "editor/editor_resource_preview.h"
#include "editor/editor_scale.h"
+#include "scene/gui/view_panner.h"
#include "scene/main/window.h"
#ifdef TOOLS_ENABLED
@@ -271,7 +272,7 @@ protected:
if (String(p_name) == "export") {
script->set_variable_export(var, p_value);
- EditorNode::get_singleton()->get_inspector()->update_tree();
+ InspectorDock::get_inspector_singleton()->update_tree();
return true;
}
@@ -1366,7 +1367,7 @@ void VisualScriptEditor::_create_function() {
}
void VisualScriptEditor::_add_node_dialog() {
- _generic_search(script->get_instance_base_type(), graph->get_global_position() + Vector2(55, 80), true);
+ _generic_search(graph->get_global_position() + Vector2(55, 80), true);
}
void VisualScriptEditor::_add_func_input() {
@@ -1442,7 +1443,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
if (p_button == 1) {
// Ensure script base exists otherwise use custom base type.
ERR_FAIL_COND(script.is_null());
- new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), String(), true);
+ new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), true);
return;
} else if (p_button == 0) {
String name = _validate_name("new_function");
@@ -1948,14 +1949,14 @@ void VisualScriptEditor::_on_nodes_duplicate() {
}
}
-void VisualScriptEditor::_generic_search(String p_base_type, Vector2 pos, bool node_centered) {
+void VisualScriptEditor::_generic_search(Vector2 pos, bool node_centered) {
if (node_centered) {
port_action_pos = graph->get_size() / 2.0f;
} else {
port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position();
}
- new_connect_node_select->select_from_visual_script(p_base_type, false, false); // neither connecting nor reset text
+ new_connect_node_select->select_from_visual_script(script, false); // do not reset text
// Ensure that the dialog fits inside the graph.
Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
@@ -1992,7 +1993,7 @@ void VisualScriptEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
}
}
if (is_empty_selection && clipboard->nodes.is_empty()) {
- _generic_search(script->get_instance_base_type(), mouse_up_position);
+ _generic_search();
} else {
popup_menu->set_item_disabled(int(EDIT_CUT_NODES), is_empty_selection);
popup_menu->set_item_disabled(int(EDIT_COPY_NODES), is_empty_selection);
@@ -2446,7 +2447,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
drop_position = pos;
drop_node = node;
drop_path = sn->get_path_to(node);
- new_connect_node_select->select_from_instance(node, "", false, node->get_class());
+ new_connect_node_select->select_from_instance(node, false);
}
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
@@ -2802,7 +2803,7 @@ void VisualScriptEditor::add_callback(const String &p_function, PackedStringArra
String name = p_args[i];
Variant::Type type = Variant::NIL;
- if (name.find(":") != -1) {
+ if (name.contains(":")) {
String tt = name.get_slice(":", 1);
name = name.get_slice(":", 0);
for (int j = 0; j < Variant::VARIANT_MAX; j++) {
@@ -2846,7 +2847,7 @@ Control *VisualScriptEditor::get_base_editor() const {
return graph;
}
-void VisualScriptEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
+void VisualScriptEditor::set_tooltip_request_func(const Callable &p_toolip_callback) {
}
Control *VisualScriptEditor::get_edit_menu() {
@@ -3234,19 +3235,34 @@ void VisualScriptEditor::_port_action_menu(int p_option) {
n->set_base_type("Object");
}
String type_string;
+ String base_script = "";
if (script->get_node(port_action_node)->get_output_value_port_count() > 0) {
type_string = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ VisualScriptFunctionCall *vsfc = Object::cast_to<VisualScriptFunctionCall>(*script->get_node(port_action_node));
+ if (vsfc) {
+ base_script = vsfc->get_base_script();
+ } else {
+ VisualScriptPropertyGet *vspg = Object::cast_to<VisualScriptPropertyGet>(*script->get_node(port_action_node));
+ if (vspg) {
+ base_script = vspg->get_base_script();
+ } else {
+ VisualScriptPropertySet *vsps = Object::cast_to<VisualScriptPropertySet>(*script->get_node(port_action_node));
+ if (vsps) {
+ base_script = vsps->get_base_script();
+ }
+ }
+ }
}
if (tg.type == Variant::OBJECT) {
if (tg.script.is_valid()) {
- new_connect_node_select->select_from_script(tg.script, "");
- } else if (!type_string.is_empty()) {
- new_connect_node_select->select_from_base_type(type_string);
+ new_connect_node_select->select_from_script(tg.script);
+ } else if (type_string != String()) {
+ new_connect_node_select->select_from_base_type(type_string, base_script);
} else {
- new_connect_node_select->select_from_base_type(n->get_base_type());
+ new_connect_node_select->select_from_base_type(n->get_base_type(), base_script);
}
} else if (tg.type == Variant::NIL) {
- new_connect_node_select->select_from_base_type("");
+ new_connect_node_select->select_from_base_type("", base_script);
} else {
new_connect_node_select->select_from_basic_type(tg.type);
}
@@ -3309,66 +3325,54 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua
}
void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) {
+#ifdef OSX_ENABLED
+ bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::META);
+#else
+ bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
+#endif
Vector2 pos = _get_pos_in_graph(port_action_pos);
Set<int> vn;
+ bool port_node_exists = true;
if (drop_position != Vector2()) {
pos = drop_position;
}
drop_position = Vector2();
- bool port_node_exists = true;
-
- // if (func == StringName()) {
- // func = default_func;
- // port_node_exists = false;
- // }
-
- if (p_category == "visualscript") {
- Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text);
- Ref<VisualScriptNode> vnode_old;
- if (port_node_exists && p_connecting) {
- vnode_old = script->get_node(port_action_node);
- }
- int new_id = script->get_available_id();
+ Ref<VisualScriptNode> vnode;
+ Ref<VisualScriptNode> vnode_old;
+ if (port_node_exists && p_connecting) {
+ vnode_old = script->get_node(port_action_node);
+ }
- if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr()) && vnode_old.is_valid()) {
- Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type;
- Object::cast_to<VisualScriptOperator>(vnode_new.ptr())->set_typed(type);
- }
+ if (p_category.begins_with("VisualScriptNode")) {
+ Ref<VisualScriptNode> n = VisualScriptLanguage::singleton->create_node_from_name(p_text);
- if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr()) && vnode_old.is_valid()) {
+ if (Object::cast_to<VisualScriptTypeCast>(n.ptr()) && vnode_old.is_valid()) {
Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type;
String hint_name = vnode_old->get_output_value_port_info(port_action_output).hint_string;
if (type == Variant::OBJECT) {
- Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(hint_name);
+ Object::cast_to<VisualScriptTypeCast>(n.ptr())->set_base_type(hint_name);
} else if (type == Variant::NIL) {
- Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type("");
+ Object::cast_to<VisualScriptTypeCast>(n.ptr())->set_base_type("");
} else {
- Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(Variant::get_type_name(type));
+ Object::cast_to<VisualScriptTypeCast>(n.ptr())->set_base_type(Variant::get_type_name(type));
}
}
-
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode_new, pos);
- if (vnode_old.is_valid() && p_connecting) {
- connect_seq(vnode_old, vnode_new, new_id);
- connect_data(vnode_old, vnode_new, new_id);
- }
-
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
- return;
+ vnode = n;
}
- Ref<VisualScriptNode> vnode;
- Ref<VisualScriptPropertySet> script_prop_set;
-
- if (p_category == String("method")) {
+ if (p_category == String("Class") && !p_connecting) {
+ Ref<VisualScriptFunctionCall> n;
+ n.instantiate();
+ n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SINGLETON);
+ n->set_singleton("ClassDB");
+ n->set_function("instantiate");
+ // Did not find a way to edit the input port value
+ vnode = n;
+ } else if (p_category == String("class_method")) {
Ref<VisualScriptFunctionCall> n;
n.instantiate();
if (!drop_path.is_empty()) {
@@ -3386,96 +3390,151 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
}
vnode = n;
- } else if (p_category == String("set")) {
- Ref<VisualScriptPropertySet> n;
- n.instantiate();
- if (!drop_path.is_empty()) {
- if (drop_path == ".") {
- n->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
- } else {
- n->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
- n->set_base_path(drop_path);
+ } else if (p_category == String("class_property")) {
+ Vector<String> property_path = p_text.split(":");
+ if (held_ctrl) {
+ Ref<VisualScriptPropertySet> n;
+ n.instantiate();
+ n->set_property(property_path[1]);
+ if (!drop_path.is_empty()) {
+ if (drop_path == ".") {
+ n->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
+ } else {
+ n->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
+ n->set_base_path(drop_path);
+ }
}
- }
- if (drop_node) {
- n->set_base_type(drop_node->get_class());
- if (drop_node->get_script_instance()) {
- n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ if (drop_node) {
+ n->set_base_type(drop_node->get_class());
+ if (drop_node->get_script_instance()) {
+ n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ }
}
- }
- vnode = n;
- script_prop_set = n;
- } else if (p_category == String("get")) {
- Ref<VisualScriptPropertyGet> n;
- n.instantiate();
- n->set_property(p_text);
- if (!drop_path.is_empty()) {
- if (drop_path == ".") {
- n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
- } else {
- n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH);
- n->set_base_path(drop_path);
+ vnode = n;
+ } else {
+ Ref<VisualScriptPropertyGet> n;
+ n.instantiate();
+ n->set_property(property_path[1]);
+ if (!drop_path.is_empty()) {
+ if (drop_path == ".") {
+ n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
+ } else {
+ n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH);
+ n->set_base_path(drop_path);
+ }
}
- }
- if (drop_node) {
- n->set_base_type(drop_node->get_class());
- if (drop_node->get_script_instance()) {
- n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ if (drop_node) {
+ n->set_base_type(drop_node->get_class());
+ if (drop_node->get_script_instance()) {
+ n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ }
}
- }
- vnode = n;
- }
- drop_path = String();
- drop_node = nullptr;
-
- if (p_category == String("action")) {
- if (p_text == "VisualScriptCondition") {
- Ref<VisualScriptCondition> n;
- n.instantiate();
vnode = n;
}
- if (p_text == "VisualScriptSwitch") {
- Ref<VisualScriptSwitch> n;
- n.instantiate();
- vnode = n;
- } else if (p_text == "VisualScriptSequence") {
- Ref<VisualScriptSequence> n;
- n.instantiate();
- vnode = n;
- } else if (p_text == "VisualScriptIterator") {
- Ref<VisualScriptIterator> n;
+ } else if (p_category == String("class_constant")) {
+ Vector<String> property_path = p_text.split(":");
+ if (ClassDB::class_exists(property_path[0])) {
+ Ref<VisualScriptClassConstant> n;
n.instantiate();
+ n->set_base_type(property_path[0]);
+ n->set_class_constant(property_path[1]);
vnode = n;
- } else if (p_text == "VisualScriptWhile") {
- Ref<VisualScriptWhile> n;
- n.instantiate();
- vnode = n;
- } else if (p_text == "VisualScriptReturn") {
- Ref<VisualScriptReturn> n;
+ } else {
+ Ref<VisualScriptBasicTypeConstant> n;
n.instantiate();
+ if (property_path[0] == "Nil") {
+ n->set_basic_type(Variant::NIL);
+ } else if (property_path[0] == "bool") {
+ n->set_basic_type(Variant::BOOL);
+ } else if (property_path[0] == "int") {
+ n->set_basic_type(Variant::INT);
+ } else if (property_path[0] == "float") {
+ n->set_basic_type(Variant::FLOAT);
+ } else if (property_path[0] == "String") {
+ n->set_basic_type(Variant::STRING);
+ } else if (property_path[0] == "Vector2") {
+ n->set_basic_type(Variant::VECTOR2);
+ } else if (property_path[0] == "Vector2i") {
+ n->set_basic_type(Variant::VECTOR2I);
+ } else if (property_path[0] == "Rect2") {
+ n->set_basic_type(Variant::RECT2);
+ } else if (property_path[0] == "Rect2i") {
+ n->set_basic_type(Variant::RECT2I);
+ } else if (property_path[0] == "Transform2D") {
+ n->set_basic_type(Variant::TRANSFORM2D);
+ } else if (property_path[0] == "Vector3") {
+ n->set_basic_type(Variant::VECTOR3);
+ } else if (property_path[0] == "Vector3i") {
+ n->set_basic_type(Variant::VECTOR3I);
+ } else if (property_path[0] == "Plane") {
+ n->set_basic_type(Variant::PLANE);
+ } else if (property_path[0] == "ABB") {
+ n->set_basic_type(Variant::AABB);
+ } else if (property_path[0] == "Quaternion") {
+ n->set_basic_type(Variant::QUATERNION);
+ } else if (property_path[0] == "Basis") {
+ n->set_basic_type(Variant::BASIS);
+ } else if (property_path[0] == "Transform3D") {
+ n->set_basic_type(Variant::TRANSFORM3D);
+ } else if (property_path[0] == "Color") {
+ n->set_basic_type(Variant::COLOR);
+ } else if (property_path[0] == "RID") {
+ n->set_basic_type(Variant::RID);
+ } else if (property_path[0] == "Object") {
+ n->set_basic_type(Variant::OBJECT);
+ } else if (property_path[0] == "Callable") {
+ n->set_basic_type(Variant::CALLABLE);
+ } else if (property_path[0] == "Signal") {
+ n->set_basic_type(Variant::SIGNAL);
+ } else if (property_path[0] == "StringName") {
+ n->set_basic_type(Variant::STRING_NAME);
+ } else if (property_path[0] == "NodePath") {
+ n->set_basic_type(Variant::NODE_PATH);
+ } else if (property_path[0] == "Dictionary") {
+ n->set_basic_type(Variant::DICTIONARY);
+ } else if (property_path[0] == "Array") {
+ n->set_basic_type(Variant::ARRAY);
+ } else if (property_path[0] == "PackedByteArray") {
+ n->set_basic_type(Variant::PACKED_BYTE_ARRAY);
+ } else if (property_path[0] == "PackedInt32Array") {
+ n->set_basic_type(Variant::PACKED_INT32_ARRAY);
+ } else if (property_path[0] == "PackedInt64Array") {
+ n->set_basic_type(Variant::PACKED_INT64_ARRAY);
+ } else if (property_path[0] == "PackedFloat32Array") {
+ n->set_basic_type(Variant::PACKED_FLOAT32_ARRAY);
+ } else if (property_path[0] == "PackedStringArray") {
+ n->set_basic_type(Variant::PACKED_STRING_ARRAY);
+ } else if (property_path[0] == "PackedVector2Array") {
+ n->set_basic_type(Variant::PACKED_VECTOR2_ARRAY);
+ } else if (property_path[0] == "PackedVector3Array") {
+ n->set_basic_type(Variant::PACKED_VECTOR3_ARRAY);
+ } else if (property_path[0] == "PackedColorArray") {
+ n->set_basic_type(Variant::PACKED_COLOR_ARRAY);
+ }
+ n->set_basic_type_constant(property_path[1]);
vnode = n;
}
- }
- int new_id = script->get_available_id();
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph", new_id);
- undo_redo->add_undo_method(this, "_update_graph", new_id);
- undo_redo->commit_action();
+ } else if (p_category == String("class_signal")) {
+ Vector<String> property_path = p_text.split(":");
+ ERR_FAIL_COND(!(script->has_custom_signal(property_path[1]) || ClassDB::has_signal(script->get_instance_base_type(), property_path[1])));
- if (script_prop_set.is_valid()) {
- script_prop_set->set_property(p_text);
+ Ref<VisualScriptEmitSignal> n;
+ n.instantiate();
+ n->set_signal(property_path[1]);
+ vnode = n;
+ }
+ if (vnode == nullptr) {
+ print_error("Category not handled: " + p_category.quote());
}
- port_action_new_node = new_id;
-
- Ref<VisualScriptNode> vsn = script->get_node(port_action_new_node);
+ if (Object::cast_to<VisualScriptFunctionCall>(vnode.ptr()) && p_category != "Class") {
+ Vector<String> property_path = p_text.split(":");
+ String class_of_method = property_path[0];
+ String method_name = property_path[1];
- if (Object::cast_to<VisualScriptFunctionCall>(vsn.ptr())) {
- Ref<VisualScriptFunctionCall> vsfc = vsn;
- vsfc->set_function(p_text);
+ Ref<VisualScriptFunctionCall> vsfc = vnode;
+ vsfc->set_function(method_name);
if (port_node_exists && p_connecting) {
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
@@ -3492,7 +3551,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
if (!base_type.is_empty() && hint == PROPERTY_HINT_TYPE_STRING) {
vsfc->set_base_type(base_type);
}
- if (p_text == "call" || p_text == "call_deferred") {
+ if (method_name == "call" || method_name == "call_deferred") {
vsfc->set_function(String(""));
}
}
@@ -3510,8 +3569,8 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
if (port_node_exists && p_connecting) {
- if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) {
- Ref<VisualScriptPropertySet> vsp = vsn;
+ if (Object::cast_to<VisualScriptPropertySet>(vnode.ptr())) {
+ Ref<VisualScriptPropertySet> vsp = vnode;
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
if (tg.type == Variant::OBJECT) {
@@ -3540,8 +3599,8 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
}
- if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) {
- Ref<VisualScriptPropertyGet> vsp = vsn;
+ if (Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())) {
+ Ref<VisualScriptPropertyGet> vsp = vnode;
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
if (tg.type == Variant::OBJECT) {
@@ -3569,13 +3628,85 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
}
}
+ if (vnode == nullptr) {
+ print_error("Not able to create node from category: \"" + p_category + "\" and text \"" + p_text + "\" Not created");
+ return;
+ }
+
+ int new_id = script->get_available_id();
+ undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
+ undo_redo->add_do_method(this, "_update_graph", new_id);
+ undo_redo->add_undo_method(this, "_update_graph", new_id);
+ undo_redo->commit_action();
+
+ port_action_new_node = new_id;
+
+ String base_script = "";
+ String base_type = "";
if (port_node_exists) {
- Ref<VisualScriptNode> vnode_old = script->get_node(port_action_node);
+ if (vnode_old.is_valid()) {
+ if (Object::cast_to<VisualScriptTypeCast>(vnode_old.ptr())) {
+ base_type = Object::cast_to<VisualScriptTypeCast>(vnode_old.ptr())->get_base_type();
+ base_script = Object::cast_to<VisualScriptTypeCast>(vnode_old.ptr())->get_base_script();
+ } else if (Object::cast_to<VisualScriptFunctionCall>(vnode_old.ptr())) {
+ base_type = Object::cast_to<VisualScriptFunctionCall>(vnode_old.ptr())->get_base_type();
+ base_script = Object::cast_to<VisualScriptFunctionCall>(vnode_old.ptr())->get_base_script();
+ } else if (Object::cast_to<VisualScriptPropertySet>(vnode_old.ptr())) {
+ base_type = Object::cast_to<VisualScriptPropertySet>(vnode_old.ptr())->get_base_type();
+ base_script = Object::cast_to<VisualScriptPropertySet>(vnode_old.ptr())->get_base_script();
+ } else if (Object::cast_to<VisualScriptPropertyGet>(vnode_old.ptr())) {
+ base_type = Object::cast_to<VisualScriptPropertyGet>(vnode_old.ptr())->get_base_type();
+ base_script = Object::cast_to<VisualScriptPropertyGet>(vnode_old.ptr())->get_base_script();
+ }
+ }
+
+ Vector<String> property_path = p_text.split(":");
+ if (ClassDB::is_parent_class(script->get_instance_base_type(), property_path[0]) || script->get_path().ends_with(property_path[0].unquote())) {
+ if (!p_connecting) {
+ base_type = script->get_instance_base_type();
+ base_script = script->get_path();
+ }
+ } else {
+ base_type = property_path[0];
+ base_script = "";
+ }
+
+ if (drop_node) {
+ Ref<Script> script = drop_node->get_script();
+ if (script != nullptr) {
+ base_script = script->get_path();
+ }
+ }
+
if (vnode_old.is_valid() && p_connecting) {
+ if (base_type == "") {
+ base_type = property_path[0];
+ } else if (ClassDB::is_parent_class(property_path[0], base_type)) {
+ base_type = property_path[0];
+ }
connect_seq(vnode_old, vnode, port_action_new_node);
connect_data(vnode_old, vnode, port_action_new_node);
}
}
+ if (Object::cast_to<VisualScriptTypeCast>(vnode.ptr())) {
+ Object::cast_to<VisualScriptTypeCast>(vnode.ptr())->set_base_type(base_type);
+ Object::cast_to<VisualScriptTypeCast>(vnode.ptr())->set_base_script(base_script);
+ } else if (Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())) {
+ Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())->set_base_type(base_type);
+ Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())->set_base_script(base_script);
+ } else if (Object::cast_to<VisualScriptPropertySet>(vnode.ptr())) {
+ Object::cast_to<VisualScriptPropertySet>(vnode.ptr())->set_base_type(base_type);
+ Object::cast_to<VisualScriptPropertySet>(vnode.ptr())->set_base_script(base_script);
+ } else if (Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())) {
+ Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())->set_base_type(base_type);
+ Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())->set_base_script(base_script);
+ }
+
+ drop_path = String();
+ drop_node = nullptr;
+
_update_graph(port_action_new_node);
}
@@ -3625,7 +3756,7 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual
}
void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting) {
- String name = p_text;
+ String name = p_text.substr(p_text.find_char(':') + 1);
if (script->has_function(name)) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Script already has function '%s'"), name));
return;
@@ -3782,7 +3913,8 @@ void VisualScriptEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- graph->set_panning_scheme((GraphEdit::PanningScheme)EDITOR_GET("interface/editors/sub_editor_panning_scheme").operator int());
+ graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
+ graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning")));
} break;
case NOTIFICATION_READY: {
@@ -3901,7 +4033,7 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_
void VisualScriptEditor::_menu_option(int p_what) {
switch (p_what) {
case EDIT_ADD_NODE: {
- _generic_search(script->get_instance_base_type(), mouse_up_position);
+ _generic_search();
} break;
case EDIT_DELETE_NODES: {
_on_nodes_delete();
@@ -3931,7 +4063,7 @@ void VisualScriptEditor::_menu_option(int p_what) {
} break;
case EDIT_FIND_NODE_TYPE: {
- _generic_search(script->get_instance_base_type());
+ _generic_search();
} break;
case EDIT_COPY_NODES: {
_on_nodes_copy();
diff --git a/modules/visual_script/editor/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h
index 90e4fb9d56..b01732b2fd 100644
--- a/modules/visual_script/editor/visual_script_editor.h
+++ b/modules/visual_script/editor/visual_script_editor.h
@@ -85,55 +85,55 @@ class VisualScriptEditor : public ScriptEditorBase {
MEMBER_SIGNAL
};
- VBoxContainer *members_section;
- MenuButton *edit_menu;
+ VBoxContainer *members_section = nullptr;
+ MenuButton *edit_menu = nullptr;
Ref<VisualScript> script;
- Button *base_type_select;
+ Button *base_type_select = nullptr;
- LineEdit *func_name_box;
- ScrollContainer *func_input_scroll;
- VBoxContainer *func_input_vbox;
- ConfirmationDialog *function_create_dialog;
+ LineEdit *func_name_box = nullptr;
+ ScrollContainer *func_input_scroll = nullptr;
+ VBoxContainer *func_input_vbox = nullptr;
+ ConfirmationDialog *function_create_dialog = nullptr;
- GraphEdit *graph;
- HBoxContainer *status_bar;
- Button *toggle_scripts_button;
+ GraphEdit *graph = nullptr;
+ HBoxContainer *status_bar = nullptr;
+ Button *toggle_scripts_button = nullptr;
- VisualScriptEditorSignalEdit *signal_editor;
+ VisualScriptEditorSignalEdit *signal_editor = nullptr;
- AcceptDialog *edit_signal_dialog;
- EditorInspector *edit_signal_edit;
+ AcceptDialog *edit_signal_dialog = nullptr;
+ EditorInspector *edit_signal_edit = nullptr;
- VisualScriptPropertySelector *method_select;
- VisualScriptPropertySelector *new_connect_node_select;
- VisualScriptPropertySelector *new_virtual_method_select;
+ VisualScriptPropertySelector *method_select = nullptr;
+ VisualScriptPropertySelector *new_connect_node_select = nullptr;
+ VisualScriptPropertySelector *new_virtual_method_select = nullptr;
- VisualScriptEditorVariableEdit *variable_editor;
+ VisualScriptEditorVariableEdit *variable_editor = nullptr;
- AcceptDialog *edit_variable_dialog;
- EditorInspector *edit_variable_edit;
+ AcceptDialog *edit_variable_dialog = nullptr;
+ EditorInspector *edit_variable_edit = nullptr;
- CustomPropertyEditor *default_value_edit;
+ CustomPropertyEditor *default_value_edit = nullptr;
- UndoRedo *undo_redo;
+ UndoRedo *undo_redo = nullptr;
- Tree *members;
- AcceptDialog *function_name_edit;
- LineEdit *function_name_box;
+ Tree *members = nullptr;
+ AcceptDialog *function_name_edit = nullptr;
+ LineEdit *function_name_box = nullptr;
- Label *hint_text;
- Timer *hint_text_timer;
+ Label *hint_text = nullptr;
+ Timer *hint_text_timer = nullptr;
- Label *select_func_text;
+ Label *select_func_text = nullptr;
bool updating_graph = false;
void _show_hint(const String &p_hint);
void _hide_timer();
- CreateDialog *select_base_type;
+ CreateDialog *select_base_type = nullptr;
struct VirtualInMenu {
String name;
@@ -173,10 +173,10 @@ class VisualScriptEditor : public ScriptEditorBase {
String member_name;
PortAction port_action;
- int port_action_node;
- int port_action_output;
+ int port_action_node = 0;
+ int port_action_output = 0;
Vector2 port_action_pos;
- int port_action_new_node;
+ int port_action_new_node = 0;
bool saved_pos_dirty = false;
@@ -196,7 +196,7 @@ class VisualScriptEditor : public ScriptEditorBase {
int _create_new_node_from_name(const String &p_text, const Vector2 &p_point);
void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting);
- int error_line;
+ int error_line = -1;
void _node_selected(Node *p_node);
void _center_on_node(int p_id);
@@ -241,7 +241,7 @@ class VisualScriptEditor : public ScriptEditorBase {
bool node_has_sequence_connections(int p_id);
- void _generic_search(String p_base_type = "", Vector2 pos = Vector2(), bool node_centered = false);
+ void _generic_search(Vector2 pos = Vector2(), bool node_centered = false);
virtual void input(const Ref<InputEvent> &p_event) override;
void _graph_gui_input(const Ref<InputEvent> &p_event);
@@ -267,12 +267,12 @@ class VisualScriptEditor : public ScriptEditorBase {
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
- int editing_id;
- int editing_input;
+ int editing_id = 0;
+ int editing_input = 0;
- bool can_swap;
- int data_disconnect_node;
- int data_disconnect_port;
+ bool can_swap = false;
+ int data_disconnect_node = 0;
+ int data_disconnect_port = 0;
void _default_value_changed();
void _default_value_edited(Node *p_button, int p_id, int p_input_port);
@@ -328,7 +328,7 @@ public:
virtual void update_settings() override;
virtual bool show_members_overview() override;
virtual void set_debugger_active(bool p_active) override;
- virtual void set_tooltip_request_func(String p_method, Object *p_obj) override;
+ virtual void set_tooltip_request_func(const Callable &p_toolip_callback) override;
virtual Control *get_edit_menu() override;
virtual void clear_edit_menu() override;
virtual void set_find_replace_bar(FindReplaceBar *p_bar) override { p_bar->hide(); }; // Not needed here.
diff --git a/modules/visual_script/editor/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp
index c88d10dabd..4072dcebe5 100644
--- a/modules/visual_script/editor/visual_script_property_selector.cpp
+++ b/modules/visual_script/editor/visual_script_property_selector.cpp
@@ -37,13 +37,28 @@
#include "../visual_script_nodes.h"
#include "core/os/keyboard.h"
#include "editor/doc_tools.h"
+#include "editor/editor_feature_profile.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "scene/main/node.h"
#include "scene/main/window.h"
-void VisualScriptPropertySelector::_text_changed(const String &p_newtext) {
- _update_search();
+void VisualScriptPropertySelector::_update_icons() {
+ search_box->set_right_icon(results_tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ search_box->set_clear_button_enabled(true);
+ search_box->add_theme_icon_override("right_icon", results_tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+
+ search_visual_script_nodes->set_icon(results_tree->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
+ search_classes->set_icon(results_tree->get_theme_icon(SNAME("Object"), SNAME("EditorIcons")));
+ search_methods->set_icon(results_tree->get_theme_icon(SNAME("MemberMethod"), SNAME("EditorIcons")));
+ search_operators->set_icon(results_tree->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ search_signals->set_icon(results_tree->get_theme_icon(SNAME("MemberSignal"), SNAME("EditorIcons")));
+ search_constants->set_icon(results_tree->get_theme_icon(SNAME("MemberConstant"), SNAME("EditorIcons")));
+ search_properties->set_icon(results_tree->get_theme_icon(SNAME("MemberProperty"), SNAME("EditorIcons")));
+ search_theme_items->set_icon(results_tree->get_theme_icon(SNAME("MemberTheme"), SNAME("EditorIcons")));
+
+ case_sensitive_button->set_icon(results_tree->get_theme_icon(SNAME("MatchCase"), SNAME("EditorIcons")));
+ hierarchy_button->set_icon(results_tree->get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons")));
}
void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
@@ -55,24 +70,8 @@ void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
case Key::DOWN:
case Key::PAGEUP:
case Key::PAGEDOWN: {
- search_options->gui_input(k);
+ results_tree->gui_input(k);
search_box->accept_event();
-
- TreeItem *root = search_options->get_root();
- if (!root->get_first_child()) {
- break;
- }
-
- TreeItem *current = search_options->get_selected();
-
- TreeItem *item = search_options->get_next_selected(root);
- while (item) {
- item->deselect(0);
- item = search_options->get_next_selected(item);
- }
-
- current->select(0);
-
} break;
default:
break;
@@ -80,654 +79,1189 @@ void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
}
}
-void VisualScriptPropertySelector::_update_search() {
- set_title(TTR("Search VisualScript"));
-
- search_options->clear();
- help_bit->set_text("");
-
- TreeItem *root = search_options->create_item();
- bool found = false;
- StringName base = base_type;
- List<StringName> base_list;
- while (base) {
- base_list.push_back(base);
- base = ClassDB::get_parent_class_nocheck(base);
- }
-
- for (const StringName &E : base_list) {
- List<MethodInfo> methods;
- List<PropertyInfo> props;
- TreeItem *category = nullptr;
- Ref<Texture2D> type_icons[Variant::VARIANT_MAX] = {
- vbc->get_theme_icon(SNAME("Variant"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("String"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Vector2i"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Rect2"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Rect2i"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Vector3i"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Transform2D"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Plane"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Quaternion"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("AABB"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Basis"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("StringName"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("NodePath"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("RID"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("MiniObject"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Callable"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Signal"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Dictionary"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedByteArray"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedInt32Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedInt64Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedFloat32Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedFloat64Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedStringArray"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedVector2Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedVector3Array"), SNAME("EditorIcons")),
- vbc->get_theme_icon(SNAME("PackedColorArray"), SNAME("EditorIcons"))
- };
- {
- String b = String(E);
- category = search_options->create_item(root);
- if (category) {
- category->set_text(0, b.replace_first("*", ""));
- category->set_selectable(0, false);
- Ref<Texture2D> icon;
- String rep = b.replace("*", "");
- icon = EditorNode::get_singleton()->get_class_icon(rep);
- category->set_icon(0, icon);
- }
- }
- if (properties || seq_connect) {
- if (instance) {
- instance->get_property_list(&props, true);
- } else {
- Object *obj = ObjectDB::get_instance(script);
- if (Object::cast_to<Script>(obj)) {
- Object::cast_to<Script>(obj)->get_script_property_list(&props);
- } else {
- ClassDB::get_property_list(E, &props, true);
- }
- }
- for (const PropertyInfo &F : props) {
- if (!(F.usage & PROPERTY_USAGE_EDITOR) && !(F.usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) {
- continue;
- }
+void VisualScriptPropertySelector::_update_results_i(int p_int) {
+ _update_results();
+}
- if (type_filter.size() && type_filter.find(F.type) == -1) {
- continue;
- }
+void VisualScriptPropertySelector::_update_results_s(String p_string) {
+ _update_results();
+}
- // capitalize() also converts underscore to space, we'll match again both possible styles
- String get_text_raw = String(vformat(TTR("Get %s"), F.name));
- String get_text = get_text_raw.capitalize();
- String set_text_raw = String(vformat(TTR("Set %s"), F.name));
- String set_text = set_text_raw.capitalize();
- String input = search_box->get_text().capitalize();
-
- if (input.is_empty() || get_text_raw.findn(input) != -1 || get_text.findn(input) != -1) {
- TreeItem *item = search_options->create_item(category ? category : root);
- item->set_text(0, get_text);
- item->set_metadata(0, F.name);
- item->set_icon(0, type_icons[F.type]);
- item->set_metadata(1, "get");
- item->set_collapsed(true);
- item->set_selectable(0, true);
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
- }
+void VisualScriptPropertySelector::_update_results() {
+ _update_icons();
+ search_runner = Ref<SearchRunner>(memnew(SearchRunner(this, results_tree)));
+ set_process(true);
+}
+
+void VisualScriptPropertySelector::_confirmed() {
+ TreeItem *ti = results_tree->get_selected();
+ if (!ti) {
+ return;
+ }
+ emit_signal(SNAME("selected"), ti->get_metadata(0), ti->get_metadata(1), connecting);
+ set_visible(false);
+}
+
+void VisualScriptPropertySelector::_item_selected() {
+ if (results_tree->get_selected()->has_meta("description")) {
+ help_bit->set_text(results_tree->get_selected()->get_meta("description"));
+ } else {
+ help_bit->set_text("No description available");
+ }
+}
- if (input.is_empty() || set_text_raw.findn(input) != -1 || set_text.findn(input) != -1) {
- TreeItem *item = search_options->create_item(category ? category : root);
- item->set_text(0, set_text);
- item->set_metadata(0, F.name);
- item->set_icon(0, type_icons[F.type]);
- item->set_metadata(1, "set");
- item->set_selectable(0, true);
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
+void VisualScriptPropertySelector::_hide_requested() {
+ _cancel_pressed(); // From AcceptDialog.
+}
+
+void VisualScriptPropertySelector::_notification(int p_what) {
+ switch (p_what) {
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ _update_icons();
+ } break;
+ case NOTIFICATION_ENTER_TREE: {
+ connect("confirmed", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
+ } break;
+ case NOTIFICATION_PROCESS: {
+ // Update background search.
+ if (search_runner.is_valid()) {
+ if (search_runner->work()) {
+ // Search done.
+ get_ok_button()->set_disabled(!results_tree->get_selected());
+
+ search_runner = Ref<SearchRunner>();
+ set_process(false);
}
- }
- }
- {
- if (type != Variant::NIL) {
- Variant v;
- Callable::CallError ce;
- Variant::construct(type, v, nullptr, 0, ce);
- v.get_method_list(&methods);
} else {
- Object *obj = ObjectDB::get_instance(script);
- if (Object::cast_to<Script>(obj)) {
- Object::cast_to<Script>(obj)->get_script_method_list(&methods);
- }
-
- ClassDB::get_method_list(E, &methods, true, true);
- }
- }
- for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) {
- String name = M->get().name.get_slice(":", 0);
- if (name.begins_with("_") && !(M->get().flags & METHOD_FLAG_VIRTUAL)) {
- continue;
+ // if one is valid
+ set_process(false);
}
+ } break;
+ }
+}
- if (virtuals_only && !(M->get().flags & METHOD_FLAG_VIRTUAL)) {
- continue;
- }
+void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const bool p_virtuals_only, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select method from base type"));
+ base_type = p_base;
+ base_script = "";
+ type = Variant::NIL;
+ connecting = p_connecting;
- if (!virtuals_only && (M->get().flags & METHOD_FLAG_VIRTUAL)) {
- continue;
- }
+ if (clear_text) {
+ if (p_virtuals_only) {
+ search_box->set_text("._"); // show all _methods
+ search_box->set_caret_column(2);
+ } else {
+ search_box->set_text("."); // show all methods
+ search_box->set_caret_column(1);
+ }
+ }
- MethodInfo mi = M->get();
- String desc_arguments;
- if (mi.arguments.size() > 0) {
- desc_arguments = "(";
- for (int i = 0; i < mi.arguments.size(); i++) {
- if (i > 0) {
- desc_arguments += ", ";
- }
- if (mi.arguments[i].type == Variant::NIL) {
- desc_arguments += "var";
- } else if (mi.arguments[i].name.find(":") != -1) {
- desc_arguments += mi.arguments[i].name.get_slice(":", 1);
- mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0);
- } else {
- desc_arguments += Variant::get_type_name(mi.arguments[i].type);
- }
- }
- desc_arguments += ")";
- }
- String desc_raw = mi.name + desc_arguments;
- String desc = desc_raw.capitalize().replace("( ", "(");
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(false);
+ search_constants->set_pressed(false);
+ search_properties->set_pressed(false);
+ search_theme_items->set_pressed(false);
- if (!search_box->get_text().is_empty() &&
- name.findn(search_box->get_text()) == -1 &&
- desc.findn(search_box->get_text()) == -1 &&
- desc_raw.findn(search_box->get_text()) == -1) {
- continue;
- }
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated"
- TreeItem *item = search_options->create_item(category ? category : root);
- item->set_text(0, desc);
- item->set_icon(0, vbc->get_theme_icon(SNAME("MemberMethod"), SNAME("EditorIcons")));
- item->set_metadata(0, name);
- item->set_selectable(0, true);
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
- item->set_metadata(1, "method");
- item->set_collapsed(true);
- item->set_selectable(1, false);
+ _update_results();
+}
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
- }
+void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_base_script, bool p_virtuals_only, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from base type"));
+ base_type = p_base;
+ base_script = p_base_script.lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ type = Variant::NIL;
+ connecting = p_connecting;
- if (category && category->get_first_child() == nullptr) {
- memdelete(category); //old category was unused
+ if (clear_text) {
+ if (p_virtuals_only) {
+ search_box->set_text("_");
+ } else {
+ search_box->set_text(" ");
}
}
- if (properties) {
- if (!seq_connect && !visual_script_generic) {
- get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box);
- get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
- get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box);
- get_visual_node_names("functions/deconstruct/" + Variant::get_type_name(type), Set<String>(), found, root, search_box);
- get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box);
- if (type == Variant::INT) {
- get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box);
- }
- if (type == Variant::BOOL) {
- get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box);
- }
- if (type == Variant::BOOL || type == Variant::INT || type == Variant::FLOAT || type == Variant::VECTOR2 || type == Variant::VECTOR3) {
- get_visual_node_names("operators/math/", Set<String>(), found, root, search_box);
- }
- }
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(true);
+ search_constants->set_pressed(false);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
+
+ // When class is Input only show inheritors
+ scope_combo->select(0); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
+}
+
+void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from script"));
+ ERR_FAIL_COND(p_script.is_null());
+
+ base_type = p_script->get_instance_base_type();
+ base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ type = Variant::NIL;
+ script = p_script->get_instance_id();
+ connecting = p_connecting;
+
+ if (clear_text) {
+ search_box->set_text("");
}
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(true);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(true);
+ search_signals->set_pressed(true);
+ search_constants->set_pressed(true);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
+
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated"
- if (seq_connect && !visual_script_generic) {
- String text = search_box->get_text();
- create_visualscript_item(String("VisualScriptCondition"), root, text, String("Condition"));
- create_visualscript_item(String("VisualScriptSwitch"), root, text, String("Switch"));
- create_visualscript_item(String("VisualScriptSequence"), root, text, String("Sequence"));
- create_visualscript_item(String("VisualScriptIterator"), root, text, String("Iterator"));
- create_visualscript_item(String("VisualScriptWhile"), root, text, String("While"));
- create_visualscript_item(String("VisualScriptReturn"), root, text, String("Return"));
- get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box);
- get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box);
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
+}
+
+void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from basic type"));
+ ERR_FAIL_COND(p_type == Variant::NIL);
+ base_type = Variant::get_type_name(p_type);
+ base_script = "";
+ type = p_type;
+ connecting = p_connecting;
+
+ if (clear_text) {
+ search_box->set_text(" ");
}
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(true);
+ search_signals->set_pressed(false);
+ search_constants->set_pressed(true);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
+
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated" //id5 "Search All"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+
+ _update_results();
+}
+
+void VisualScriptPropertySelector::select_from_action(const String &p_type, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from action"));
+ base_type = p_type;
+ base_script = "";
+ type = Variant::NIL;
+ connecting = p_connecting;
- if ((properties || seq_connect) && visual_script_generic) {
- get_visual_node_names("", Set<String>(), found, root, search_box);
+ if (clear_text) {
+ search_box->set_text("");
}
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(true);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(false);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(false);
+ search_constants->set_pressed(false);
+ search_properties->set_pressed(false);
+ search_theme_items->set_pressed(false);
+
+ scope_combo->select(0); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated" //id5 "Search All"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
+}
+
+void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const bool p_connecting, bool clear_text) {
+ set_title(TTR("Select from instance"));
+ base_type = p_instance->get_class();
- TreeItem *selected_item = search_options->search_item_text(search_box->get_text());
- if (!found && selected_item != nullptr) {
- selected_item->select(0);
- found = true;
+ const Ref<Script> &p_script = p_instance->get_script();
+ if (p_script == nullptr) {
+ base_script = "";
+ } else {
+ base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ }
+
+ type = Variant::NIL;
+ connecting = p_connecting;
+
+ if (clear_text) {
+ search_box->set_text(" ");
}
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(false);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(true);
+ search_constants->set_pressed(true);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
+
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated" //id5 "Search All"
- get_ok_button()->set_disabled(root->get_first_child() == nullptr);
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
}
-void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) {
- if (search_input.is_empty() || text.findn(search_input) != -1) {
- TreeItem *item = search_options->create_item(root);
- item->set_text(0, text);
- item->set_icon(0, vbc->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
- item->set_metadata(0, name);
- item->set_metadata(1, "action");
- item->set_selectable(0, true);
- item->set_collapsed(true);
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
+void VisualScriptPropertySelector::select_from_visual_script(const Ref<Script> &p_script, bool clear_text) {
+ set_title(TTR("Select from visual script"));
+ base_type = p_script->get_instance_base_type();
+ if (p_script == nullptr) {
+ base_script = "";
+ } else {
+ base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ }
+ type = Variant::NIL;
+ connecting = false;
+
+ if (clear_text) {
+ search_box->set_text(" ");
}
+ search_box->select_all();
+
+ search_visual_script_nodes->set_pressed(true);
+ search_classes->set_pressed(false);
+ search_methods->set_pressed(true);
+ search_operators->set_pressed(false);
+ search_signals->set_pressed(true);
+ search_constants->set_pressed(true);
+ search_properties->set_pressed(true);
+ search_theme_items->set_pressed(false);
+
+ scope_combo->select(2); //id0 = "Search Related" //id2 = "Search Base" //id3 = "Search Inheriters" //id4 = "Search Unrelated" //id5 "Search All"
+
+ results_tree->clear();
+ show_window(.5f);
+ search_box->grab_focus();
+ _update_results();
+}
+
+void VisualScriptPropertySelector::show_window(float p_screen_ratio) {
+ popup_centered_ratio(p_screen_ratio);
}
-void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &p_modifiers, bool &found, TreeItem *const root, LineEdit *const search_box) {
- Map<String, TreeItem *> path_cache;
+void VisualScriptPropertySelector::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "category"), PropertyInfo(Variant::BOOL, "connecting")));
+}
- List<String> fnodes;
- VisualScriptLanguage::singleton->get_registered_node_names(&fnodes);
+VisualScriptPropertySelector::VisualScriptPropertySelector() {
+ vbox = memnew(VBoxContainer);
+ add_child(vbox);
+
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ hbox->set_alignment(hbox->ALIGNMENT_CENTER);
+ vbox->add_child(hbox);
+
+ case_sensitive_button = memnew(Button);
+ case_sensitive_button->set_flat(true);
+ case_sensitive_button->set_tooltip(TTR("Case Sensitive"));
+ case_sensitive_button->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ case_sensitive_button->set_toggle_mode(true);
+ case_sensitive_button->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(case_sensitive_button);
+
+ hierarchy_button = memnew(Button);
+ hierarchy_button->set_flat(true);
+ hierarchy_button->set_tooltip(TTR("Show Hierarchy"));
+ hierarchy_button->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ hierarchy_button->set_toggle_mode(true);
+ hierarchy_button->set_pressed(true);
+ hierarchy_button->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(hierarchy_button);
+
+ hbox->add_child(memnew(VSeparator));
+
+ search_visual_script_nodes = memnew(Button);
+ search_visual_script_nodes->set_flat(true);
+ search_visual_script_nodes->set_tooltip(TTR("Search Visual Script Nodes"));
+ search_visual_script_nodes->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_visual_script_nodes->set_toggle_mode(true);
+ search_visual_script_nodes->set_pressed(true);
+ search_visual_script_nodes->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_visual_script_nodes);
+
+ search_classes = memnew(Button);
+ search_classes->set_flat(true);
+ search_classes->set_tooltip(TTR("Search Classes"));
+ search_classes->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_classes->set_toggle_mode(true);
+ search_classes->set_pressed(true);
+ search_classes->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_classes);
+
+ search_operators = memnew(Button);
+ search_operators->set_flat(true);
+ search_operators->set_tooltip(TTR("Search Operators"));
+ search_operators->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_operators->set_toggle_mode(true);
+ search_operators->set_pressed(true);
+ search_operators->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_operators);
+
+ hbox->add_child(memnew(VSeparator));
+
+ search_methods = memnew(Button);
+ search_methods->set_flat(true);
+ search_methods->set_tooltip(TTR("Search Methods"));
+ search_methods->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_methods->set_toggle_mode(true);
+ search_methods->set_pressed(true);
+ search_methods->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_methods);
+
+ search_signals = memnew(Button);
+ search_signals->set_flat(true);
+ search_signals->set_tooltip(TTR("Search Signals"));
+ search_signals->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_signals->set_toggle_mode(true);
+ search_signals->set_pressed(true);
+ search_signals->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_signals);
+
+ search_constants = memnew(Button);
+ search_constants->set_flat(true);
+ search_constants->set_tooltip(TTR("Search Constants"));
+ search_constants->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_constants->set_toggle_mode(true);
+ search_constants->set_pressed(true);
+ search_constants->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_constants);
+
+ search_properties = memnew(Button);
+ search_properties->set_flat(true);
+ search_properties->set_tooltip(TTR("Search Properties"));
+ search_properties->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_properties->set_toggle_mode(true);
+ search_properties->set_pressed(true);
+ search_properties->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_properties);
+
+ search_theme_items = memnew(Button);
+ search_theme_items->set_flat(true);
+ search_theme_items->set_tooltip(TTR("Search Theme Items"));
+ search_theme_items->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
+ search_theme_items->set_toggle_mode(true);
+ search_theme_items->set_pressed(true);
+ search_theme_items->set_focus_mode(Control::FOCUS_NONE);
+ hbox->add_child(search_theme_items);
+
+ scope_combo = memnew(OptionButton);
+ scope_combo->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
+ scope_combo->set_tooltip(TTR("Select the search limits"));
+ scope_combo->set_stretch_ratio(0); // Fixed width.
+ scope_combo->add_item(TTR("Search Related"), SCOPE_RELATED);
+ scope_combo->add_separator();
+ scope_combo->add_item(TTR("Search Base"), SCOPE_BASE);
+ scope_combo->add_item(TTR("Search Inheriters"), SCOPE_INHERITERS);
+ scope_combo->add_item(TTR("Search Unrelated"), SCOPE_UNRELATED);
+ scope_combo->add_item(TTR("Search All"), SCOPE_ALL);
+ scope_combo->connect("item_selected", callable_mp(this, &VisualScriptPropertySelector::_update_results_i));
+ hbox->add_child(scope_combo);
- for (const String &E : fnodes) {
- if (!E.begins_with(root_filter)) {
- continue;
- }
- Vector<String> path = E.split("/");
-
- // check if the name has the filter
- bool in_filter = false;
- Vector<String> tx_filters = search_box->get_text().split(" ");
- for (int i = 0; i < tx_filters.size(); i++) {
- if (tx_filters[i].is_empty()) {
- in_filter = true;
- } else {
- in_filter = false;
- }
- if (E.findn(tx_filters[i]) != -1) {
- in_filter = true;
- break;
- }
- }
- if (!in_filter) {
- continue;
- }
+ search_box = memnew(LineEdit);
+ search_box->set_tooltip(TTR("Enter \" \" to show all filterd options\nEnter \".\" to show all filterd methods, operators and constructors\nUse CTRL_KEY to drop property setters"));
+ search_box->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
+ search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ search_box->connect("text_changed", callable_mp(this, &VisualScriptPropertySelector::_update_results_s));
+ search_box->connect("gui_input", callable_mp(this, &VisualScriptPropertySelector::_sbox_input));
+ register_text_enter(search_box);
+ vbox->add_child(search_box);
+
+ results_tree = memnew(Tree);
+ results_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ results_tree->set_hide_root(true);
+ results_tree->set_hide_folding(false);
+ results_tree->set_columns(2);
+ results_tree->set_column_title(0, TTR("Name"));
+ results_tree->set_column_clip_content(0, true);
+ results_tree->set_column_title(1, TTR("Member Type"));
+ results_tree->set_column_expand(1, false);
+ results_tree->set_column_custom_minimum_width(1, 150 * EDSCALE);
+ results_tree->set_column_clip_content(1, true);
+ results_tree->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
+ results_tree->set_select_mode(Tree::SELECT_ROW);
+ results_tree->connect("item_activated", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
+ results_tree->connect("item_selected", callable_mp(this, &VisualScriptPropertySelector::_item_selected));
+ vbox->add_child(results_tree);
- bool in_modifier = p_modifiers.is_empty();
- for (Set<String>::Element *F = p_modifiers.front(); F && in_modifier; F = F->next()) {
- if (E.findn(F->get()) != -1) {
- in_modifier = true;
- }
- }
- if (!in_modifier) {
- continue;
- }
+ help_bit = memnew(EditorHelpBit);
+ vbox->add_child(help_bit);
+ help_bit->connect("request_hide", callable_mp(this, &VisualScriptPropertySelector::_hide_requested));
+ get_ok_button()->set_text(TTR("Open"));
+ get_ok_button()->set_disabled(true);
+ set_hide_on_ok(false);
+}
- TreeItem *item = search_options->create_item(root);
- Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(E);
- Ref<VisualScriptOperator> vnode_operator = vnode;
- String type_name;
- if (vnode_operator.is_valid()) {
- String type;
- if (path.size() >= 2) {
- type = path[1];
- }
- type_name = type.capitalize() + " ";
+bool VisualScriptPropertySelector::SearchRunner::_is_class_disabled_by_feature_profile(const StringName &p_class) {
+ Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
+ if (profile.is_null()) {
+ return false;
+ }
+
+ StringName class_name = p_class;
+ while (class_name != StringName()) {
+ if (!ClassDB::class_exists(class_name)) {
+ return false;
}
- Ref<VisualScriptFunctionCall> vnode_function_call = vnode;
- if (vnode_function_call.is_valid()) {
- String basic_type = Variant::get_type_name(vnode_function_call->get_basic_type());
- type_name = basic_type.capitalize() + " ";
+
+ if (profile->is_class_disabled(class_name)) {
+ return true;
}
- Ref<VisualScriptConstructor> vnode_constructor = vnode;
- if (vnode_constructor.is_valid()) {
- type_name = "Construct ";
+ class_name = ClassDB::get_parent_class(class_name);
+ }
+
+ return false;
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_is_class_disabled_by_scope(const StringName &p_class) {
+ bool is_base_script = false;
+ if (p_class == selector_ui->base_script) {
+ is_base_script = true;
+ }
+ bool is_base = false;
+ if (selector_ui->base_type == p_class) {
+ is_base = true;
+ }
+ bool is_parent = false;
+ if ((ClassDB::is_parent_class(selector_ui->base_type, p_class)) && !is_base) {
+ is_parent = true;
+ }
+
+ bool is_inheriter = false;
+ List<StringName> inheriters;
+ ClassDB::get_inheriters_from_class(selector_ui->base_type, &inheriters);
+ if (inheriters.find(p_class)) {
+ is_inheriter = true;
+ }
+
+ if (scope_flags & SCOPE_BASE) {
+ if (is_base_script || is_base || is_parent) {
+ return false;
}
- Ref<VisualScriptDeconstruct> vnode_deconstruct = vnode;
- if (vnode_deconstruct.is_valid()) {
- type_name = "Deconstruct ";
+ }
+ if (scope_flags & SCOPE_INHERITERS) {
+ if (is_base_script || is_base || is_inheriter) {
+ return false;
}
- Vector<String> desc = path[path.size() - 1].replace("(", " ").replace(")", " ").replace(",", " ").split(" ");
- for (int i = 0; i < desc.size(); i++) {
- desc.write[i] = desc[i].capitalize();
- if (desc[i].ends_with(",")) {
- desc.write[i] = desc[i].replace(",", ", ");
- }
+ }
+ // if (scope_flags & SCOPE_RELATED) {
+ // /* code */
+ // }
+ if (scope_flags & SCOPE_UNRELATED) {
+ if (!is_base_script && !is_base && !is_inheriter) {
+ return false;
}
-
- item->set_text(0, type_name + String("").join(desc));
- item->set_icon(0, vbc->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
- item->set_selectable(0, true);
- item->set_metadata(0, E);
- item->set_selectable(0, true);
- item->set_metadata(1, "visualscript");
- item->set_selectable(1, false);
- item->set_selectable(2, false);
- item->set_metadata(2, connecting);
}
+ return true;
}
-void VisualScriptPropertySelector::_confirmed() {
- TreeItem *ti = search_options->get_selected();
- if (!ti) {
- return;
+bool VisualScriptPropertySelector::SearchRunner::_slice() {
+ bool phase_done = false;
+ switch (phase) {
+ case PHASE_INIT:
+ phase_done = _phase_init();
+ break;
+ case PHASE_MATCH_CLASSES_INIT:
+ phase_done = _phase_match_classes_init();
+ break;
+ case PHASE_NODE_CLASSES_INIT:
+ phase_done = _phase_node_classes_init();
+ break;
+ case PHASE_NODE_CLASSES_BUILD:
+ phase_done = _phase_node_classes_build();
+ break;
+ case PHASE_MATCH_CLASSES:
+ phase_done = _phase_match_classes();
+ break;
+ case PHASE_CLASS_ITEMS_INIT:
+ phase_done = _phase_class_items_init();
+ break;
+ case PHASE_CLASS_ITEMS:
+ phase_done = _phase_class_items();
+ break;
+ case PHASE_MEMBER_ITEMS_INIT:
+ phase_done = _phase_member_items_init();
+ break;
+ case PHASE_MEMBER_ITEMS:
+ phase_done = _phase_member_items();
+ break;
+ case PHASE_SELECT_MATCH:
+ phase_done = _phase_select_match();
+ break;
+ case PHASE_MAX:
+ return true;
+ default:
+ WARN_PRINT("Invalid or unhandled phase in EditorHelpSearch::Runner, aborting search.");
+ return true;
+ };
+
+ if (phase_done) {
+ phase++;
}
- emit_signal(SNAME("selected"), ti->get_metadata(0), ti->get_metadata(1), ti->get_metadata(2));
- set_visible(false);
+ return false;
}
-void VisualScriptPropertySelector::_item_selected() {
- help_bit->set_text("");
-
- TreeItem *item = search_options->get_selected();
- if (!item) {
- return;
+bool VisualScriptPropertySelector::SearchRunner::_phase_init() {
+ search_flags = 0; // selector_ui->filter_combo->get_selected_id();
+ if (selector_ui->search_visual_script_nodes->is_pressed()) {
+ search_flags |= SEARCH_VISUAL_SCRIPT_NODES;
}
- String name = item->get_metadata(0);
-
- String class_type;
- if (type != Variant::NIL) {
- class_type = Variant::get_type_name(type);
-
- } else {
- class_type = base_type;
+ if (selector_ui->search_classes->is_pressed()) {
+ search_flags |= SEARCH_CLASSES;
}
+ // if (selector_ui->search_constructors->is_pressed()) {
+ search_flags |= SEARCH_CONSTRUCTORS;
+ // }
+ if (selector_ui->search_methods->is_pressed()) {
+ search_flags |= SEARCH_METHODS;
+ }
+ if (selector_ui->search_operators->is_pressed()) {
+ search_flags |= SEARCH_OPERATORS;
+ }
+ if (selector_ui->search_signals->is_pressed()) {
+ search_flags |= SEARCH_SIGNALS;
+ }
+ if (selector_ui->search_constants->is_pressed()) {
+ search_flags |= SEARCH_CONSTANTS;
+ }
+ if (selector_ui->search_properties->is_pressed()) {
+ search_flags |= SEARCH_PROPERTIES;
+ }
+ if (selector_ui->search_theme_items->is_pressed()) {
+ search_flags |= SEARCH_THEME_ITEMS;
+ }
+ if (selector_ui->case_sensitive_button->is_pressed()) {
+ search_flags |= SEARCH_CASE_SENSITIVE;
+ }
+ if (selector_ui->hierarchy_button->is_pressed()) {
+ search_flags |= SEARCH_SHOW_HIERARCHY;
+ }
+ scope_flags = selector_ui->scope_combo->get_selected_id();
- DocTools *dd = EditorHelp::get_doc_data();
- String text;
-
- String at_class = class_type;
+ return true;
+}
- while (!at_class.is_empty()) {
- Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class);
- if (E) {
- for (int i = 0; i < E->get().properties.size(); i++) {
- if (E->get().properties[i].name == name) {
- text = DTR(E->get().properties[i].description);
+bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes_init() {
+ combined_docs = EditorHelp::get_doc_data()->class_list;
+ matches.clear();
+ matched_item = nullptr;
+ match_highest_score = 0;
+
+ if (
+ (selector_ui->base_script.unquote() != "") &&
+ (selector_ui->base_script.unquote() != ".") &&
+ !combined_docs.has(selector_ui->base_script)) {
+ String file_path = "res://" + selector_ui->base_script.unquote(); // EditorHelp::get_doc_data().name to filepath
+ Ref<Script> script;
+ script = ResourceLoader::load(file_path);
+ if (!script.is_null()) {
+ DocData::ClassDoc class_doc = DocData::ClassDoc();
+
+ class_doc.name = selector_ui->base_script;
+
+ class_doc.inherits = script->get_instance_base_type();
+ class_doc.brief_description = ".vs files not suported by EditorHelp::get_doc_data()";
+ class_doc.description = "";
+
+ Object *obj = ObjectDB::get_instance(script->get_instance_id());
+ if (Object::cast_to<Script>(obj)) {
+ List<MethodInfo> methods;
+ Object::cast_to<Script>(obj)->get_script_method_list(&methods);
+ for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) {
+ class_doc.methods.push_back(_get_method_doc(M->get()));
}
- }
- }
- at_class = ClassDB::get_parent_class_nocheck(at_class);
- }
- at_class = class_type;
+ List<MethodInfo> signals;
+ Object::cast_to<Script>(obj)->get_script_signal_list(&signals);
+ for (List<MethodInfo>::Element *S = signals.front(); S; S = S->next()) {
+ class_doc.signals.push_back(_get_method_doc(S->get()));
+ }
- while (!at_class.is_empty()) {
- Map<String, DocData::ClassDoc>::Element *C = dd->class_list.find(at_class);
- if (C) {
- for (int i = 0; i < C->get().methods.size(); i++) {
- if (C->get().methods[i].name == name) {
- text = DTR(C->get().methods[i].description);
+ List<PropertyInfo> propertys;
+ Object::cast_to<Script>(obj)->get_script_property_list(&propertys);
+ for (List<PropertyInfo>::Element *P = propertys.front(); P; P = P->next()) {
+ DocData::PropertyDoc pd = DocData::PropertyDoc();
+ pd.name = P->get().name;
+ pd.type = Variant::get_type_name(P->get().type);
+ class_doc.properties.push_back(pd);
}
}
+ combined_docs.insert(class_doc.name, class_doc);
}
+ }
+ iterator_doc = combined_docs.front();
+ return true;
+}
- at_class = ClassDB::get_parent_class_nocheck(at_class);
+bool VisualScriptPropertySelector::SearchRunner::_phase_node_classes_init() {
+ VisualScriptLanguage::singleton->get_registered_node_names(&vs_nodes);
+ _add_class_doc("functions", "", "");
+ _add_class_doc("operators", "", "");
+ return true;
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_phase_node_classes_build() {
+ if (vs_nodes.is_empty()) {
+ return true;
}
- Vector<String> functions = name.rsplit("/", false);
- at_class = functions.size() > 3 ? functions[functions.size() - 2] : class_type;
- Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(at_class);
- if (T) {
- for (int i = 0; i < T->get().methods.size(); i++) {
- if (T->get().methods[i].name == functions[functions.size() - 1]) {
- text = DTR(T->get().methods[i].description);
+ String registerd_node_name = vs_nodes[0];
+ vs_nodes.pop_front();
+
+ Vector<String> path = registerd_node_name.split("/");
+ if (path[0] == "constants") {
+ _add_class_doc(registerd_node_name, "", "constants");
+ } else if (path[0] == "custom") {
+ _add_class_doc(registerd_node_name, "", "custom");
+ } else if (path[0] == "data") {
+ _add_class_doc(registerd_node_name, "", "data");
+ } else if (path[0] == "flow_control") {
+ _add_class_doc(registerd_node_name, "", "flow_control");
+ } else if (path[0] == "functions") {
+ if (path[1] == "built_in") {
+ _add_class_doc(registerd_node_name, "functions", "built_in");
+ } else if (path[1] == "by_type") {
+ if (search_flags & SEARCH_CLASSES) {
+ _add_class_doc(registerd_node_name, path[2], "by_type_class");
+ }
+ } else if (path[1] == "constructors") {
+ if (search_flags & SEARCH_CLASSES) {
+ _add_class_doc(registerd_node_name, path[2].substr(0, path[2].find_char('(')), "constructors_class");
}
+ } else if (path[1] == "deconstruct") {
+ _add_class_doc(registerd_node_name, "", "deconstruct");
+ } else if (path[1] == "wait") {
+ _add_class_doc(registerd_node_name, "functions", "yield");
+ } else {
+ _add_class_doc(registerd_node_name, "functions", "");
+ }
+ } else if (path[0] == "index") {
+ _add_class_doc(registerd_node_name, "", "index");
+ } else if (path[0] == "operators") {
+ if (path[1] == "bitwise") {
+ _add_class_doc(registerd_node_name, "operators", "bitwise");
+ } else if (path[1] == "compare") {
+ _add_class_doc(registerd_node_name, "operators", "compare");
+ } else if (path[1] == "logic") {
+ _add_class_doc(registerd_node_name, "operators", "logic");
+ } else if (path[1] == "math") {
+ _add_class_doc(registerd_node_name, "operators", "math");
+ } else {
+ _add_class_doc(registerd_node_name, "operators", "");
}
}
+ return false;
+}
- List<String> *names = memnew(List<String>);
- VisualScriptLanguage::singleton->get_registered_node_names(names);
- if (names->find(name) != nullptr) {
- Ref<VisualScriptOperator> operator_node = VisualScriptLanguage::singleton->create_node_from_name(name);
- if (operator_node.is_valid()) {
- Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(operator_node->get_class_name());
- if (F) {
- text = Variant::get_operator_name(operator_node->get_operator());
+bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes() {
+ DocData::ClassDoc &class_doc = iterator_doc->value();
+ if (
+ (!_is_class_disabled_by_feature_profile(class_doc.name) && !_is_class_disabled_by_scope(class_doc.name)) ||
+ _match_visual_script(class_doc)) {
+ if (class_doc.inherits == "VisualScriptCustomNode") {
+ class_doc.script_path = "res://" + class_doc.name.unquote();
+ Ref<Script> script = ResourceLoader::load(class_doc.script_path);
+ Ref<VisualScriptCustomNode> vsn;
+ vsn.instantiate();
+ vsn->set_script(script);
+ class_doc.name = vsn->get_caption();
+ if (combined_docs.has(vsn->get_category())) {
+ class_doc.inherits = vsn->get_category();
+ } else if (combined_docs.has("VisualScriptNode/" + vsn->get_category())) {
+ class_doc.inherits = "VisualScriptNode/" + vsn->get_category();
+ } else if (combined_docs.has("VisualScriptCustomNode/" + vsn->get_category())) {
+ class_doc.inherits = "VisualScriptCustomNode/" + vsn->get_category();
+ } else {
+ class_doc.inherits = "";
}
+ class_doc.category = "VisualScriptCustomNode/" + vsn->get_category();
+ class_doc.brief_description = "";
+ class_doc.constructors.clear();
+ class_doc.methods.clear();
+ class_doc.operators.clear();
+ class_doc.signals.clear();
+ class_doc.constants.clear();
+ class_doc.enums.clear();
+ class_doc.properties.clear();
+ class_doc.theme_properties.clear();
}
- Ref<VisualScriptTypeCast> typecast_node = VisualScriptLanguage::singleton->create_node_from_name(name);
- if (typecast_node.is_valid()) {
- Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(typecast_node->get_class_name());
- if (F) {
- text = DTR(F->get().description);
+
+ matches[class_doc.name] = ClassMatch();
+ ClassMatch &match = matches[class_doc.name];
+
+ match.category = class_doc.category;
+ match.doc = &class_doc;
+ // Match class name.
+ if (search_flags & SEARCH_CLASSES || _match_visual_script(class_doc)) {
+ if (term == "") {
+ match.name = !_match_is_hidden(class_doc);
+ } else {
+ match.name = _match_string(term, class_doc.name);
}
+ // match.name = term == "" || _match_string(term, class_doc.name);
}
- Ref<VisualScriptBuiltinFunc> builtin_node = VisualScriptLanguage::singleton->create_node_from_name(name);
- if (builtin_node.is_valid()) {
- Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(builtin_node->get_class_name());
- if (F) {
- for (int i = 0; i < F->get().constants.size(); i++) {
- if (F->get().constants[i].value.to_int() == int(builtin_node->get_func())) {
- text = DTR(F->get().constants[i].description);
+ // Match members if the term is long enough.
+ if (term.length() >= 0) {
+ if (search_flags & SEARCH_CONSTRUCTORS) {
+ for (int i = 0; i < class_doc.constructors.size(); i++) {
+ String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.constructors[i].name : class_doc.constructors[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ term == " " ||
+ (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
+ match.constructors.push_back(const_cast<DocData::MethodDoc *>(&class_doc.constructors[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_METHODS) {
+ for (int i = 0; i < class_doc.methods.size(); i++) {
+ String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.methods[i].name : class_doc.methods[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ term == " " ||
+ (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
+ match.methods.push_back(const_cast<DocData::MethodDoc *>(&class_doc.methods[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_OPERATORS) {
+ for (int i = 0; i < class_doc.operators.size(); i++) {
+ String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.operators[i].name : class_doc.operators[i].name.to_lower();
+ if (method_name.find(term) > -1 ||
+ term == " " ||
+ (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
+ (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+ (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
+ match.operators.push_back(const_cast<DocData::MethodDoc *>(&class_doc.operators[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_SIGNALS) {
+ for (int i = 0; i < class_doc.signals.size(); i++) {
+ if (_match_string(term, class_doc.signals[i].name) ||
+ term == " ") {
+ match.signals.push_back(const_cast<DocData::MethodDoc *>(&class_doc.signals[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_CONSTANTS) {
+ for (int i = 0; i < class_doc.constants.size(); i++) {
+ if (_match_string(term, class_doc.constants[i].name) ||
+ term == " ") {
+ match.constants.push_back(const_cast<DocData::ConstantDoc *>(&class_doc.constants[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_PROPERTIES) {
+ for (int i = 0; i < class_doc.properties.size(); i++) {
+ if (_match_string(term, class_doc.properties[i].name) ||
+ term == " " ||
+ _match_string(term, class_doc.properties[i].getter) ||
+ _match_string(term, class_doc.properties[i].setter)) {
+ match.properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.properties[i]));
+ }
+ }
+ }
+ if (search_flags & SEARCH_THEME_ITEMS) {
+ for (int i = 0; i < class_doc.theme_properties.size(); i++) {
+ if (_match_string(term, class_doc.theme_properties[i].name) ||
+ term == " ") {
+ match.theme_properties.push_back(const_cast<DocData::ThemeItemDoc *>(&class_doc.theme_properties[i]));
}
}
}
}
}
- memdelete(names);
+ iterator_doc = iterator_doc->next();
+ return !iterator_doc;
+}
- if (text.is_empty()) {
- return;
- }
+bool VisualScriptPropertySelector::SearchRunner::_phase_class_items_init() {
+ results_tree->clear();
+ iterator_match = matches.front();
- help_bit->set_text(text);
-}
+ root_item = results_tree->create_item();
+ class_items.clear();
-void VisualScriptPropertySelector::_hide_requested() {
- _cancel_pressed(); // From AcceptDialog.
+ return true;
}
-void VisualScriptPropertySelector::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- connect("confirmed", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
+bool VisualScriptPropertySelector::SearchRunner::_phase_class_items() {
+ if (!iterator_match) {
+ return true;
}
-}
-void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, const bool p_virtuals_only, const bool p_connecting, bool clear_text) {
- base_type = p_base;
- selected = p_current;
- type = Variant::NIL;
- properties = false;
- instance = nullptr;
- virtuals_only = p_virtuals_only;
+ ClassMatch &match = iterator_match->value();
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
+ if (search_flags & SEARCH_SHOW_HIERARCHY) {
+ if (match.required()) {
+ _create_class_hierarchy(match);
+ }
} else {
- search_box->select_all();
+ if (match.name) {
+ _create_class_item(root_item, match.doc, true);
+ }
}
- search_box->grab_focus();
- connecting = p_connecting;
- _update_search();
+ iterator_match = iterator_match->next();
+ return !iterator_match;
}
-void VisualScriptPropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) {
- type_filter = p_type_filter;
+bool VisualScriptPropertySelector::SearchRunner::_phase_member_items_init() {
+ iterator_match = matches.front();
+
+ return true;
}
-void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only, bool p_seq_connect, const bool p_connecting, bool clear_text) {
- base_type = p_base;
- selected = p_current;
- type = Variant::NIL;
- properties = true;
- visual_script_generic = false;
- instance = nullptr;
- virtuals_only = p_virtuals_only;
+bool VisualScriptPropertySelector::SearchRunner::_phase_member_items() {
+ if (!iterator_match) {
+ return true;
+ }
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
+ ClassMatch &match = iterator_match->value();
+
+ TreeItem *parent = (search_flags & SEARCH_SHOW_HIERARCHY) ? class_items[match.doc->name] : root_item;
+ bool constructor_created = false;
+ for (int i = 0; i < match.methods.size(); i++) {
+ String text = match.methods[i]->name;
+ if (!constructor_created) {
+ if (match.doc->name == match.methods[i]->name) {
+ text += " " + TTR("(constructors)");
+ constructor_created = true;
+ }
+ } else {
+ if (match.doc->name == match.methods[i]->name) {
+ continue;
+ }
+ }
+ _create_method_item(parent, match.doc, text, match.methods[i]);
+ }
+ for (int i = 0; i < match.signals.size(); i++) {
+ _create_signal_item(parent, match.doc, match.signals[i]);
+ }
+ for (int i = 0; i < match.constants.size(); i++) {
+ _create_constant_item(parent, match.doc, match.constants[i]);
+ }
+ for (int i = 0; i < match.properties.size(); i++) {
+ _create_property_item(parent, match.doc, match.properties[i]);
+ }
+ for (int i = 0; i < match.theme_properties.size(); i++) {
+ _create_theme_property_item(parent, match.doc, match.theme_properties[i]);
+ }
+
+ iterator_match = iterator_match->next();
+ return !iterator_match;
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_phase_select_match() {
+ if (matched_item) {
+ matched_item->select(0);
+ }
+ return true;
+}
+
+bool VisualScriptPropertySelector::SearchRunner::_match_string(const String &p_term, const String &p_string) const {
+ if (search_flags & SEARCH_CASE_SENSITIVE) {
+ return p_string.find(p_term) > -1;
} else {
- search_box->select_all();
+ return p_string.findn(p_term) > -1;
}
- search_box->grab_focus();
- seq_connect = p_seq_connect;
- connecting = p_connecting;
+}
- _update_search();
+bool VisualScriptPropertySelector::SearchRunner::_match_visual_script(DocData::ClassDoc &class_doc) {
+ if (class_doc.category.ends_with("_class")) {
+ if (class_doc.category.begins_with("VisualScript") && search_flags & SEARCH_CLASSES) {
+ if (matches.has(class_doc.inherits)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (class_doc.category.begins_with("VisualScript") && search_flags & SEARCH_VISUAL_SCRIPT_NODES) {
+ return true;
+ }
+ if (class_doc.name.begins_with("operators") && search_flags & SEARCH_OPERATORS) {
+ return true;
+ }
+ if (class_doc.category.begins_with("VisualScriptNode/deconstruct")) {
+ if (class_doc.name.find(selector_ui->base_type, 0) > -1) {
+ return true;
+ }
+ }
+
+ return false;
}
-void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current, const bool p_connecting, bool clear_text) {
- ERR_FAIL_COND(p_script.is_null());
+bool VisualScriptPropertySelector::SearchRunner::_match_is_hidden(DocData::ClassDoc &class_doc) {
+ if (class_doc.category.begins_with("VisualScript")) {
+ if (class_doc.name.begins_with("flow_control")) {
+ return false;
+ } else if (class_doc.name.begins_with("operators")) {
+ return !(search_flags & SEARCH_OPERATORS);
+ } else if (class_doc.name.begins_with("functions/built_in/print")) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
- base_type = p_script->get_instance_base_type();
- selected = p_current;
- type = Variant::NIL;
- script = p_script->get_instance_id();
- properties = true;
- visual_script_generic = false;
- instance = nullptr;
- virtuals_only = false;
+void VisualScriptPropertySelector::SearchRunner::_match_item(TreeItem *p_item, const String &p_text) {
+ float inverse_length = 1.f / float(p_text.length());
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
- } else {
- search_box->select_all();
+ // Favor types where search term is a substring close to the start of the type.
+ float w = 0.5f;
+ int pos = p_text.findn(term);
+ float score = (pos > -1) ? 1.0f - w * MIN(1, 3 * pos * inverse_length) : MAX(0.f, .9f - w);
+
+ // Favor shorter items: they resemble the search term more.
+ w = 0.1f;
+ score *= (1 - w) + w * (term.length() * inverse_length);
+
+ if (match_highest_score == 0 || score > match_highest_score) {
+ matched_item = p_item;
+ match_highest_score = score;
}
- search_box->grab_focus();
- seq_connect = false;
- connecting = p_connecting;
+}
- _update_search();
+void VisualScriptPropertySelector::SearchRunner::_add_class_doc(String class_name, String inherits, String category) {
+ DocData::ClassDoc class_doc = DocData::ClassDoc();
+ class_doc.name = class_name;
+ class_doc.inherits = inherits;
+ class_doc.category = "VisualScriptNode/" + category;
+ class_doc.brief_description = category;
+ combined_docs.insert(class_doc.name, class_doc);
}
-void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current, const bool p_connecting, bool clear_text) {
- ERR_FAIL_COND(p_type == Variant::NIL);
- base_type = "";
- selected = p_current;
- type = p_type;
- properties = true;
- visual_script_generic = false;
- instance = nullptr;
- virtuals_only = false;
+DocData::MethodDoc VisualScriptPropertySelector::SearchRunner::_get_method_doc(MethodInfo method_info) {
+ DocData::MethodDoc method_doc = DocData::MethodDoc();
+ method_doc.name = method_info.name;
+ method_doc.return_type = Variant::get_type_name(method_info.return_val.type);
+ method_doc.description = "No description available";
+ for (List<PropertyInfo>::Element *P = method_info.arguments.front(); P; P = P->next()) {
+ DocData::ArgumentDoc argument_doc = DocData::ArgumentDoc();
+ argument_doc.name = P->get().name;
+ argument_doc.type = Variant::get_type_name(P->get().type);
+ method_doc.arguments.push_back(argument_doc);
+ }
+ return method_doc;
+}
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
- } else {
- search_box->select_all();
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_class_hierarchy(const ClassMatch &p_match) {
+ if (class_items.has(p_match.doc->name)) {
+ return class_items[p_match.doc->name];
}
- search_box->grab_focus();
- seq_connect = false;
- connecting = p_connecting;
- _update_search();
+ // Ensure parent nodes are created first.
+ TreeItem *parent = root_item;
+ if (p_match.doc->inherits != "") {
+ if (class_items.has(p_match.doc->inherits)) {
+ parent = class_items[p_match.doc->inherits];
+ } else if (matches.has(p_match.doc->inherits)) {
+ ClassMatch &base_match = matches[p_match.doc->inherits];
+ parent = _create_class_hierarchy(base_match);
+ }
+ }
+
+ TreeItem *class_item = _create_class_item(parent, p_match.doc, !p_match.name);
+ class_items[p_match.doc->name] = class_item;
+ return class_item;
}
-void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current, const bool p_connecting, bool clear_text) {
- base_type = p_type;
- selected = p_current;
- type = Variant::NIL;
- properties = false;
- visual_script_generic = false;
- instance = nullptr;
- virtuals_only = false;
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
+ Ref<Texture2D> icon = empty_icon;
+ String text_0 = p_doc->name;
+ String text_1 = "Class";
+
+ String what = "Class";
+ String details = p_doc->name;
+ if (p_doc->category.begins_with("VisualScriptCustomNode/")) {
+ Vector<String> path = p_doc->name.split("/");
+ icon = ui_service->get_theme_icon("VisualScript", "EditorIcons");
+ text_0 = path[path.size() - 1];
+ text_1 = "VisualScriptCustomNode";
+ what = "VisualScriptCustomNode";
+ details = "CustomNode";
+ } else if (p_doc->category.begins_with("VisualScriptNode/")) {
+ Vector<String> path = p_doc->name.split("/");
+ icon = ui_service->get_theme_icon("VisualScript", "EditorIcons");
+ text_0 = path[path.size() - 1];
+ if (p_doc->category.begins_with("VisualScriptNode/deconstruct")) {
+ text_0 = "deconstruct " + text_0;
+ }
+ text_1 = "VisualScriptNode";
+ what = "VisualScriptNode";
+ details = p_doc->name;
+
+ if (path.size() == 1) {
+ if (path[0] == "functions" || path[0] == "operators") {
+ text_1 = "VisualScript";
+ p_gray = true;
+ what = "no_result";
+ details = "";
+ }
+ }
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
} else {
- search_box->select_all();
+ if (p_doc->name.is_quoted()) {
+ text_0 = p_doc->name.unquote().get_file();
+ if (ui_service->has_theme_icon(p_doc->inherits, "EditorIcons")) {
+ icon = ui_service->get_theme_icon(p_doc->inherits, "EditorIcons");
+ }
+ } else if (ui_service->has_theme_icon(p_doc->name, "EditorIcons")) {
+ icon = ui_service->get_theme_icon(p_doc->name, "EditorIcons");
+ } else if (ClassDB::class_exists(p_doc->name) && ClassDB::is_parent_class(p_doc->name, "Object")) {
+ icon = ui_service->get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
+ }
+ }
+ String tooltip = p_doc->brief_description.strip_edges();
+
+ TreeItem *item = results_tree->create_item(p_parent);
+ item->set_icon(0, icon);
+ item->set_text(0, text_0);
+ item->set_text(1, TTR(text_1));
+ item->set_tooltip(0, tooltip);
+ item->set_tooltip(1, tooltip);
+ item->set_metadata(0, details);
+ item->set_metadata(1, what);
+ if (p_gray) {
+ item->set_custom_color(0, disabled_color);
+ item->set_custom_color(1, disabled_color);
}
- search_box->grab_focus();
- seq_connect = true;
- connecting = p_connecting;
- _update_search();
+ _match_item(item, p_doc->name);
+
+ return item;
}
-void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current, const bool p_connecting, const String &p_basetype, bool clear_text) {
- base_type = p_basetype;
- selected = p_current;
- type = Variant::NIL;
- properties = true;
- visual_script_generic = false;
- instance = p_instance;
- virtuals_only = false;
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) {
+ String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
+ for (int i = 0; i < p_doc->arguments.size(); i++) {
+ const DocData::ArgumentDoc &arg = p_doc->arguments[i];
+ tooltip += arg.type + " " + arg.name;
+ if (arg.default_value != "") {
+ tooltip += " = " + arg.default_value;
+ }
+ if (i < p_doc->arguments.size() - 1) {
+ tooltip += ", ";
+ }
+ }
+ tooltip += ")";
+ return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, p_text, TTRC("Method"), "method", tooltip, p_doc->description);
+}
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
- } else {
- search_box->select_all();
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
+ String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
+ for (int i = 0; i < p_doc->arguments.size(); i++) {
+ const DocData::ArgumentDoc &arg = p_doc->arguments[i];
+ tooltip += arg.type + " " + arg.name;
+ if (arg.default_value != "") {
+ tooltip += " = " + arg.default_value;
+ }
+ if (i < p_doc->arguments.size() - 1) {
+ tooltip += ", ";
+ }
}
- search_box->grab_focus();
- seq_connect = false;
- connecting = p_connecting;
+ tooltip += ")";
+ return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, p_doc->name, TTRC("Signal"), "signal", tooltip, p_doc->description);
+}
- _update_search();
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc) {
+ String tooltip = p_class_doc->name + "." + p_doc->name;
+ return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, p_doc->name, TTRC("Constant"), "constant", tooltip, p_doc->description);
}
-void VisualScriptPropertySelector::select_from_visual_script(const String &p_base, const bool p_connecting, bool clear_text) {
- base_type = p_base;
- selected = "";
- type = Variant::NIL;
- properties = true;
- visual_script_generic = true;
- instance = nullptr;
- virtuals_only = false;
- show_window(.5f);
- if (clear_text) {
- search_box->set_text("");
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) {
+ String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
+ tooltip += "\n " + p_class_doc->name + "." + p_doc->setter + "(value) setter";
+ tooltip += "\n " + p_class_doc->name + "." + p_doc->getter + "() getter";
+ return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, p_doc->name, TTRC("Property"), "property", tooltip, p_doc->description);
+}
+
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc) {
+ String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
+ return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, p_doc->name, TTRC("Theme Property"), "theme_item", tooltip, p_doc->description);
+}
+
+TreeItem *VisualScriptPropertySelector::SearchRunner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_description) {
+ Ref<Texture2D> icon;
+ String text;
+ if (search_flags & SEARCH_SHOW_HIERARCHY) {
+ icon = ui_service->get_theme_icon(p_icon, SNAME("EditorIcons"));
+ text = p_text;
} else {
- search_box->select_all();
+ icon = ui_service->get_theme_icon(p_icon, SNAME("EditorIcons"));
+ text = p_class_name + "." + p_text;
}
- search_box->grab_focus();
- connecting = p_connecting;
- _update_search();
-}
+ TreeItem *item = results_tree->create_item(p_parent);
+ item->set_icon(0, icon);
+ item->set_text(0, text);
+ item->set_text(1, TTRGET(p_type));
+ item->set_tooltip(0, p_tooltip);
+ item->set_tooltip(1, p_tooltip);
+ item->set_metadata(0, p_class_name + ":" + p_name);
+ item->set_metadata(1, "class_" + p_metatype);
+ item->set_meta("description", p_description);
-void VisualScriptPropertySelector::show_window(float p_screen_ratio) {
- popup_centered_ratio(p_screen_ratio);
+ _match_item(item, p_name);
+
+ return item;
}
-void VisualScriptPropertySelector::_bind_methods() {
- ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "category"), PropertyInfo(Variant::BOOL, "connecting")));
+bool VisualScriptPropertySelector::SearchRunner::work(uint64_t slot) {
+ // Return true when the search has been completed, otherwise false.
+ const uint64_t until = OS::get_singleton()->get_ticks_usec() + slot;
+ while (!_slice()) {
+ if (OS::get_singleton()->get_ticks_usec() > until) {
+ return false;
+ }
+ }
+ return true;
}
-VisualScriptPropertySelector::VisualScriptPropertySelector() {
- vbc = memnew(VBoxContainer);
- add_child(vbc);
- //set_child_rect(vbc);
- search_box = memnew(LineEdit);
- vbc->add_margin_child(TTR("Search:"), search_box);
- search_box->connect("text_changed", callable_mp(this, &VisualScriptPropertySelector::_text_changed));
- search_box->connect("gui_input", callable_mp(this, &VisualScriptPropertySelector::_sbox_input));
- search_options = memnew(Tree);
- vbc->add_margin_child(TTR("Matches:"), search_options, true);
- get_ok_button()->set_text(TTR("Open"));
- get_ok_button()->set_disabled(true);
- register_text_enter(search_box);
- set_hide_on_ok(false);
- search_options->connect("item_activated", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
- search_options->connect("cell_selected", callable_mp(this, &VisualScriptPropertySelector::_item_selected));
- search_options->set_hide_root(true);
- search_options->set_hide_folding(true);
- virtuals_only = false;
- seq_connect = false;
- help_bit = memnew(EditorHelpBit);
- vbc->add_margin_child(TTR("Description:"), help_bit);
- help_bit->connect("request_hide", callable_mp(this, &VisualScriptPropertySelector::_hide_requested));
- search_options->set_columns(3);
- search_options->set_column_expand(1, false);
- search_options->set_column_expand(2, false);
+VisualScriptPropertySelector::SearchRunner::SearchRunner(VisualScriptPropertySelector *p_selector_ui, Tree *p_results_tree) :
+ selector_ui(p_selector_ui),
+ ui_service(p_selector_ui->vbox),
+ results_tree(p_results_tree),
+ term(p_selector_ui->search_box->get_text()),
+ empty_icon(ui_service->get_theme_icon(SNAME("ArrowRight"), SNAME("EditorIcons"))),
+ disabled_color(ui_service->get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))) {
}
diff --git a/modules/visual_script/editor/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h
index 9e065548a0..6b5112f1af 100644
--- a/modules/visual_script/editor/visual_script_property_selector.h
+++ b/modules/visual_script/editor/visual_script_property_selector.h
@@ -31,6 +31,7 @@
#ifndef VISUALSCRIPT_PROPERTYSELECTOR_H
#define VISUALSCRIPT_PROPERTYSELECTOR_H
+#include "../visual_script.h"
#include "editor/editor_help.h"
#include "editor/property_editor.h"
#include "scene/gui/rich_text_label.h"
@@ -38,15 +39,56 @@
class VisualScriptPropertySelector : public ConfirmationDialog {
GDCLASS(VisualScriptPropertySelector, ConfirmationDialog);
+ enum SearchFlags {
+ SEARCH_CLASSES = 1 << 0,
+ SEARCH_CONSTRUCTORS = 1 << 1,
+ SEARCH_METHODS = 1 << 2,
+ SEARCH_OPERATORS = 1 << 3,
+ SEARCH_SIGNALS = 1 << 4,
+ SEARCH_CONSTANTS = 1 << 5,
+ SEARCH_PROPERTIES = 1 << 6,
+ SEARCH_THEME_ITEMS = 1 << 7,
+ SEARCH_VISUAL_SCRIPT_NODES = 1 << 8,
+ SEARCH_ALL = SEARCH_CLASSES | SEARCH_CONSTRUCTORS | SEARCH_METHODS | SEARCH_OPERATORS | SEARCH_SIGNALS | SEARCH_CONSTANTS | SEARCH_PROPERTIES | SEARCH_THEME_ITEMS,
+ SEARCH_CASE_SENSITIVE = 1 << 29,
+ SEARCH_SHOW_HIERARCHY = 1 << 30,
+ };
+
+ enum ScopeFlags {
+ SCOPE_BASE = 1 << 0,
+ SCOPE_INHERITERS = 1 << 1,
+ SCOPE_UNRELATED = 1 << 2,
+ SCOPE_RELATED = SCOPE_BASE | SCOPE_INHERITERS,
+ SCOPE_ALL = SCOPE_BASE | SCOPE_INHERITERS | SCOPE_UNRELATED
+ };
+
LineEdit *search_box;
- Tree *search_options;
- void _text_changed(const String &p_newtext);
- void _sbox_input(const Ref<InputEvent> &p_ie);
- void _update_search();
+ Button *case_sensitive_button;
+ Button *hierarchy_button;
+
+ Button *search_visual_script_nodes;
+ Button *search_classes;
+ Button *search_operators;
+
+ Button *search_methods;
+ Button *search_signals;
+ Button *search_constants;
+ Button *search_properties;
+ Button *search_theme_items;
- void create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text);
- void get_visual_node_names(const String &root_filter, const Set<String> &p_modifiers, bool &found, TreeItem *const root, LineEdit *const search_box);
+ OptionButton *scope_combo;
+ Tree *results_tree;
+
+ class SearchRunner;
+ Ref<SearchRunner> search_runner;
+
+ void _update_icons();
+
+ void _sbox_input(const Ref<InputEvent> &p_ie);
+ void _update_results_i(int p_int);
+ void _update_results_s(String p_string);
+ void _update_results();
void _confirmed();
void _item_selected();
@@ -54,38 +96,124 @@ class VisualScriptPropertySelector : public ConfirmationDialog {
EditorHelpBit *help_bit;
- bool properties;
- bool visual_script_generic;
- bool connecting;
+ bool properties = false;
+ bool visual_script_generic = false;
+ bool connecting = false;
String selected;
Variant::Type type;
String base_type;
+ String base_script;
ObjectID script;
Object *instance;
- bool virtuals_only;
- bool seq_connect;
- VBoxContainer *vbc;
-
- Vector<Variant::Type> type_filter;
+ bool virtuals_only = false;
+ VBoxContainer *vbox;
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- void select_method_from_base_type(const String &p_base, const String &p_current = "", const bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true);
- void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false, const bool p_connecting = true, bool clear_text = true);
- void select_from_script(const Ref<Script> &p_script, const String &p_current = "", const bool p_connecting = true, bool clear_text = true);
- void select_from_basic_type(Variant::Type p_type, const String &p_current = "", const bool p_connecting = true, bool clear_text = true);
- void select_from_action(const String &p_type, const String &p_current = "", const bool p_connecting = true, bool clear_text = true);
- void select_from_instance(Object *p_instance, const String &p_current = "", const bool p_connecting = true, const String &p_basetype = "", bool clear_text = true);
- void select_from_visual_script(const String &p_base, const bool p_connecting = true, bool clear_text = true);
+ void select_method_from_base_type(const String &p_base, const bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true);
+ void select_from_base_type(const String &p_base, const String &p_base_script = "", bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true);
+ void select_from_script(const Ref<Script> &p_script, const bool p_connecting = true, bool clear_text = true);
+ void select_from_basic_type(Variant::Type p_type, const bool p_connecting = true, bool clear_text = true);
+ void select_from_action(const String &p_type, const bool p_connecting = true, bool clear_text = true);
+ void select_from_instance(Object *p_instance, const bool p_connecting = true, bool clear_text = true);
+ void select_from_visual_script(const Ref<Script> &p_script, bool clear_text = true);
void show_window(float p_screen_ratio);
- void set_type_filter(const Vector<Variant::Type> &p_type_filter);
-
VisualScriptPropertySelector();
};
+class VisualScriptPropertySelector::SearchRunner : public RefCounted {
+ enum Phase {
+ PHASE_INIT,
+ PHASE_MATCH_CLASSES_INIT,
+ PHASE_NODE_CLASSES_INIT,
+ PHASE_NODE_CLASSES_BUILD,
+ PHASE_MATCH_CLASSES,
+ PHASE_CLASS_ITEMS_INIT,
+ PHASE_CLASS_ITEMS,
+ PHASE_MEMBER_ITEMS_INIT,
+ PHASE_MEMBER_ITEMS,
+ PHASE_SELECT_MATCH,
+ PHASE_MAX
+ };
+ int phase = 0;
+
+ struct ClassMatch {
+ DocData::ClassDoc *doc;
+ bool name = false;
+ String category = "";
+ Vector<DocData::MethodDoc *> constructors;
+ Vector<DocData::MethodDoc *> methods;
+ Vector<DocData::MethodDoc *> operators;
+ Vector<DocData::MethodDoc *> signals;
+ Vector<DocData::ConstantDoc *> constants;
+ Vector<DocData::PropertyDoc *> properties;
+ Vector<DocData::ThemeItemDoc *> theme_properties;
+
+ bool required() {
+ return name || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size();
+ }
+ };
+
+ VisualScriptPropertySelector *selector_ui;
+ Control *ui_service;
+ Tree *results_tree;
+ String term;
+ int search_flags;
+ int scope_flags;
+
+ Ref<Texture2D> empty_icon;
+ Color disabled_color;
+
+ Map<String, DocData::ClassDoc>::Element *iterator_doc = nullptr;
+ Map<String, ClassMatch> matches;
+ Map<String, ClassMatch>::Element *iterator_match = nullptr;
+ TreeItem *root_item = nullptr;
+ Map<String, TreeItem *> class_items;
+ TreeItem *matched_item = nullptr;
+ float match_highest_score = 0;
+
+ Map<String, DocData::ClassDoc> combined_docs;
+ List<String> vs_nodes;
+
+ bool _is_class_disabled_by_feature_profile(const StringName &p_class);
+ bool _is_class_disabled_by_scope(const StringName &p_class);
+
+ bool _slice();
+ bool _phase_init();
+ bool _phase_match_classes_init();
+ bool _phase_node_classes_init();
+ bool _phase_node_classes_build();
+ bool _phase_match_classes();
+ bool _phase_class_items_init();
+ bool _phase_class_items();
+ bool _phase_member_items_init();
+ bool _phase_member_items();
+ bool _phase_select_match();
+
+ bool _match_string(const String &p_term, const String &p_string) const;
+ bool _match_visual_script(DocData::ClassDoc &class_doc);
+ bool _match_is_hidden(DocData::ClassDoc &class_doc);
+ void _match_item(TreeItem *p_item, const String &p_text);
+ void _add_class_doc(String class_name, String inherits, String category);
+ DocData::MethodDoc _get_method_doc(MethodInfo method_info);
+ TreeItem *_create_class_hierarchy(const ClassMatch &p_match);
+ TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray);
+ TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc);
+ TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc);
+ TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc);
+ TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
+ TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc);
+ TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_description);
+
+public:
+ bool work(uint64_t slot = 100000);
+
+ SearchRunner(VisualScriptPropertySelector *p_selector_ui, Tree *p_results_tree);
+};
+
#endif // VISUALSCRIPT_PROPERTYSELECTOR_H
diff --git a/modules/visual_script/icons/VisualScript.svg b/modules/visual_script/icons/VisualScript.svg
index 2352ba5d87..bc698247c9 100644
--- a/modules/visual_script/icons/VisualScript.svg
+++ b/modules/visual_script/icons/VisualScript.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#e0e0e0"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/></g></svg>
diff --git a/modules/visual_script/icons/VisualScriptInternal.svg b/modules/visual_script/icons/VisualScriptInternal.svg
index ea83047a29..8ab39ad929 100644
--- a/modules/visual_script/icons/VisualScriptInternal.svg
+++ b/modules/visual_script/icons/VisualScriptInternal.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="3" cy="3.000024" fill="#6e6e6e" r="0"/><path d="m11 10a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0"/><path d="m3 10v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4z" fill="#e0e0e0"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2z" fill="none" stroke="#e0e0e0"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="3" cy="3.000024" fill="#e0e0e0" r="0"/><path d="m11 10a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0"/><path d="m3 10v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4z" fill="#e0e0e0"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2z" fill="none" stroke="#e0e0e0"/></svg>
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 99e75f9289..fd55796a66 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -964,7 +964,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
return;
}
- if (p_inputs[0]->is_ref()) {
+ if (p_inputs[0]->is_ref_counted()) {
REF r = *p_inputs[0];
if (!r.is_valid()) {
return;
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 2abbd19e12..e8942b9788 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -328,6 +328,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
};
case '"': {
String str;
+ char32_t prev = 0;
while (true) {
char32_t ch = GET_CHAR();
@@ -364,9 +365,11 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
case 'r':
res = 13;
break;
+ case 'U':
case 'u': {
- // hex number
- for (int j = 0; j < 4; j++) {
+ // Hexadecimal sequence.
+ int hex_len = (next == 'U') ? 6 : 4;
+ for (int j = 0; j < hex_len; j++) {
char32_t c = GET_CHAR();
if (c == 0) {
@@ -374,13 +377,13 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
_set_error("Malformed hex constant in string");
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
char32_t v;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -403,12 +406,46 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} break;
}
+ // Parse UTF-16 pair.
+ if ((res & 0xfffffc00) == 0xd800) {
+ if (prev == 0) {
+ prev = res;
+ continue;
+ } else {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
+ } else if ((res & 0xfffffc00) == 0xdc00) {
+ if (prev == 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired trail surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ } else {
+ res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
+ prev = 0;
+ }
+ }
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += res;
-
} else {
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
str += ch;
}
}
+ if (prev != 0) {
+ _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
+ r_token.type = TK_ERROR;
+ return ERR_PARSE_ERROR;
+ }
r_token.type = TK_CONSTANT;
r_token.value = str;
@@ -420,7 +457,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
break;
}
- if (cchar >= '0' && cchar <= '9') {
+ if (is_digit(cchar)) {
//a number
String num;
@@ -439,7 +476,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
while (true) {
switch (reading) {
case READING_INT: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
//pass
} else if (c == '.') {
reading = READING_DEC;
@@ -452,7 +489,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} break;
case READING_DEC: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
} else if (c == 'e') {
reading = READING_EXP;
@@ -462,7 +499,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} break;
case READING_EXP: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
exp_beg = true;
} else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) {
@@ -495,11 +532,11 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
}
return OK;
- } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') {
+ } else if (is_ascii_char(cchar) || cchar == '_') {
String id;
bool first = true;
- while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) {
+ while (is_ascii_char(cchar) || cchar == '_' || (!first && is_digit(cchar))) {
id += String::chr(cchar);
cchar = GET_CHAR();
first = false;
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index cc18d48dd8..ef6c1ecdb9 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -912,7 +912,7 @@ int VisualScriptPropertySet::get_output_sequence_port_count() const {
}
bool VisualScriptPropertySet::has_input_sequence_port() const {
- return 1;
+ return true;
}
Node *VisualScriptPropertySet::_get_base_node() const {
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 618fe14137..416a674435 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -33,8 +33,7 @@
<method name="create_data_channel">
<return type="WebRTCDataChannel" />
<argument index="0" name="label" type="String" />
- <argument index="1" name="options" type="Dictionary" default="{
-}" />
+ <argument index="1" name="options" type="Dictionary" default="{}" />
<description>
Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [code]label[/code] and optionally configured via the [code]options[/code] dictionary. This method can only be called when the connection is in state [constant STATE_NEW].
There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]negotiated[/code] option set to [code]true[/code].
@@ -70,8 +69,7 @@
</method>
<method name="initialize">
<return type="int" enum="Error" />
- <argument index="0" name="configuration" type="Dictionary" default="{
-}" />
+ <argument index="0" name="configuration" type="Dictionary" default="{}" />
<description>
Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [code]options[/code] can be passed to configure the peer connection.
Valid [code]options[/code] are:
@@ -119,7 +117,7 @@
</methods>
<signals>
<signal name="data_channel_received">
- <argument index="0" name="channel" type="Object" />
+ <argument index="0" name="channel" type="WebRTCDataChannel" />
<description>
Emitted when a new in-band channel is received, i.e. when the channel was created with [code]negotiated: false[/code] (default).
The object will be an instance of [WebRTCDataChannel]. You must keep a reference of it or it will be closed automatically. See [method create_data_channel].
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index 54d4f57eef..e6a8dcc31a 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -38,11 +38,11 @@
#include "webrtc_peer_connection_extension.h"
void register_webrtc_types() {
-#define _SET_HINT(NAME, _VAL_, _MAX_) \
- GLOBAL_DEF(NAME, _VAL_); \
+#define SET_HINT(NAME, _VAL_, _MAX_) \
+ GLOBAL_DEF(NAME, _VAL_); \
ProjectSettings::get_singleton()->set_custom_property_info(NAME, PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater"));
- _SET_HINT(WRTC_IN_BUF, 64, 4096);
+ SET_HINT(WRTC_IN_BUF, 64, 4096);
ClassDB::register_custom_instance_class<WebRTCPeerConnection>();
GDREGISTER_CLASS(WebRTCPeerConnectionExtension);
@@ -51,6 +51,8 @@ void register_webrtc_types() {
GDREGISTER_CLASS(WebRTCDataChannelExtension);
GDREGISTER_CLASS(WebRTCMultiplayerPeer);
+
+#undef SET_HINT
}
void unregister_webrtc_types() {}
diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 96cf518c06..7fdf26d3cd 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -69,7 +69,7 @@ void WebRTCPeerConnection::_bind_methods() {
ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
- ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel")));
+ ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel", PROPERTY_HINT_RESOURCE_TYPE, "WebRTCDataChannel")));
BIND_ENUM_CONSTANT(STATE_NEW);
BIND_ENUM_CONSTANT(STATE_CONNECTING);
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index bccbf88417..bebb198126 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -124,17 +124,17 @@ bool WSLClient::_verify_headers(String &r_protocol) {
}
}
-#define _WSL_CHECK(NAME, VALUE) \
+#define WSL_CHECK(NAME, VALUE) \
ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
"Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
-#define _WSL_CHECK_NC(NAME, VALUE) \
+#define WSL_CHECK_NC(NAME, VALUE) \
ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME] != VALUE, false, \
"Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
- _WSL_CHECK("connection", "upgrade");
- _WSL_CHECK("upgrade", "websocket");
- _WSL_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key));
-#undef _WSL_CHECK_NC
-#undef _WSL_CHECK
+ WSL_CHECK("connection", "upgrade");
+ WSL_CHECK("upgrade", "websocket");
+ WSL_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key));
+#undef WSL_CHECK_NC
+#undef WSL_CHECK
if (_protocols.size() == 0) {
// We didn't request a custom protocol
ERR_FAIL_COND_V(headers.has("sec-websocket-protocol"), false);
@@ -163,22 +163,24 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
_peer = Ref<WSLPeer>(memnew(WSLPeer));
if (p_host.is_valid_ip_address()) {
- ip_candidates.clear();
- ip_candidates.push_back(IPAddress(p_host));
+ _ip_candidates.push_back(IPAddress(p_host));
} else {
- ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host);
- }
-
- ERR_FAIL_COND_V(ip_candidates.is_empty(), ERR_INVALID_PARAMETER);
-
- String port = "";
- if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
- port = ":" + itos(p_port);
+ // Queue hostname for resolution.
+ _resolver_id = IP::get_singleton()->resolve_hostname_queue_item(p_host);
+ ERR_FAIL_COND_V(_resolver_id == IP::RESOLVER_INVALID_ID, ERR_INVALID_PARAMETER);
+ // Check if it was found in cache.
+ IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
+ if (ip_status == IP::RESOLVER_STATUS_DONE) {
+ _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
+ IP::get_singleton()->erase_resolve_item(_resolver_id);
+ _resolver_id = IP::RESOLVER_INVALID_ID;
+ }
}
- Error err = ERR_BUG; // Should be at least one entry.
- while (ip_candidates.size() > 0) {
- err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port);
+ // We assume OK while hostname resultion is pending.
+ Error err = _resolver_id != IP::RESOLVER_INVALID_ID ? OK : FAILED;
+ while (_ip_candidates.size()) {
+ err = _tcp->connect_to_host(_ip_candidates.pop_front(), p_port);
if (err == OK) {
break;
}
@@ -200,8 +202,11 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
}
_key = WSLPeer::generate_key();
- // TODO custom extra headers (allow overriding this too?)
String request = "GET " + p_path + " HTTP/1.1\r\n";
+ String port = "";
+ if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
+ port = ":" + itos(p_port);
+ }
request += "Host: " + p_host + port + "\r\n";
request += "Upgrade: websocket\r\n";
request += "Connection: Upgrade\r\n";
@@ -231,6 +236,30 @@ int WSLClient::get_max_packet_size() const {
}
void WSLClient::poll() {
+ if (_resolver_id != IP::RESOLVER_INVALID_ID) {
+ IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
+ if (ip_status == IP::RESOLVER_STATUS_WAITING) {
+ return;
+ }
+ // Anything else is either a candidate or a failure.
+ Error err = FAILED;
+ if (ip_status == IP::RESOLVER_STATUS_DONE) {
+ _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
+ while (_ip_candidates.size()) {
+ err = _tcp->connect_to_host(_ip_candidates.pop_front(), _port);
+ if (err == OK) {
+ break;
+ }
+ }
+ }
+ IP::get_singleton()->erase_resolve_item(_resolver_id);
+ _resolver_id = IP::RESOLVER_INVALID_ID;
+ if (err != OK) {
+ disconnect_from_host();
+ _on_error();
+ return;
+ }
+ }
if (_peer->is_connected_to_host()) {
_peer->poll();
if (!_peer->is_connected_to_host()) {
@@ -251,7 +280,7 @@ void WSLClient::poll() {
_on_error();
break;
case StreamPeerTCP::STATUS_CONNECTED: {
- ip_candidates.clear();
+ _ip_candidates.clear();
Ref<StreamPeerSSL> ssl;
if (_use_ssl) {
if (_connection == _tcp) {
@@ -282,9 +311,9 @@ void WSLClient::poll() {
_do_handshake();
} break;
case StreamPeerTCP::STATUS_ERROR:
- while (ip_candidates.size() > 0) {
+ while (_ip_candidates.size() > 0) {
_tcp->disconnect_from_host();
- if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) {
+ if (_tcp->connect_to_host(_ip_candidates.pop_front(), _port) == OK) {
return;
}
}
@@ -307,7 +336,7 @@ MultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const {
return CONNECTION_CONNECTED;
}
- if (_tcp->is_connected_to_host()) {
+ if (_tcp->is_connected_to_host() || _resolver_id != IP::RESOLVER_INVALID_ID) {
return CONNECTION_CONNECTING;
}
@@ -330,7 +359,12 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
memset(_resp_buf, 0, sizeof(_resp_buf));
_resp_pos = 0;
- ip_candidates.clear();
+ if (_resolver_id != IP::RESOLVER_INVALID_ID) {
+ IP::get_singleton()->erase_resolve_item(_resolver_id);
+ _resolver_id = IP::RESOLVER_INVALID_ID;
+ }
+
+ _ip_candidates.clear();
}
IPAddress WSLClient::get_connected_host() const {
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
index 4839d7ab9b..d846e6be00 100644
--- a/modules/websocket/wsl_client.h
+++ b/modules/websocket/wsl_client.h
@@ -63,10 +63,11 @@ private:
String _key;
String _host;
- int _port;
- Array ip_candidates;
+ uint16_t _port;
+ Array _ip_candidates;
Vector<String> _protocols;
bool _use_ssl = false;
+ IP::ResolverID _resolver_id = IP::RESOLVER_INVALID_ID;
void _do_handshake();
bool _verify_headers(String &r_protocol);
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
index 31175c5779..eadd7ef7ac 100644
--- a/modules/websocket/wsl_server.cpp
+++ b/modules/websocket/wsl_server.cpp
@@ -58,17 +58,17 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, St
headers[name] = value;
}
}
-#define _WSL_CHECK(NAME, VALUE) \
+#define WSL_CHECK(NAME, VALUE) \
ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
"Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
-#define _WSL_CHECK_EX(NAME) \
+#define WSL_CHECK_EX(NAME) \
ERR_FAIL_COND_V_MSG(!headers.has(NAME), false, "Missing header '" + String(NAME) + "'.");
- _WSL_CHECK("upgrade", "websocket");
- _WSL_CHECK("sec-websocket-version", "13");
- _WSL_CHECK_EX("sec-websocket-key");
- _WSL_CHECK_EX("connection");
-#undef _WSL_CHECK_EX
-#undef _WSL_CHECK
+ WSL_CHECK("upgrade", "websocket");
+ WSL_CHECK("sec-websocket-version", "13");
+ WSL_CHECK_EX("sec-websocket-key");
+ WSL_CHECK_EX("connection");
+#undef WSL_CHECK_EX
+#undef WSL_CHECK
key = headers["sec-websocket-key"];
if (headers.has("sec-websocket-protocol")) {
Vector<String> protos = headers["sec-websocket-protocol"].split(",");
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index 8eb0d8ff90..86b857f72c 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -385,7 +385,7 @@ CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p
return eye;
}
-Vector<BlitToScreen> WebXRInterfaceJS::commit_views(RID p_render_target, const Rect2 &p_screen_rect) {
+Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
Vector<BlitToScreen> blit_to_screen;
if (!initialized) {
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
index 8eddfbe484..31858194f6 100644
--- a/modules/webxr/webxr_interface_js.h
+++ b/modules/webxr/webxr_interface_js.h
@@ -88,7 +88,7 @@ public:
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
- virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override;
+ virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override;