summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp7
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp8
-rw-r--r--editor/code_editor.cpp7
-rw-r--r--editor/editor_help.cpp12
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp126
-rw-r--r--modules/gdscript/editor/gdscript_docgen.h10
-rw-r--r--modules/openxr/doc_classes/OpenXRInterface.xml36
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.cpp24
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.h2
-rw-r--r--modules/openxr/openxr_interface.cpp97
-rw-r--r--modules/openxr/openxr_interface.h13
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp10
-rw-r--r--platform/macos/crash_handler_macos.mm10
-rw-r--r--scene/gui/text_edit.cpp14
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp14
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.h4
-rw-r--r--tests/scene/test_text_edit.h7
18 files changed, 354 insertions, 49 deletions
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 2894f4164f..fcd3af8d62 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -1420,6 +1420,13 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) {
glEnableVertexAttribArray(5);
glVertexAttribIPointer(5, 4, GL_UNSIGNED_INT, instance_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(instance_color_offset * sizeof(float)));
glVertexAttribDivisor(5, 1);
+ } else {
+ // Set all default instance color and custom data values to 1.0 or 0.0 using a compressed format.
+ uint16_t zero = Math::make_half_float(0.0f);
+ uint16_t one = Math::make_half_float(1.0f);
+ GLuint default_color = (uint32_t(one) << 16) | one;
+ GLuint default_custom = (uint32_t(zero) << 16) | zero;
+ glVertexAttribI4ui(5, default_color, default_color, default_custom, default_custom);
}
}
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 1f8e9180e3..22bb772e4f 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -2962,7 +2962,15 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
glEnableVertexAttribArray(15);
glVertexAttribIPointer(15, 4, GL_UNSIGNED_INT, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(color_custom_offset * sizeof(float)));
glVertexAttribDivisor(15, 1);
+ } else {
+ // Set all default instance color and custom data values to 1.0 or 0.0 using a compressed format.
+ uint16_t zero = Math::make_half_float(0.0f);
+ uint16_t one = Math::make_half_float(1.0f);
+ GLuint default_color = (uint32_t(one) << 16) | one;
+ GLuint default_custom = (uint32_t(zero) << 16) | zero;
+ glVertexAttribI4ui(15, default_color, default_color, default_custom, default_custom);
}
+
if (use_index_buffer) {
glDrawElementsInstanced(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0, inst->instance_count);
} else {
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 56e405bfcf..64467bc254 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -368,6 +368,9 @@ void FindReplaceBar::_update_results_count() {
int col_pos = 0;
+ bool searched_start_is_symbol = is_symbol(searched[0]);
+ bool searched_end_is_symbol = is_symbol(searched[searched.length() - 1]);
+
while (true) {
col_pos = is_case_sensitive() ? line_text.find(searched, col_pos) : line_text.findn(searched, col_pos);
@@ -376,11 +379,11 @@ void FindReplaceBar::_update_results_count() {
}
if (is_whole_words()) {
- if (col_pos > 0 && !is_symbol(line_text[col_pos - 1])) {
+ if (!searched_start_is_symbol && col_pos > 0 && !is_symbol(line_text[col_pos - 1])) {
col_pos += searched.length();
continue;
}
- if (col_pos + searched.length() < line_text.length() && !is_symbol(line_text[col_pos + searched.length()])) {
+ if (!searched_end_is_symbol && col_pos + searched.length() < line_text.length() && !is_symbol(line_text[col_pos + searched.length()])) {
col_pos += searched.length();
continue;
}
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 5d07ba7568..6004591bb2 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -471,7 +471,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
class_desc->add_text(" = ");
class_desc->pop();
class_desc->push_color(theme_cache.value_color);
- _add_text(_fix_constant(p_method.arguments[j].default_value));
+ class_desc->add_text(_fix_constant(p_method.arguments[j].default_value));
class_desc->pop();
}
@@ -1073,7 +1073,7 @@ void EditorHelp::_update_doc() {
class_desc->pop();
class_desc->push_color(theme_cache.value_color);
- _add_text(_fix_constant(cd.properties[i].default_value));
+ class_desc->add_text(_fix_constant(cd.properties[i].default_value));
class_desc->pop();
class_desc->push_color(theme_cache.symbol_color);
@@ -1249,7 +1249,7 @@ void EditorHelp::_update_doc() {
class_desc->add_text(" [" + TTR("default:") + " ");
class_desc->pop();
class_desc->push_color(theme_cache.value_color);
- _add_text(_fix_constant(cd.theme_properties[i].default_value));
+ class_desc->add_text(_fix_constant(cd.theme_properties[i].default_value));
class_desc->pop();
class_desc->push_color(theme_cache.symbol_color);
class_desc->add_text("]");
@@ -1465,7 +1465,7 @@ void EditorHelp::_update_doc() {
class_desc->add_text(" = ");
class_desc->pop();
class_desc->push_color(theme_cache.value_color);
- _add_text(_fix_constant(enum_list[i].value));
+ class_desc->add_text(_fix_constant(enum_list[i].value));
class_desc->pop();
if (enum_list[i].is_deprecated) {
@@ -1541,7 +1541,7 @@ void EditorHelp::_update_doc() {
class_desc->add_text(" = ");
class_desc->pop();
class_desc->push_color(theme_cache.value_color);
- _add_text(_fix_constant(constants[i].value));
+ class_desc->add_text(_fix_constant(constants[i].value));
class_desc->pop();
if (constants[i].is_deprecated) {
@@ -1722,7 +1722,7 @@ void EditorHelp::_update_doc() {
class_desc->pop(); // color
class_desc->push_color(theme_cache.value_color);
- _add_text(_fix_constant(cd.properties[i].default_value));
+ class_desc->add_text(_fix_constant(cd.properties[i].default_value));
class_desc->pop(); // color
class_desc->push_color(theme_cache.symbol_color);
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp
index 0b440274c0..cffd661261 100644
--- a/modules/gdscript/editor/gdscript_docgen.cpp
+++ b/modules/gdscript/editor/gdscript_docgen.cpp
@@ -32,14 +32,11 @@
#include "../gdscript.h"
-using GDP = GDScriptParser;
-using GDType = GDP::DataType;
-
-static String _get_script_path(const String &p_path) {
+String GDScriptDocGen::_get_script_path(const String &p_path) {
return p_path.trim_prefix("res://").quote();
}
-static String _get_class_name(const GDP::ClassNode &p_class) {
+String GDScriptDocGen::_get_class_name(const GDP::ClassNode &p_class) {
const GDP::ClassNode *curr_class = &p_class;
if (!curr_class->identifier) { // All inner classes have an identifier, so this is the outer class.
return _get_script_path(curr_class->fqcn);
@@ -56,7 +53,7 @@ static String _get_class_name(const GDP::ClassNode &p_class) {
return full_name;
}
-static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return = false) {
+void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return) {
if (!p_gdtype.is_hard_type()) {
r_type = "Variant";
return;
@@ -82,9 +79,18 @@ static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String
r_type = Variant::get_type_name(p_gdtype.builtin_type);
return;
case GDType::NATIVE:
+ if (p_gdtype.is_meta_type) {
+ //r_type = GDScriptNativeClass::get_class_static();
+ r_type = "Object"; // "GDScriptNativeClass" refers to a blank page.
+ return;
+ }
r_type = p_gdtype.native_type;
return;
case GDType::SCRIPT:
+ if (p_gdtype.is_meta_type) {
+ r_type = p_gdtype.script_type.is_valid() ? p_gdtype.script_type->get_class() : Script::get_class_static();
+ return;
+ }
if (p_gdtype.script_type.is_valid()) {
if (p_gdtype.script_type->get_global_name() != StringName()) {
r_type = p_gdtype.script_type->get_global_name();
@@ -102,9 +108,17 @@ static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String
r_type = "Object";
return;
case GDType::CLASS:
+ if (p_gdtype.is_meta_type) {
+ r_type = GDScript::get_class_static();
+ return;
+ }
r_type = _get_class_name(*p_gdtype.class_type);
return;
case GDType::ENUM:
+ if (p_gdtype.is_meta_type) {
+ r_type = "Dictionary";
+ return;
+ }
r_type = "int";
r_enum = String(p_gdtype.native_type).replace("::", ".");
if (r_enum.begins_with("res://")) {
@@ -123,6 +137,90 @@ static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String
}
}
+String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_recursion_level) {
+ constexpr int MAX_RECURSION_LEVEL = 2;
+
+ switch (p_variant.get_type()) {
+ case Variant::STRING:
+ return String(p_variant).c_escape().quote();
+ case Variant::OBJECT:
+ return "<Object>";
+ case Variant::DICTIONARY: {
+ const Dictionary dict = p_variant;
+
+ if (dict.is_empty()) {
+ return "{}";
+ }
+
+ if (p_recursion_level > MAX_RECURSION_LEVEL) {
+ return "{...}";
+ }
+
+ List<Variant> keys;
+ dict.get_key_list(&keys);
+ keys.sort();
+
+ String data;
+ for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+ if (E->prev()) {
+ data += ", ";
+ }
+ data += _docvalue_from_variant(E->get(), p_recursion_level + 1) + ": " + _docvalue_from_variant(dict[E->get()], p_recursion_level + 1);
+ }
+
+ return "{" + data + "}";
+ } break;
+ case Variant::ARRAY: {
+ const Array array = p_variant;
+ String result;
+
+ if (array.get_typed_builtin() != Variant::NIL) {
+ result += "Array[";
+
+ Ref<Script> script = array.get_typed_script();
+ if (script.is_valid()) {
+ if (script->get_global_name() != StringName()) {
+ result += script->get_global_name();
+ } else if (!script->get_path().get_file().is_empty()) {
+ result += script->get_path().get_file();
+ } else {
+ result += array.get_typed_class_name();
+ }
+ } else if (array.get_typed_class_name() != StringName()) {
+ result += array.get_typed_class_name();
+ } else {
+ result += Variant::get_type_name((Variant::Type)array.get_typed_builtin());
+ }
+
+ result += "](";
+ }
+
+ if (array.is_empty()) {
+ result += "[]";
+ } else if (p_recursion_level > MAX_RECURSION_LEVEL) {
+ result += "[...]";
+ } else {
+ result += "[";
+ for (int i = 0; i < array.size(); i++) {
+ if (i > 0) {
+ result += ", ";
+ }
+ result += _docvalue_from_variant(array[i], p_recursion_level + 1);
+ }
+ result += "]";
+ }
+
+ if (array.get_typed_builtin() != Variant::NIL) {
+ result += ")";
+ }
+
+ return result;
+ } break;
+ default:
+ return p_variant.get_construct_string();
+ }
+}
+
void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_class) {
p_script->_clear_doc();
@@ -183,7 +281,10 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[const_name] = m_const->start_line;
DocData::ConstantDoc const_doc;
- DocData::constant_doc_from_variant(const_doc, const_name, m_const->initializer->reduced_value, m_const->doc_data.description);
+ const_doc.name = const_name;
+ const_doc.value = _docvalue_from_variant(m_const->initializer->reduced_value);
+ const_doc.is_value_valid = true;
+ const_doc.description = m_const->doc_data.description;
const_doc.is_deprecated = m_const->doc_data.is_deprecated;
const_doc.is_experimental = m_const->doc_data.is_experimental;
doc.constants.push_back(const_doc);
@@ -217,7 +318,7 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
_doctype_from_gdtype(p->get_datatype(), arg_doc.type, arg_doc.enumeration);
if (p->initializer != nullptr) {
if (p->initializer->is_constant) {
- arg_doc.default_value = p->initializer->reduced_value.get_construct_string().replace("\n", "\\n");
+ arg_doc.default_value = _docvalue_from_variant(p->initializer->reduced_value);
} else {
arg_doc.default_value = "<unknown>";
}
@@ -286,7 +387,7 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
if (m_var->initializer) {
if (m_var->initializer->is_constant) {
- prop_doc.default_value = m_var->initializer->reduced_value.get_construct_string().replace("\n", "\\n");
+ prop_doc.default_value = _docvalue_from_variant(m_var->initializer->reduced_value);
} else {
prop_doc.default_value = "<unknown>";
}
@@ -312,7 +413,7 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
for (const GDP::EnumNode::Value &val : m_enum->values) {
DocData::ConstantDoc const_doc;
const_doc.name = val.identifier->name;
- const_doc.value = String(Variant(val.value));
+ const_doc.value = _docvalue_from_variant(val.value);
const_doc.is_value_valid = true;
const_doc.enumeration = name;
const_doc.description = val.doc_data.description;
@@ -331,8 +432,11 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[name] = m_enum_val.identifier->start_line;
DocData::ConstantDoc const_doc;
- DocData::constant_doc_from_variant(const_doc, name, m_enum_val.value, m_enum_val.doc_data.description);
+ const_doc.name = name;
+ const_doc.value = _docvalue_from_variant(m_enum_val.value);
+ const_doc.is_value_valid = true;
const_doc.enumeration = "@unnamed_enums";
+ const_doc.description = m_enum_val.doc_data.description;
const_doc.is_deprecated = m_enum_val.doc_data.is_deprecated;
const_doc.is_experimental = m_enum_val.doc_data.is_experimental;
doc.constants.push_back(const_doc);
diff --git a/modules/gdscript/editor/gdscript_docgen.h b/modules/gdscript/editor/gdscript_docgen.h
index 3357fb680c..a326c02c5f 100644
--- a/modules/gdscript/editor/gdscript_docgen.h
+++ b/modules/gdscript/editor/gdscript_docgen.h
@@ -36,8 +36,16 @@
#include "core/doc_data.h"
class GDScriptDocGen {
+ using GDP = GDScriptParser;
+ using GDType = GDP::DataType;
+
+ static String _get_script_path(const String &p_path);
+ static String _get_class_name(const GDP::ClassNode &p_class);
+ static void _doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return = false);
+ static String _docvalue_from_variant(const Variant &p_variant, int p_recursion_level = 1);
+
public:
- static void generate_docs(GDScript *p_script, const GDScriptParser::ClassNode *p_class);
+ static void generate_docs(GDScript *p_script, const GDP::ClassNode *p_class);
};
#endif // GDSCRIPT_DOCGEN_H
diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index c7c666dc2f..131246fe57 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -31,6 +31,14 @@
If handtracking is enabled, returns the angular velocity of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D]!
</description>
</method>
+ <method name="get_hand_joint_flags" qualifiers="const">
+ <return type="int" enum="OpenXRInterface.HandJointFlags" is_bitfield="true" />
+ <param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
+ <param index="1" name="joint" type="int" enum="OpenXRInterface.HandJoints" />
+ <description>
+ If handtracking is enabled, returns flags that inform us of the validity of the tracking data.
+ </description>
+ </method>
<method name="get_hand_joint_linear_velocity" qualifiers="const">
<return type="Vector3" />
<param index="0" name="hand" type="int" enum="OpenXRInterface.Hand" />
@@ -91,6 +99,13 @@
[b]Note:[/b] This feature is only available on the compatibility renderer and currently only available on some stand alone headsets. For Vulkan set [member Viewport.vrs_mode] to [code]VRS_XR[/code] on desktop.
</description>
</method>
+ <method name="is_hand_tracking_supported">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if OpenXRs hand tracking is supported and enabled.
+ [b]Note:[/b] This only returns a valid value after OpenXR has been initialized.
+ </description>
+ </method>
<method name="set_action_set_active">
<return type="void" />
<param index="0" name="name" type="String" />
@@ -246,5 +261,26 @@
<constant name="HAND_JOINT_MAX" value="26" enum="HandJoints">
Maximum value for the hand joint enum.
</constant>
+ <constant name="HAND_JOINT_NONE" value="0" enum="HandJointFlags" is_bitfield="true">
+ No flags are set.
+ </constant>
+ <constant name="HAND_JOINT_ORIENTATION_VALID" value="1" enum="HandJointFlags" is_bitfield="true">
+ If set, the orientation data is valid, otherwise, the orientation data is unreliable and should not be used.
+ </constant>
+ <constant name="HAND_JOINT_ORIENTATION_TRACKED" value="2" enum="HandJointFlags" is_bitfield="true">
+ If set, the orientation data comes from tracking data, otherwise, the orientation data contains predicted data.
+ </constant>
+ <constant name="HAND_JOINT_POSITION_VALID" value="4" enum="HandJointFlags" is_bitfield="true">
+ If set, the positional data is valid, otherwise, the positional data is unreliable and should not be used.
+ </constant>
+ <constant name="HAND_JOINT_POSITION_TRACKED" value="8" enum="HandJointFlags" is_bitfield="true">
+ If set, the positional data comes from tracking data, otherwise, the positional data contains predicted data.
+ </constant>
+ <constant name="HAND_JOINT_LINEAR_VELOCITY_VALID" value="16" enum="HandJointFlags" is_bitfield="true">
+ If set, our linear velocity data is valid, otherwise, the linear velocity data is unreliable and should not be used.
+ </constant>
+ <constant name="HAND_JOINT_ANGULAR_VELOCITY_VALID" value="32" enum="HandJointFlags" is_bitfield="true">
+ If set, our angular velocity data is valid, otherwise, the angular velocity data is unreliable and should not be used.
+ </constant>
</constants>
</class>
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
index caf97ca2e0..0d667b56c6 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
@@ -245,6 +245,18 @@ void OpenXRHandTrackingExtension::set_motion_range(HandTrackedHands p_hand, XrHa
hand_trackers[p_hand].motion_range = p_motion_range;
}
+XrSpaceLocationFlags OpenXRHandTrackingExtension::get_hand_joint_location_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XrSpaceLocationFlags(0));
+ ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, XrSpaceLocationFlags(0));
+
+ if (!hand_trackers[p_hand].is_initialized) {
+ return XrSpaceLocationFlags(0);
+ }
+
+ const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
+ return location.locationFlags;
+}
+
Quaternion OpenXRHandTrackingExtension::get_hand_joint_rotation(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Quaternion());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Quaternion());
@@ -280,6 +292,18 @@ float OpenXRHandTrackingExtension::get_hand_joint_radius(HandTrackedHands p_hand
return hand_trackers[p_hand].joint_locations[p_joint].radius;
}
+XrSpaceVelocityFlags OpenXRHandTrackingExtension::get_hand_joint_velocity_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XrSpaceVelocityFlags(0));
+ ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, XrSpaceVelocityFlags(0));
+
+ if (!hand_trackers[p_hand].is_initialized) {
+ return XrSpaceVelocityFlags(0);
+ }
+
+ const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
+ return velocity.velocityFlags;
+}
+
Vector3 OpenXRHandTrackingExtension::get_hand_joint_linear_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.h b/modules/openxr/extensions/openxr_hand_tracking_extension.h
index 5ca0ff60d3..f9b26fd604 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.h
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.h
@@ -77,10 +77,12 @@ public:
XrHandJointsMotionRangeEXT get_motion_range(HandTrackedHands p_hand) const;
void set_motion_range(HandTrackedHands p_hand, XrHandJointsMotionRangeEXT p_motion_range);
+ XrSpaceLocationFlags get_hand_joint_location_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Quaternion get_hand_joint_rotation(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Vector3 get_hand_joint_position(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
float get_hand_joint_radius(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
+ XrSpaceVelocityFlags get_hand_joint_velocity_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Vector3 get_hand_joint_linear_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
Vector3 get_hand_joint_angular_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const;
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index d0b01c5771..8ce76a5fad 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -77,6 +77,8 @@ void OpenXRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_motion_range", "hand", "motion_range"), &OpenXRInterface::set_motion_range);
ClassDB::bind_method(D_METHOD("get_motion_range", "hand"), &OpenXRInterface::get_motion_range);
+ ClassDB::bind_method(D_METHOD("get_hand_joint_flags", "hand", "joint"), &OpenXRInterface::get_hand_joint_flags);
+
ClassDB::bind_method(D_METHOD("get_hand_joint_rotation", "hand", "joint"), &OpenXRInterface::get_hand_joint_rotation);
ClassDB::bind_method(D_METHOD("get_hand_joint_position", "hand", "joint"), &OpenXRInterface::get_hand_joint_position);
ClassDB::bind_method(D_METHOD("get_hand_joint_radius", "hand", "joint"), &OpenXRInterface::get_hand_joint_radius);
@@ -84,6 +86,9 @@ void OpenXRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_hand_joint_linear_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_linear_velocity);
ClassDB::bind_method(D_METHOD("get_hand_joint_angular_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_angular_velocity);
+ ClassDB::bind_method(D_METHOD("is_hand_tracking_supported"), &OpenXRInterface::is_hand_tracking_supported);
+ ClassDB::bind_method(D_METHOD("is_eye_gaze_interaction_supported"), &OpenXRInterface::is_eye_gaze_interaction_supported);
+
BIND_ENUM_CONSTANT(HAND_LEFT);
BIND_ENUM_CONSTANT(HAND_RIGHT);
BIND_ENUM_CONSTANT(HAND_MAX);
@@ -120,7 +125,13 @@ void OpenXRInterface::_bind_methods() {
BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_TIP);
BIND_ENUM_CONSTANT(HAND_JOINT_MAX);
- ClassDB::bind_method(D_METHOD("is_eye_gaze_interaction_supported"), &OpenXRInterface::is_eye_gaze_interaction_supported);
+ BIND_BITFIELD_FLAG(HAND_JOINT_NONE);
+ BIND_BITFIELD_FLAG(HAND_JOINT_ORIENTATION_VALID);
+ BIND_BITFIELD_FLAG(HAND_JOINT_ORIENTATION_TRACKED);
+ BIND_BITFIELD_FLAG(HAND_JOINT_POSITION_VALID);
+ BIND_BITFIELD_FLAG(HAND_JOINT_POSITION_TRACKED);
+ BIND_BITFIELD_FLAG(HAND_JOINT_LINEAR_VELOCITY_VALID);
+ BIND_BITFIELD_FLAG(HAND_JOINT_ANGULAR_VELOCITY_VALID);
}
StringName OpenXRInterface::get_name() const {
@@ -709,6 +720,21 @@ Array OpenXRInterface::get_available_display_refresh_rates() const {
}
}
+bool OpenXRInterface::is_hand_tracking_supported() {
+ if (openxr_api == nullptr) {
+ return false;
+ } else if (!openxr_api->is_initialized()) {
+ return false;
+ } else {
+ OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+ if (hand_tracking_ext == nullptr) {
+ return false;
+ } else {
+ return hand_tracking_ext->get_active();
+ }
+ }
+}
+
bool OpenXRInterface::is_eye_gaze_interaction_supported() {
if (openxr_api == nullptr) {
return false;
@@ -912,15 +938,39 @@ void OpenXRInterface::handle_hand_tracking(const String &p_path, OpenXRHandTrack
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
OpenXRInterface::Tracker *tracker = find_tracker(p_path);
if (tracker && tracker->positional_tracker.is_valid()) {
- // TODO add in confidence! Requires PR #82715
+ XrSpaceLocationFlags location_flags = hand_tracking_ext->get_hand_joint_location_flags(p_hand, XR_HAND_JOINT_PALM_EXT);
- Transform3D transform;
- transform.basis = Basis(hand_tracking_ext->get_hand_joint_rotation(p_hand, XR_HAND_JOINT_PALM_EXT));
- transform.origin = hand_tracking_ext->get_hand_joint_position(p_hand, XR_HAND_JOINT_PALM_EXT);
- Vector3 linear_velocity = hand_tracking_ext->get_hand_joint_linear_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);
- Vector3 angular_velocity = hand_tracking_ext->get_hand_joint_angular_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);
+ if (location_flags & (XR_SPACE_LOCATION_ORIENTATION_VALID_BIT + XR_SPACE_LOCATION_POSITION_VALID_BIT)) {
+ static const XrSpaceLocationFlags all_location_flags = XR_SPACE_LOCATION_ORIENTATION_VALID_BIT + XR_SPACE_LOCATION_POSITION_VALID_BIT + XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT + XR_SPACE_LOCATION_POSITION_TRACKED_BIT;
+ XRPose::TrackingConfidence confidence = XRPose::XR_TRACKING_CONFIDENCE_LOW;
+ Transform3D transform;
+ Vector3 linear_velocity;
+ Vector3 angular_velocity;
+
+ if ((location_flags & all_location_flags) == all_location_flags) {
+ // All flags set? confidence is high!
+ confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
+ }
- tracker->positional_tracker->set_pose("skeleton", transform, linear_velocity, angular_velocity, XRPose::XR_TRACKING_CONFIDENCE_HIGH);
+ if (location_flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
+ transform.basis = Basis(hand_tracking_ext->get_hand_joint_rotation(p_hand, XR_HAND_JOINT_PALM_EXT));
+ }
+ if (location_flags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
+ transform.origin = hand_tracking_ext->get_hand_joint_position(p_hand, XR_HAND_JOINT_PALM_EXT);
+ }
+
+ XrSpaceVelocityFlags velocity_flags = hand_tracking_ext->get_hand_joint_location_flags(p_hand, XR_HAND_JOINT_PALM_EXT);
+ if (velocity_flags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
+ linear_velocity = hand_tracking_ext->get_hand_joint_linear_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);
+ }
+ if (velocity_flags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
+ angular_velocity = hand_tracking_ext->get_hand_joint_angular_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);
+ }
+
+ tracker->positional_tracker->set_pose("skeleton", transform, linear_velocity, angular_velocity, confidence);
+ } else {
+ tracker->positional_tracker->invalidate_pose("skeleton");
+ }
}
}
}
@@ -1183,6 +1233,37 @@ OpenXRInterface::HandMotionRange OpenXRInterface::get_motion_range(const Hand p_
return HAND_MOTION_RANGE_MAX;
}
+BitField<OpenXRInterface::HandJointFlags> OpenXRInterface::get_hand_joint_flags(Hand p_hand, HandJoints p_joint) const {
+ BitField<OpenXRInterface::HandJointFlags> bits;
+
+ OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+ if (hand_tracking_ext && hand_tracking_ext->get_active()) {
+ XrSpaceLocationFlags location_flags = hand_tracking_ext->get_hand_joint_location_flags(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));
+ if (location_flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
+ bits.set_flag(HAND_JOINT_ORIENTATION_VALID);
+ }
+ if (location_flags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) {
+ bits.set_flag(HAND_JOINT_ORIENTATION_TRACKED);
+ }
+ if (location_flags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
+ bits.set_flag(HAND_JOINT_POSITION_VALID);
+ }
+ if (location_flags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {
+ bits.set_flag(HAND_JOINT_POSITION_TRACKED);
+ }
+
+ XrSpaceVelocityFlags velocity_flags = hand_tracking_ext->get_hand_joint_velocity_flags(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));
+ if (velocity_flags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
+ bits.set_flag(HAND_JOINT_LINEAR_VELOCITY_VALID);
+ }
+ if (velocity_flags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
+ bits.set_flag(HAND_JOINT_ANGULAR_VELOCITY_VALID);
+ }
+ }
+
+ return bits;
+}
+
Quaternion OpenXRInterface::get_hand_joint_rotation(Hand p_hand, HandJoints p_joint) const {
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index 8e24c8dce9..51ef4ea228 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -111,6 +111,7 @@ public:
virtual PackedStringArray get_suggested_tracker_names() const override;
virtual TrackingStatus get_tracking_status() const override;
+ bool is_hand_tracking_supported();
bool is_eye_gaze_interaction_supported();
bool initialize_on_startup() const;
@@ -222,6 +223,17 @@ public:
HAND_JOINT_MAX = 26,
};
+ enum HandJointFlags {
+ HAND_JOINT_NONE = 0,
+ HAND_JOINT_ORIENTATION_VALID = 1,
+ HAND_JOINT_ORIENTATION_TRACKED = 2,
+ HAND_JOINT_POSITION_VALID = 4,
+ HAND_JOINT_POSITION_TRACKED = 8,
+ HAND_JOINT_LINEAR_VELOCITY_VALID = 16,
+ HAND_JOINT_ANGULAR_VELOCITY_VALID = 32,
+ };
+
+ BitField<HandJointFlags> get_hand_joint_flags(Hand p_hand, HandJoints p_joint) const;
Quaternion get_hand_joint_rotation(Hand p_hand, HandJoints p_joint) const;
Vector3 get_hand_joint_position(Hand p_hand, HandJoints p_joint) const;
float get_hand_joint_radius(Hand p_hand, HandJoints p_joint) const;
@@ -236,5 +248,6 @@ public:
VARIANT_ENUM_CAST(OpenXRInterface::Hand)
VARIANT_ENUM_CAST(OpenXRInterface::HandMotionRange)
VARIANT_ENUM_CAST(OpenXRInterface::HandJoints)
+VARIANT_BITFIELD_CAST(OpenXRInterface::HandJointFlags)
#endif // OPENXR_INTERFACE_H
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index 3a245460b4..fd4bcf92be 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -49,6 +49,10 @@
#include <stdlib.h>
static void handle_crash(int sig) {
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+
if (OS::get_singleton() == nullptr) {
abort();
}
@@ -156,9 +160,9 @@ void CrashHandler::disable() {
}
#ifdef CRASH_HANDLER_ENABLED
- signal(SIGSEGV, nullptr);
- signal(SIGFPE, nullptr);
- signal(SIGILL, nullptr);
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
#endif
disabled = true;
diff --git a/platform/macos/crash_handler_macos.mm b/platform/macos/crash_handler_macos.mm
index 7f9a88121e..7c0cab0210 100644
--- a/platform/macos/crash_handler_macos.mm
+++ b/platform/macos/crash_handler_macos.mm
@@ -72,6 +72,10 @@ static uint64_t load_address() {
}
static void handle_crash(int sig) {
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+
if (OS::get_singleton() == nullptr) {
abort();
}
@@ -186,9 +190,9 @@ void CrashHandler::disable() {
}
#ifdef CRASH_HANDLER_ENABLED
- signal(SIGSEGV, nullptr);
- signal(SIGFPE, nullptr);
- signal(SIGILL, nullptr);
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGFPE, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
#endif
disabled = true;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 1642d19b67..3d426b8bf3 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -4112,6 +4112,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro
int line = p_from_line;
int pos = -1;
+ bool key_start_is_symbol = is_symbol(p_key[0]);
+ bool key_end_is_symbol = is_symbol(p_key[p_key.length() - 1]);
+
for (int i = 0; i < text.size() + 1; i++) {
if (line < 0) {
line = text.size() - 1;
@@ -4175,9 +4178,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro
if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) {
// Validate for whole words.
- if (pos > 0 && !is_symbol(text_line[pos - 1])) {
+ if (!key_start_is_symbol && pos > 0 && !is_symbol(text_line[pos - 1])) {
is_match = false;
- } else if (pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) {
+ } else if (!key_end_is_symbol && pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) {
is_match = false;
}
}
@@ -7022,6 +7025,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
p_from_column = 0;
}
+ bool key_start_is_symbol = is_symbol(p_key[0]);
+ bool key_end_is_symbol = is_symbol(p_key[p_key.length() - 1]);
+
while (col == -1 && p_from_column <= p_search.length()) {
if (p_search_flags & SEARCH_MATCH_CASE) {
col = p_search.find(p_key, p_from_column);
@@ -7038,9 +7044,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) {
p_from_column = col;
- if (col > 0 && !is_symbol(p_search[col - 1])) {
+ if (!key_start_is_symbol && col > 0 && !is_symbol(p_search[col - 1])) {
col = -1;
- } else if ((col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) {
+ } else if (!key_end_is_symbol && (col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) {
col = -1;
}
}
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index fd65b739ab..3bd35c53cc 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -931,11 +931,11 @@ void MaterialStorage::MaterialData::update_textures(const HashMap<StringName, Va
roughness_detect_texture = tex;
roughness_channel = RS::TextureDetectRoughnessChannel(p_texture_uniforms[i].hint - ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_R);
}
+#endif // TOOLS_ENABLED
if (tex->render_target) {
tex->render_target->was_used = true;
render_target_cache.push_back(tex->render_target);
}
-#endif
}
if (rd_texture.is_null()) {
rd_texture = texture_storage->texture_rd_get_default(TextureStorage::DEFAULT_RD_TEXTURE_WHITE);
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 9c7bcbb54f..cb7eb3cd59 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -1061,7 +1061,7 @@ void MeshStorage::update_mesh_instances() {
RD::get_singleton()->compute_list_end();
}
-void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis) {
+void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis, uint32_t p_current_buffer, uint32_t p_previous_buffer) {
Vector<RD::VertexAttribute> attributes;
Vector<RID> buffers;
@@ -1134,7 +1134,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
if (mis) {
- buffer = mis->vertex_buffer[mis->current_buffer];
+ buffer = mis->vertex_buffer[p_current_buffer];
} else {
buffer = s->vertex_buffer;
}
@@ -1146,7 +1146,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
stride += sizeof(uint16_t) * 2;
if (mis) {
- buffer = mis->vertex_buffer[mis->current_buffer];
+ buffer = mis->vertex_buffer[p_current_buffer];
} else {
buffer = s->vertex_buffer;
}
@@ -1157,7 +1157,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
stride += sizeof(uint16_t) * 2;
if (mis) {
- buffer = mis->vertex_buffer[mis->current_buffer];
+ buffer = mis->vertex_buffer[p_current_buffer];
} else {
buffer = s->vertex_buffer;
}
@@ -1241,7 +1241,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
if (int(vd.location) != i) {
if (mis && buffer != mesh_default_rd_buffers[i]) {
- buffer = mis->vertex_buffer[mis->previous_buffer];
+ buffer = mis->vertex_buffer[p_previous_buffer];
}
attributes.push_back(vd);
@@ -1267,8 +1267,8 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
v.input_mask = p_input_mask;
- v.current_buffer = mis ? mis->current_buffer : 0;
- v.previous_buffer = mis ? mis->previous_buffer : 0;
+ v.current_buffer = p_current_buffer;
+ v.previous_buffer = p_previous_buffer;
v.input_motion_vectors = p_input_motion_vectors;
v.vertex_format = RD::get_singleton()->vertex_format_create(attributes);
v.vertex_array = RD::get_singleton()->vertex_array_create(s->vertex_count, v.vertex_format, buffers);
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
index 86e81d56cc..1e1db9c47d 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
@@ -190,7 +190,7 @@ private:
weight_update_list(this), array_update_list(this) {}
};
- void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis = nullptr);
+ void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis = nullptr, uint32_t p_current_buffer = 0, uint32_t p_previous_buffer = 0);
void _mesh_instance_clear(MeshInstance *mi);
void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface);
@@ -523,7 +523,7 @@ public:
mis->version_count++;
mis->versions = (Mesh::Surface::Version *)memrealloc(mis->versions, sizeof(Mesh::Surface::Version) * mis->version_count);
- _mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, p_input_motion_vectors, mis);
+ _mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, p_input_motion_vectors, mis, current_buffer, previous_buffer);
r_vertex_format = mis->versions[version].vertex_format;
r_vertex_array_rd = mis->versions[version].vertex_array;
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index 7e9b472af1..840a04becf 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -3193,7 +3193,7 @@ TEST_CASE("[SceneTree][TextEdit] search") {
TextEdit *text_edit = memnew(TextEdit);
SceneTree::get_singleton()->get_root()->add_child(text_edit);
- text_edit->set_text("hay needle, hay\nHAY NEEDLE, HAY");
+ text_edit->set_text("hay needle, hay\nHAY NEEDLE, HAY\nwordword.word.word");
int length = text_edit->get_line(1).length();
CHECK(text_edit->search("test", 0, 0, 0) == Point2i(-1, -1));
@@ -3225,6 +3225,11 @@ TEST_CASE("[SceneTree][TextEdit] search") {
CHECK(text_edit->search("need", TextEdit::SEARCH_WHOLE_WORDS | TextEdit::SEARCH_MATCH_CASE, 0, 0) == Point2i(-1, -1));
CHECK(text_edit->search("need", TextEdit::SEARCH_WHOLE_WORDS | TextEdit::SEARCH_MATCH_CASE | TextEdit::SEARCH_BACKWARDS, 0, 0) == Point2i(-1, -1));
+ CHECK(text_edit->search("word", TextEdit::SEARCH_WHOLE_WORDS, 2, 0) == Point2i(9, 2));
+ CHECK(text_edit->search("word", TextEdit::SEARCH_WHOLE_WORDS, 2, 10) == Point2i(14, 2));
+ CHECK(text_edit->search(".word", TextEdit::SEARCH_WHOLE_WORDS, 2, 0) == Point2i(8, 2));
+ CHECK(text_edit->search("word.", TextEdit::SEARCH_WHOLE_WORDS, 2, 0) == Point2i(9, 2));
+
ERR_PRINT_OFF;
CHECK(text_edit->search("", 0, 0, 0) == Point2i(-1, -1));
CHECK(text_edit->search("needle", 0, -1, 0) == Point2i(-1, -1));