summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/core/input/test_input_event.h4
-rw-r--r--tests/core/io/test_resource.h5
-rw-r--r--tests/core/math/test_aabb.h61
-rw-r--r--tests/core/math/test_geometry_2d.h94
-rw-r--r--tests/core/math/test_math_funcs.h3
-rw-r--r--tests/core/math/test_transform_3d.h29
-rw-r--r--tests/core/object/test_class_db.h30
-rw-r--r--tests/core/object/test_object.h7
-rw-r--r--tests/core/os/test_os.h4
-rw-r--r--tests/core/string/test_string.h309
-rw-r--r--tests/core/templates/test_command_queue.h44
-rw-r--r--tests/core/templates/test_local_vector.h11
-rw-r--r--tests/core/threads/test_worker_thread_pool.h67
-rw-r--r--tests/core/variant/test_array.h4
-rw-r--r--tests/core/variant/test_dictionary.h2
-rw-r--r--tests/core/variant/test_variant.h4
-rwxr-xr-x[-rw-r--r--]tests/create_test.py0
-rw-r--r--tests/display_server_mock.h14
-rw-r--r--tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl1
-rw-r--r--tests/python_build/test_gles3_builder.py2
-rw-r--r--tests/python_build/test_glsl_builder.py2
-rw-r--r--tests/scene/test_animation.h2
-rw-r--r--tests/scene/test_code_edit.h2016
-rw-r--r--tests/scene/test_curve_2d.h45
-rw-r--r--tests/scene/test_curve_3d.h20
-rw-r--r--tests/scene/test_graph_node.h59
-rw-r--r--tests/scene/test_instance_placeholder.h532
-rw-r--r--tests/scene/test_navigation_region_2d.h2
-rw-r--r--tests/scene/test_navigation_region_3d.h13
-rw-r--r--tests/scene/test_sprite_frames.h5
-rw-r--r--tests/scene/test_text_edit.h5041
-rw-r--r--tests/scene/test_timer.h217
-rw-r--r--tests/scene/test_viewport.h174
-rw-r--r--tests/servers/test_navigation_server_3d.h3
-rw-r--r--tests/servers/test_text_server.h91
-rw-r--r--tests/test_macros.h77
-rw-r--r--tests/test_main.cpp20
-rw-r--r--tests/test_validate_testing.h7
38 files changed, 7598 insertions, 1423 deletions
diff --git a/tests/core/input/test_input_event.h b/tests/core/input/test_input_event.h
index 6b4b80486c..c6a287e47b 100644
--- a/tests/core/input/test_input_event.h
+++ b/tests/core/input/test_input_event.h
@@ -43,7 +43,7 @@ TEST_CASE("[InputEvent] Signal is emitted when device is changed") {
Ref<InputEventKey> input_event;
input_event.instantiate();
- SIGNAL_WATCH(*input_event, SNAME("changed"));
+ SIGNAL_WATCH(*input_event, CoreStringName(changed));
Array args1;
Array empty_args;
empty_args.push_back(args1);
@@ -53,7 +53,7 @@ TEST_CASE("[InputEvent] Signal is emitted when device is changed") {
SIGNAL_CHECK("changed", empty_args);
CHECK(input_event->get_device() == 1);
- SIGNAL_UNWATCH(*input_event, SNAME("changed"));
+ SIGNAL_UNWATCH(*input_event, CoreStringName(changed));
}
TEST_CASE("[InputEvent] Test accumulate") {
diff --git a/tests/core/io/test_resource.h b/tests/core/io/test_resource.h
index 9ddb51220b..a83e7f88ba 100644
--- a/tests/core/io/test_resource.h
+++ b/tests/core/io/test_resource.h
@@ -38,6 +38,8 @@
#include "thirdparty/doctest/doctest.h"
+#include "tests/test_macros.h"
+
namespace TestResource {
TEST_CASE("[Resource] Duplication") {
@@ -124,9 +126,12 @@ TEST_CASE("[Resource] Breaking circular references on save") {
const String save_path_binary = OS::get_singleton()->get_cache_path().path_join("resource.res");
const String save_path_text = OS::get_singleton()->get_cache_path().path_join("resource.tres");
ResourceSaver::save(resource_a, save_path_binary);
+ // Suppress expected errors caused by the resources above being uncached.
+ ERR_PRINT_OFF;
ResourceSaver::save(resource_a, save_path_text);
const Ref<Resource> &loaded_resource_a_binary = ResourceLoader::load(save_path_binary);
+ ERR_PRINT_ON;
CHECK_MESSAGE(
loaded_resource_a_binary->get_name() == "A",
"The loaded resource name should be equal to the expected value.");
diff --git a/tests/core/math/test_aabb.h b/tests/core/math/test_aabb.h
index b9f84cca24..dbc62bc248 100644
--- a/tests/core/math/test_aabb.h
+++ b/tests/core/math/test_aabb.h
@@ -204,6 +204,67 @@ TEST_CASE("[AABB] Intersection") {
CHECK_MESSAGE(
!aabb_big.intersects_segment(Vector3(0, 300, 0), Vector3(0, 300, 0)),
"intersects_segment() should return the expected result with segment of length 0.");
+ CHECK_MESSAGE( // Simple ray intersection test.
+ aabb_big.intersects_ray(Vector3(-100, 3, 0), Vector3(1, 0, 0)),
+ "intersects_ray() should return true when ray points directly to AABB from outside.");
+ CHECK_MESSAGE( // Ray parallel to an edge.
+ !aabb_big.intersects_ray(Vector3(10, 10, 0), Vector3(0, 1, 0)),
+ "intersects_ray() should return false for ray parallel and outside of AABB.");
+ CHECK_MESSAGE( // Ray origin inside aabb.
+ aabb_big.intersects_ray(Vector3(1, 1, 1), Vector3(0, 1, 0)),
+ "intersects_ray() should return true for rays originating inside the AABB.");
+ CHECK_MESSAGE( // Ray pointing away from aabb.
+ !aabb_big.intersects_ray(Vector3(-10, 0, 0), Vector3(-1, 0, 0)),
+ "intersects_ray() should return false when ray points away from AABB.");
+ CHECK_MESSAGE( // Ray along a diagonal of aabb.
+ aabb_big.intersects_ray(Vector3(0, 0, 0), Vector3(1, 1, 1)),
+ "intersects_ray() should return true for rays along the AABB diagonal.");
+ CHECK_MESSAGE( // Ray originating at aabb edge.
+ aabb_big.intersects_ray(aabb_big.position, Vector3(-1, 0, 0)),
+ "intersects_ray() should return true for rays starting on AABB's edge.");
+ CHECK_MESSAGE( // Ray with zero direction inside.
+ aabb_big.intersects_ray(Vector3(-1, 3, -2), Vector3(0, 0, 0)),
+ "intersects_ray() should return true because its inside.");
+ CHECK_MESSAGE( // Ray with zero direction outside.
+ !aabb_big.intersects_ray(Vector3(-1000, 3, -2), Vector3(0, 0, 0)),
+ "intersects_ray() should return false for being outside.");
+
+ // Finding ray intersections.
+ const AABB aabb_simple = AABB(Vector3(), Vector3(1, 1, 1));
+ bool inside = false;
+ Vector3 intersection_point;
+ Vector3 intersection_normal;
+
+ // Borders.
+ aabb_simple.find_intersects_ray(Vector3(0.5, 0, 0.5), Vector3(0, 1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() border intersection point incorrect.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, -1, 0)), "find_intersects_ray() border intersection normal incorrect.");
+ aabb_simple.find_intersects_ray(Vector3(0.5, 1, 0.5), Vector3(0, -1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 1, 0.5)), "find_intersects_ray() border intersection point incorrect.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, 1, 0)), "find_intersects_ray() border intersection normal incorrect.");
+
+ // Inside.
+ aabb_simple.find_intersects_ray(Vector3(0.5, 0.1, 0.5), Vector3(0, 1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == true, "find_intersects_ray() should return inside when inside.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() inside backtracking intersection point incorrect.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, -1, 0)), "find_intersects_ray() inside intersection normal incorrect.");
+
+ // Zero sized AABB.
+ const AABB aabb_zero = AABB(Vector3(), Vector3(1, 0, 1));
+ aabb_zero.find_intersects_ray(Vector3(0.5, 0, 0.5), Vector3(0, 1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders of zero sized AABB.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() border intersection point incorrect for zero sized AABB.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, -1, 0)), "find_intersects_ray() border intersection normal incorrect for zero sized AABB.");
+ aabb_zero.find_intersects_ray(Vector3(0.5, 0, 0.5), Vector3(0, -1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders of zero sized AABB.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() border intersection point incorrect for zero sized AABB.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, 1, 0)), "find_intersects_ray() border intersection normal incorrect for zero sized AABB.");
+ aabb_zero.find_intersects_ray(Vector3(0.5, -1, 0.5), Vector3(0, 1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders of zero sized AABB.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() border intersection point incorrect for zero sized AABB.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, -1, 0)), "find_intersects_ray() border intersection normal incorrect for zero sized AABB.");
}
TEST_CASE("[AABB] Merging") {
diff --git a/tests/core/math/test_geometry_2d.h b/tests/core/math/test_geometry_2d.h
index c3ff4f3ec9..a4bb6dfca0 100644
--- a/tests/core/math/test_geometry_2d.h
+++ b/tests/core/math/test_geometry_2d.h
@@ -282,41 +282,67 @@ TEST_CASE("[Geometry2D] Closest point to uncapped segment") {
TEST_CASE("[Geometry2D] Closest points between segments") {
Vector2 c1, c2;
- Geometry2D::get_closest_points_between_segments(Vector2(2, 2), Vector2(3, 3), Vector2(4, 4), Vector2(4, 5), c1, c2);
- CHECK(c1.is_equal_approx(Vector2(3, 3)));
- CHECK(c2.is_equal_approx(Vector2(4, 4)));
+ // Basis Path Testing suite
+ SUBCASE("[Geometry2D] Both segments degenerate to a point") {
+ Geometry2D::get_closest_points_between_segments(Vector2(0, 0), Vector2(0, 0), Vector2(0, 0), Vector2(0, 0), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(0, 0)));
+ CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(0, 1), Vector2(-2, -1), Vector2(0, 0), Vector2(2, -2), c1, c2);
- CHECK(c1.is_equal_approx(Vector2(-0.5, 0.5)));
- CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ SUBCASE("[Geometry2D] Closest point on second segment trajectory is above [0,1]") {
+ Geometry2D::get_closest_points_between_segments(Vector2(50, -25), Vector2(50, -10), Vector2(-50, 10), Vector2(-40, 10), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(50, -10)));
+ CHECK(c2.is_equal_approx(Vector2(-40, 10)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), c1, c2);
- CHECK(c1.is_equal_approx(Vector2(0, 0)));
- CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ SUBCASE("[Geometry2D] Parallel segments") {
+ Geometry2D::get_closest_points_between_segments(Vector2(2, 1), Vector2(4, 3), Vector2(2, 3), Vector2(4, 5), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(3, 2)));
+ CHECK(c2.is_equal_approx(Vector2(2, 3)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(-3, 4), Vector2(-3, 4), Vector2(-4, 3), Vector2(-2, 3), c1, c2);
- CHECK_MESSAGE(
- c1.is_equal_approx(Vector2(-3, 4)),
- "1st line segment is only a point, this point should be the closest point to the 2nd line segment.");
- CHECK_MESSAGE(
- c2.is_equal_approx(Vector2(-3, 3)),
- "1st line segment is only a point, this should not matter when determining the closest point on the 2nd line segment.");
+ SUBCASE("[Geometry2D] Closest point on second segment trajectory is within [0,1]") {
+ Geometry2D::get_closest_points_between_segments(Vector2(2, 4), Vector2(2, 3), Vector2(1, 1), Vector2(4, 4), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(2, 3)));
+ CHECK(c2.is_equal_approx(Vector2(2.5, 2.5)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(-4, 3), Vector2(-2, 3), Vector2(-3, 4), Vector2(-3, 4), c1, c2);
- CHECK_MESSAGE(
- c1.is_equal_approx(Vector2(-3, 3)),
- "2nd line segment is only a point, this should not matter when determining the closest point on the 1st line segment.");
- CHECK_MESSAGE(
- c2.is_equal_approx(Vector2(-3, 4)),
- "2nd line segment is only a point, this point should be the closest point to the 1st line segment.");
+ SUBCASE("[Geometry2D] Closest point on second segment trajectory is below [0,1]") {
+ Geometry2D::get_closest_points_between_segments(Vector2(-20, -20), Vector2(-10, -40), Vector2(10, 25), Vector2(25, 40), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(-20, -20)));
+ CHECK(c2.is_equal_approx(Vector2(10, 25)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(5, -4), Vector2(5, -4), Vector2(-2, 1), Vector2(-2, 1), c1, c2);
- CHECK_MESSAGE(
- c1.is_equal_approx(Vector2(5, -4)),
- "Both line segments are only a point. On the 1st line segment, that point should be the closest point to the 2nd line segment.");
- CHECK_MESSAGE(
- c2.is_equal_approx(Vector2(-2, 1)),
- "Both line segments are only a point. On the 2nd line segment, that point should be the closest point to the 1st line segment.");
+ SUBCASE("[Geometry2D] Second segment degenerates to a point") {
+ Geometry2D::get_closest_points_between_segments(Vector2(1, 2), Vector2(2, 1), Vector2(3, 3), Vector2(3, 3), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(1.5, 1.5)));
+ CHECK(c2.is_equal_approx(Vector2(3, 3)));
+ }
+
+ SUBCASE("[Geometry2D] First segment degenerates to a point") {
+ Geometry2D::get_closest_points_between_segments(Vector2(1, 1), Vector2(1, 1), Vector2(2, 2), Vector2(4, 4), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(1, 1)));
+ CHECK(c2.is_equal_approx(Vector2(2, 2)));
+ }
+ // End Basis Path Testing suite
+
+ SUBCASE("[Geometry2D] Segments are equal vectors") {
+ Geometry2D::get_closest_points_between_segments(Vector2(2, 2), Vector2(3, 3), Vector2(4, 4), Vector2(4, 5), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(3, 3)));
+ CHECK(c2.is_equal_approx(Vector2(4, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Standard case") {
+ Geometry2D::get_closest_points_between_segments(Vector2(0, 1), Vector2(-2, -1), Vector2(0, 0), Vector2(2, -2), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(-0.5, 0.5)));
+ CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ }
+
+ SUBCASE("[Geometry2D] Segments intersect") {
+ Geometry2D::get_closest_points_between_segments(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(0, 0)));
+ CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ }
}
TEST_CASE("[Geometry2D] Make atlas") {
@@ -685,12 +711,12 @@ TEST_CASE("[Geometry2D] Clip polyline with polygon") {
r = Geometry2D::clip_polyline_with_polygon(l, p);
REQUIRE_MESSAGE(r.size() == 2, "There should be 2 resulting clipped lines.");
REQUIRE_MESSAGE(r[0].size() == 3, "The resulting clipped line should have 3 vertices.");
- CHECK(r[0][0].is_equal_approx(Vector2(160, 320)));
+ CHECK(r[0][0].is_equal_approx(Vector2(121.412682, 225.038757)));
CHECK(r[0][1].is_equal_approx(Vector2(122, 250)));
- CHECK(r[0][2].is_equal_approx(Vector2(121.412682, 225.038757)));
+ CHECK(r[0][2].is_equal_approx(Vector2(160, 320)));
REQUIRE_MESSAGE(r[1].size() == 2, "The resulting clipped line should have 2 vertices.");
- CHECK(r[1][0].is_equal_approx(Vector2(53.07737, 116.143021)));
- CHECK(r[1][1].is_equal_approx(Vector2(55, 70)));
+ CHECK(r[1][0].is_equal_approx(Vector2(55, 70)));
+ CHECK(r[1][1].is_equal_approx(Vector2(53.07737, 116.143021)));
}
}
diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h
index 0a9d9c97d9..68540e4d61 100644
--- a/tests/core/math/test_math_funcs.h
+++ b/tests/core/math/test_math_funcs.h
@@ -381,6 +381,9 @@ TEST_CASE_TEMPLATE("[Math] remap", T, float, double) {
CHECK(Math::remap((T)-100.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)0.0));
CHECK(Math::remap((T)-200.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)-1000.0));
CHECK(Math::remap((T)-250.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)-1500.0));
+
+ // Note: undefined behavior can happen when `p_istart == p_istop`. We don't bother testing this as it will
+ // vary between hardware and compilers properly implementing IEEE 754.
}
TEST_CASE_TEMPLATE("[Math] angle_difference", T, float, double) {
diff --git a/tests/core/math/test_transform_3d.h b/tests/core/math/test_transform_3d.h
index 551b20fe74..fba0fcb280 100644
--- a/tests/core/math/test_transform_3d.h
+++ b/tests/core/math/test_transform_3d.h
@@ -107,6 +107,35 @@ TEST_CASE("[Transform3D] Finite number checks") {
"Transform3D with two components infinite should not be finite.");
}
+TEST_CASE("[Transform3D] Rotate around global origin") {
+ // Start with the default orientation, but not centered on the origin.
+ // Rotating should rotate both our basis and the origin.
+ Transform3D transform = Transform3D();
+ transform.origin = Vector3(0, 0, 1);
+
+ Transform3D expected = Transform3D();
+ expected.origin = Vector3(0, 0, -1);
+ expected.basis[0] = Vector3(-1, 0, 0);
+ expected.basis[2] = Vector3(0, 0, -1);
+
+ const Transform3D rotated_transform = transform.rotated(Vector3(0, 1, 0), Math_PI);
+ CHECK_MESSAGE(rotated_transform.is_equal_approx(expected), "The rotated transform should have a new orientation and basis.");
+}
+
+TEST_CASE("[Transform3D] Rotate in-place (local rotation)") {
+ // Start with the default orientation.
+ // Local rotation should not change the origin, only the basis.
+ Transform3D transform = Transform3D();
+ transform.origin = Vector3(1, 2, 3);
+
+ Transform3D expected = Transform3D();
+ expected.origin = Vector3(1, 2, 3);
+ expected.basis[0] = Vector3(-1, 0, 0);
+ expected.basis[2] = Vector3(0, 0, -1);
+
+ const Transform3D rotated_transform = Transform3D(transform.rotated_local(Vector3(0, 1, 0), Math_PI));
+ CHECK_MESSAGE(rotated_transform.is_equal_approx(expected), "The rotated transform should have a new orientation but still be based on the same origin.");
+}
} // namespace TestTransform3D
#endif // TEST_TRANSFORM_3D_H
diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h
index 5f7de11c71..381d759e5b 100644
--- a/tests/core/object/test_class_db.h
+++ b/tests/core/object/test_class_db.h
@@ -141,7 +141,7 @@ struct NamesCache {
StringName vector3_type = StaticCString::create("Vector3");
// Object not included as it must be checked for all derived classes
- static constexpr int nullable_types_count = 17;
+ static constexpr int nullable_types_count = 18;
StringName nullable_types[nullable_types_count] = {
string_type,
string_name_type,
@@ -161,6 +161,7 @@ struct NamesCache {
StaticCString::create(_STR(PackedVector2Array)),
StaticCString::create(_STR(PackedVector3Array)),
StaticCString::create(_STR(PackedColorArray)),
+ StaticCString::create(_STR(PackedVector4Array)),
};
bool is_nullable_type(const StringName &p_type) const {
@@ -194,12 +195,12 @@ struct Context {
}
bool has_type(const TypeReference &p_type_ref) const {
- if (builtin_types.find(p_type_ref.name) >= 0) {
+ if (builtin_types.has(p_type_ref.name)) {
return true;
}
if (p_type_ref.is_enum) {
- if (enum_types.find(p_type_ref.name) >= 0) {
+ if (enum_types.has(p_type_ref.name)) {
return true;
}
@@ -258,6 +259,7 @@ bool arg_default_value_is_assignable_to_type(const Context &p_context, const Var
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
case Variant::PACKED_COLOR_ARRAY:
+ case Variant::PACKED_VECTOR4_ARRAY:
case Variant::CALLABLE:
case Variant::SIGNAL:
return p_arg_type.name == Variant::get_type_name(p_val.get_type());
@@ -353,7 +355,7 @@ void validate_property(const Context &p_context, const ExposedClass &p_class, co
const ArgumentData &idx_arg = getter->arguments.front()->get();
if (idx_arg.type.name != p_context.names_cache.int_type) {
// If not an int, it can be an enum
- TEST_COND(p_context.enum_types.find(idx_arg.type.name) < 0,
+ TEST_COND(!p_context.enum_types.has(idx_arg.type.name),
"Invalid type '", idx_arg.type.name, "' for index argument of property getter: '", p_class.name, ".", String(p_prop.name), "'.");
}
}
@@ -365,7 +367,7 @@ void validate_property(const Context &p_context, const ExposedClass &p_class, co
if (idx_arg.type.name != p_context.names_cache.int_type) {
// Assume the index parameter is an enum
// If not an int, it can be an enum
- TEST_COND(p_context.enum_types.find(idx_arg.type.name) < 0,
+ TEST_COND(!p_context.enum_types.has(idx_arg.type.name),
"Invalid type '", idx_arg.type.name, "' for index argument of property setter: '", p_class.name, ".", String(p_prop.name), "'.");
}
}
@@ -548,8 +550,6 @@ void add_exposed_classes(Context &r_context) {
for (const MethodInfo &E : method_list) {
const MethodInfo &method_info = E;
- int argc = method_info.arguments.size();
-
if (method_info.name.is_empty()) {
continue;
}
@@ -611,8 +611,9 @@ void add_exposed_classes(Context &r_context) {
method.return_type.name = Variant::get_type_name(return_info.type);
}
- for (int i = 0; i < argc; i++) {
- PropertyInfo arg_info = method_info.arguments[i];
+ int i = 0;
+ for (List<PropertyInfo>::ConstIterator itr = method_info.arguments.begin(); itr != method_info.arguments.end(); ++itr, ++i) {
+ const PropertyInfo &arg_info = *itr;
String orig_arg_name = arg_info.name;
@@ -684,10 +685,9 @@ void add_exposed_classes(Context &r_context) {
TEST_FAIL_COND(!String(signal.name).is_valid_identifier(),
"Signal name is not a valid identifier: '", exposed_class.name, ".", signal.name, "'.");
- int argc = method_info.arguments.size();
-
- for (int i = 0; i < argc; i++) {
- PropertyInfo arg_info = method_info.arguments[i];
+ int i = 0;
+ for (List<PropertyInfo>::ConstIterator itr = method_info.arguments.begin(); itr != method_info.arguments.end(); ++itr, ++i) {
+ const PropertyInfo &arg_info = *itr;
String orig_arg_name = arg_info.name;
@@ -736,7 +736,7 @@ void add_exposed_classes(Context &r_context) {
for (const StringName &E : K.value.constants) {
const StringName &constant_name = E;
- TEST_FAIL_COND(String(constant_name).find("::") != -1,
+ TEST_FAIL_COND(String(constant_name).contains("::"),
"Enum constant contains '::', check bindings to remove the scope: '",
String(class_name), ".", String(enum_.name), ".", String(constant_name), "'.");
int64_t *value = class_info->constant_map.getptr(constant_name);
@@ -758,7 +758,7 @@ void add_exposed_classes(Context &r_context) {
for (const String &E : constants) {
const String &constant_name = E;
- TEST_FAIL_COND(constant_name.find("::") != -1,
+ TEST_FAIL_COND(constant_name.contains("::"),
"Constant contains '::', check bindings to remove the scope: '",
String(class_name), ".", constant_name, "'.");
int64_t *value = class_info->constant_map.getptr(StringName(E));
diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h
index 3a3013a102..57bc65328a 100644
--- a/tests/core/object/test_object.h
+++ b/tests/core/object/test_object.h
@@ -31,7 +31,6 @@
#ifndef TEST_OBJECT_H
#define TEST_OBJECT_H
-#include "core/core_string_names.h"
#include "core/object/class_db.h"
#include "core/object/object.h"
#include "core/object/script_language.h"
@@ -142,7 +141,7 @@ TEST_CASE("[Object] Core getters") {
inheritance_list.size() == 1,
"The inheritance list should consist of Object only");
CHECK_MESSAGE(
- inheritance_list[0] == "Object",
+ inheritance_list.front()->get() == "Object",
"The inheritance list should consist of Object only");
}
@@ -251,7 +250,7 @@ TEST_CASE("[Object] Script property setter") {
Variant script;
bool valid = false;
- object.set(CoreStringNames::get_singleton()->_script, script, &valid);
+ object.set(CoreStringName(script), script, &valid);
CHECK(valid);
CHECK_MESSAGE(
object.get_script() == script,
@@ -264,7 +263,7 @@ TEST_CASE("[Object] Script property getter") {
object.set_script(script);
bool valid = false;
- const Variant &actual_value = object.get(CoreStringNames::get_singleton()->_script, &valid);
+ const Variant &actual_value = object.get(CoreStringName(script), &valid);
CHECK(valid);
CHECK_MESSAGE(
actual_value == script,
diff --git a/tests/core/os/test_os.h b/tests/core/os/test_os.h
index 63f8b18238..6ee0ff82e7 100644
--- a/tests/core/os/test_os.h
+++ b/tests/core/os/test_os.h
@@ -79,8 +79,8 @@ TEST_CASE("[OS] Non-UTF-8 environment variables") {
TEST_CASE("[OS] Command line arguments") {
List<String> arguments = OS::get_singleton()->get_cmdline_args();
bool found = false;
- for (int i = 0; i < arguments.size(); i++) {
- if (arguments[i] == "--test") {
+ for (const String &arg : arguments) {
+ if (arg == "--test") {
found = true;
break;
}
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 64f03e5879..cf57183a02 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -297,6 +297,19 @@ TEST_CASE("[String] Contains") {
CHECK(!s.contains(String("\\char_test.tscn")));
}
+TEST_CASE("[String] Contains case insensitive") {
+ String s = "C:\\Godot\\project\\string_test.tscn";
+ CHECK(s.containsn("Godot"));
+ CHECK(s.containsn("godot"));
+ CHECK(s.containsn(String("Project\\string_test")));
+ CHECK(s.containsn(String("\\string_Test.tscn")));
+
+ CHECK(!s.containsn("Godoh"));
+ CHECK(!s.containsn("godoh"));
+ CHECK(!s.containsn(String("project\\string test")));
+ CHECK(!s.containsn(String("\\char_test.tscn")));
+}
+
TEST_CASE("[String] Test chr") {
CHECK(String::chr('H') == "H");
CHECK(String::chr(0x3012)[0] == 0x3012);
@@ -360,18 +373,37 @@ TEST_CASE("[String] Substr") {
TEST_CASE("[String] Find") {
String s = "Pretty Woman Woman";
- CHECK(s.find("tty") == 3);
- CHECK(s.find("Wo", 9) == 13);
- CHECK(s.find("Revenge of the Monster Truck") == -1);
- CHECK(s.rfind("man") == 15);
-}
-
-TEST_CASE("[String] Find no case") {
+ MULTICHECK_STRING_EQ(s, find, "tty", 3);
+ MULTICHECK_STRING_EQ(s, find, "Revenge of the Monster Truck", -1);
+ MULTICHECK_STRING_INT_EQ(s, find, "Wo", 9, 13);
+ MULTICHECK_STRING_EQ(s, find, "", -1);
+ MULTICHECK_STRING_EQ(s, find, "Pretty Woman Woman", 0);
+ MULTICHECK_STRING_EQ(s, find, "WOMAN", -1);
+ MULTICHECK_STRING_INT_EQ(s, find, "", 9, -1);
+
+ MULTICHECK_STRING_EQ(s, rfind, "", -1);
+ MULTICHECK_STRING_EQ(s, rfind, "foo", -1);
+ MULTICHECK_STRING_EQ(s, rfind, "Pretty Woman Woman", 0);
+ MULTICHECK_STRING_EQ(s, rfind, "man", 15);
+ MULTICHECK_STRING_EQ(s, rfind, "WOMAN", -1);
+ MULTICHECK_STRING_INT_EQ(s, rfind, "", 15, -1);
+}
+
+TEST_CASE("[String] Find case insensitive") {
String s = "Pretty Whale Whale";
- CHECK(s.findn("WHA") == 7);
- CHECK(s.findn("WHA", 9) == 13);
- CHECK(s.findn("Revenge of the Monster SawFish") == -1);
- CHECK(s.rfindn("WHA") == 13);
+ MULTICHECK_STRING_EQ(s, findn, "WHA", 7);
+ MULTICHECK_STRING_INT_EQ(s, findn, "WHA", 9, 13);
+ MULTICHECK_STRING_EQ(s, findn, "Revenge of the Monster SawFish", -1);
+ MULTICHECK_STRING_EQ(s, findn, "", -1);
+ MULTICHECK_STRING_EQ(s, findn, "wha", 7);
+ MULTICHECK_STRING_EQ(s, findn, "Wha", 7);
+ MULTICHECK_STRING_INT_EQ(s, findn, "", 3, -1);
+
+ MULTICHECK_STRING_EQ(s, rfindn, "WHA", 13);
+ MULTICHECK_STRING_EQ(s, rfindn, "", -1);
+ MULTICHECK_STRING_EQ(s, rfindn, "wha", 13);
+ MULTICHECK_STRING_EQ(s, rfindn, "Wha", 13);
+ MULTICHECK_STRING_INT_EQ(s, rfindn, "", 13, -1);
}
TEST_CASE("[String] Find MK") {
@@ -392,11 +424,9 @@ TEST_CASE("[String] Find MK") {
TEST_CASE("[String] Find and replace") {
String s = "Happy Birthday, Anna!";
- s = s.replace("Birthday", "Halloween");
- CHECK(s == "Happy Halloween, Anna!");
-
- s = s.replace_first("H", "W");
- CHECK(s == "Wappy Halloween, Anna!");
+ MULTICHECK_STRING_STRING_EQ(s, replace, "Birthday", "Halloween", "Happy Halloween, Anna!");
+ MULTICHECK_STRING_STRING_EQ(s, replace_first, "y", "Y", "HappY Birthday, Anna!");
+ MULTICHECK_STRING_STRING_EQ(s, replacen, "Y", "Y", "HappY BirthdaY, Anna!");
}
TEST_CASE("[String] Insertion") {
@@ -557,51 +587,76 @@ TEST_CASE("[String] String to float") {
TEST_CASE("[String] Slicing") {
String s = "Mars,Jupiter,Saturn,Uranus";
-
const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
- for (int i = 0; i < s.get_slice_count(","); i++) {
- CHECK(s.get_slice(",", i) == slices[i]);
- }
+ MULTICHECK_GET_SLICE(s, ",", slices);
+}
+
+TEST_CASE("[String] Begins with") {
+ // Test cases for true:
+ MULTICHECK_STRING_EQ(String("res://foobar"), begins_with, "res://", true);
+ MULTICHECK_STRING_EQ(String("abc"), begins_with, "abc", true);
+ MULTICHECK_STRING_EQ(String("abc"), begins_with, "", true);
+ MULTICHECK_STRING_EQ(String(""), begins_with, "", true);
+
+ // Test cases for false:
+ MULTICHECK_STRING_EQ(String("res"), begins_with, "res://", false);
+ MULTICHECK_STRING_EQ(String("abcdef"), begins_with, "foo", false);
+ MULTICHECK_STRING_EQ(String("abc"), begins_with, "ax", false);
+ MULTICHECK_STRING_EQ(String(""), begins_with, "abc", false);
+
+ // Test "const char *" version also with nullptr.
+ String s("foo");
+ bool state = s.begins_with(nullptr) == false;
+ CHECK_MESSAGE(state, "nullptr check failed");
+
+ String empty("");
+ state = empty.begins_with(nullptr) == false;
+ CHECK_MESSAGE(state, "nullptr check with empty string failed");
+}
+
+TEST_CASE("[String] Ends with") {
+ // Test cases for true:
+ MULTICHECK_STRING_EQ(String("res://foobar"), ends_with, "foobar", true);
+ MULTICHECK_STRING_EQ(String("abc"), ends_with, "abc", true);
+ MULTICHECK_STRING_EQ(String("abc"), ends_with, "", true);
+ MULTICHECK_STRING_EQ(String(""), ends_with, "", true);
+
+ // Test cases for false:
+ MULTICHECK_STRING_EQ(String("res"), ends_with, "res://", false);
+ MULTICHECK_STRING_EQ(String("abcdef"), ends_with, "foo", false);
+ MULTICHECK_STRING_EQ(String("abc"), ends_with, "ax", false);
+ MULTICHECK_STRING_EQ(String(""), ends_with, "abc", false);
+
+ // Test "const char *" version also with nullptr.
+ String s("foo");
+ bool state = s.ends_with(nullptr) == false;
+ CHECK_MESSAGE(state, "nullptr check failed");
+
+ String empty("");
+ state = empty.ends_with(nullptr) == false;
+ CHECK_MESSAGE(state, "nullptr check with empty string failed");
}
TEST_CASE("[String] Splitting") {
String s = "Mars,Jupiter,Saturn,Uranus";
- Vector<String> l;
-
const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" };
- const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" };
- const char *slices_3[4] = { "t", "e", "s", "t" };
+ MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3);
- l = s.split(",", true, 2);
- CHECK(l.size() == 3);
- for (int i = 0; i < l.size(); i++) {
- CHECK(l[i] == slices_l[i]);
- }
-
- l = s.rsplit(",", true, 2);
- CHECK(l.size() == 3);
- for (int i = 0; i < l.size(); i++) {
- CHECK(l[i] == slices_r[i]);
- }
+ const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" };
+ MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3);
s = "test";
- l = s.split();
- CHECK(l.size() == 4);
- for (int i = 0; i < l.size(); i++) {
- CHECK(l[i] == slices_3[i]);
- }
+ const char *slices_3[4] = { "t", "e", "s", "t" };
+ MULTICHECK_SPLIT(s, split, "", true, 0, slices_3, 4);
s = "";
- l = s.split();
- CHECK(l.size() == 1);
- CHECK(l[0] == "");
-
- l = s.split("", false);
- CHECK(l.size() == 0);
+ const char *slices_4[1] = { "" };
+ MULTICHECK_SPLIT(s, split, "", true, 0, slices_4, 1);
+ MULTICHECK_SPLIT(s, split, "", false, 0, slices_4, 0);
s = "Mars Jupiter Saturn Uranus";
const char *slices_s[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
- l = s.split_spaces();
+ Vector<String> l = s.split_spaces();
for (int i = 0; i < l.size(); i++) {
CHECK(l[i] == slices_s[i]);
}
@@ -644,69 +699,6 @@ TEST_CASE("[String] Splitting") {
}
}
-struct test_27_data {
- char const *data;
- char const *part;
- bool expected;
-};
-
-TEST_CASE("[String] Begins with") {
- test_27_data tc[] = {
- // Test cases for true:
- { "res://foobar", "res://", true },
- { "abc", "abc", true },
- { "abc", "", true },
- { "", "", true },
- // Test cases for false:
- { "res", "res://", false },
- { "abcdef", "foo", false },
- { "abc", "ax", false },
- { "", "abc", false }
- };
- size_t count = sizeof(tc) / sizeof(tc[0]);
- bool state = true;
- for (size_t i = 0; i < count; ++i) {
- String s = tc[i].data;
- state = s.begins_with(tc[i].part) == tc[i].expected;
- CHECK_MESSAGE(state, "first check failed at: ", i);
-
- String sb = tc[i].part;
- state = s.begins_with(sb) == tc[i].expected;
- CHECK_MESSAGE(state, "second check failed at: ", i);
- }
-
- // Test "const char *" version also with nullptr.
- String s("foo");
- state = s.begins_with(nullptr) == false;
- CHECK_MESSAGE(state, "nullptr check failed");
-
- String empty("");
- state = empty.begins_with(nullptr) == false;
- CHECK_MESSAGE(state, "nullptr check with empty string failed");
-}
-
-TEST_CASE("[String] Ends with") {
- test_27_data tc[] = {
- // test cases for true:
- { "res://foobar", "foobar", true },
- { "abc", "abc", true },
- { "abc", "", true },
- { "", "", true },
- // test cases for false:
- { "res", "res://", false },
- { "", "abc", false },
- { "abcdef", "foo", false },
- { "abc", "xc", false }
- };
- size_t count = sizeof(tc) / sizeof(tc[0]);
- for (size_t i = 0; i < count; ++i) {
- String s = tc[i].data;
- String sb = tc[i].part;
- bool state = s.ends_with(sb) == tc[i].expected;
- CHECK_MESSAGE(state, "check failed at: ", i);
- }
-}
-
TEST_CASE("[String] format") {
const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"";
@@ -1498,39 +1490,62 @@ TEST_CASE("[String] Cyrillic to_lower()") {
}
TEST_CASE("[String] Count and countn functionality") {
-#define COUNT_TEST(x) \
- { \
- bool success = x; \
- state = state && success; \
- }
+ String s = String("");
+ MULTICHECK_STRING_EQ(s, count, "Test", 0);
- bool state = true;
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, count, "", 0);
- COUNT_TEST(String("").count("Test") == 0);
- COUNT_TEST(String("Test").count("") == 0);
- COUNT_TEST(String("Test").count("test") == 0);
- COUNT_TEST(String("Test").count("TEST") == 0);
- COUNT_TEST(String("TEST").count("TEST") == 1);
- COUNT_TEST(String("Test").count("Test") == 1);
- COUNT_TEST(String("aTest").count("Test") == 1);
- COUNT_TEST(String("Testa").count("Test") == 1);
- COUNT_TEST(String("TestTestTest").count("Test") == 3);
- COUNT_TEST(String("TestTestTest").count("TestTest") == 1);
- COUNT_TEST(String("TestGodotTestGodotTestGodot").count("Test") == 3);
-
- COUNT_TEST(String("TestTestTestTest").count("Test", 4, 8) == 1);
- COUNT_TEST(String("TestTestTestTest").count("Test", 4, 12) == 2);
- COUNT_TEST(String("TestTestTestTest").count("Test", 4, 16) == 3);
- COUNT_TEST(String("TestTestTestTest").count("Test", 4) == 3);
-
- COUNT_TEST(String("Test").countn("test") == 1);
- COUNT_TEST(String("Test").countn("TEST") == 1);
- COUNT_TEST(String("testTest-Testatest").countn("tEst") == 4);
- COUNT_TEST(String("testTest-TeStatest").countn("tEsT", 4, 16) == 2);
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, count, "test", 0);
- CHECK(state);
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, count, "TEST", 0);
+
+ s = "TEST";
+ MULTICHECK_STRING_EQ(s, count, "TEST", 1);
+
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, count, "Test", 1);
+
+ s = "aTest";
+ MULTICHECK_STRING_EQ(s, count, "Test", 1);
+
+ s = "Testa";
+ MULTICHECK_STRING_EQ(s, count, "Test", 1);
+
+ s = "TestTestTest";
+ MULTICHECK_STRING_EQ(s, count, "Test", 3);
+
+ s = "TestTestTest";
+ MULTICHECK_STRING_EQ(s, count, "TestTest", 1);
+
+ s = "TestGodotTestGodotTestGodot";
+ MULTICHECK_STRING_EQ(s, count, "Test", 3);
+
+ s = "TestTestTestTest";
+ MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 8, 1);
+
+ s = "TestTestTestTest";
+ MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 12, 2);
+
+ s = "TestTestTestTest";
+ MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 16, 3);
+
+ s = "TestTestTestTest";
+ MULTICHECK_STRING_INT_EQ(s, count, "Test", 4, 3);
+
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, countn, "test", 1);
+
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, countn, "TEST", 1);
+
+ s = "testTest-Testatest";
+ MULTICHECK_STRING_EQ(s, countn, "tEst", 4);
-#undef COUNT_TEST
+ s = "testTest-TeStatest";
+ MULTICHECK_STRING_INT_INT_EQ(s, countn, "tEsT", 4, 16, 2);
}
TEST_CASE("[String] Bigrams") {
@@ -1703,9 +1718,19 @@ TEST_CASE("[String] Strip edges") {
TEST_CASE("[String] Trim") {
String s = "aaaTestbbb";
- CHECK(s.trim_prefix("aaa") == "Testbbb");
- CHECK(s.trim_suffix("bbb") == "aaaTest");
- CHECK(s.trim_suffix("Test") == s);
+ MULTICHECK_STRING_EQ(s, trim_prefix, "aaa", "Testbbb");
+ MULTICHECK_STRING_EQ(s, trim_prefix, "Test", s);
+ MULTICHECK_STRING_EQ(s, trim_prefix, "", s);
+ MULTICHECK_STRING_EQ(s, trim_prefix, "aaaTestbbb", "");
+ MULTICHECK_STRING_EQ(s, trim_prefix, "bbb", s);
+ MULTICHECK_STRING_EQ(s, trim_prefix, "AAA", s);
+
+ MULTICHECK_STRING_EQ(s, trim_suffix, "bbb", "aaaTest");
+ MULTICHECK_STRING_EQ(s, trim_suffix, "Test", s);
+ MULTICHECK_STRING_EQ(s, trim_suffix, "", s);
+ MULTICHECK_STRING_EQ(s, trim_suffix, "aaaTestbbb", "");
+ MULTICHECK_STRING_EQ(s, trim_suffix, "aaa", s);
+ MULTICHECK_STRING_EQ(s, trim_suffix, "BBB", s);
}
TEST_CASE("[String] Right/Left") {
diff --git a/tests/core/templates/test_command_queue.h b/tests/core/templates/test_command_queue.h
index e94c108694..d2957b5c40 100644
--- a/tests/core/templates/test_command_queue.h
+++ b/tests/core/templates/test_command_queue.h
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/math/random_number_generator.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/os.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
@@ -100,7 +101,7 @@ public:
ThreadWork reader_threadwork;
ThreadWork writer_threadwork;
- CommandQueueMT command_queue = CommandQueueMT(true);
+ CommandQueueMT command_queue;
enum TestMsgType {
TEST_MSG_FUNC1_TRANSFORM,
@@ -119,6 +120,7 @@ public:
bool exit_threads = false;
Thread reader_thread;
+ WorkerThreadPool::TaskID reader_task_id = WorkerThreadPool::INVALID_TASK_ID;
Thread writer_thread;
int func1_count = 0;
@@ -148,11 +150,16 @@ public:
void reader_thread_loop() {
reader_threadwork.thread_wait_for_work();
while (!exit_threads) {
- if (message_count_to_read < 0) {
+ if (reader_task_id == WorkerThreadPool::INVALID_TASK_ID) {
command_queue.flush_all();
- }
- for (int i = 0; i < message_count_to_read; i++) {
- command_queue.wait_and_flush();
+ } else {
+ if (message_count_to_read < 0) {
+ command_queue.flush_all();
+ }
+ for (int i = 0; i < message_count_to_read; i++) {
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.wait_and_flush();
+ }
}
message_count_to_read = 0;
@@ -216,8 +223,13 @@ public:
sts->writer_thread_loop();
}
- void init_threads() {
- reader_thread.start(&SharedThreadState::static_reader_thread_loop, this);
+ void init_threads(bool p_use_thread_pool_sync = false) {
+ if (p_use_thread_pool_sync) {
+ reader_task_id = WorkerThreadPool::get_singleton()->add_native_task(&SharedThreadState::static_reader_thread_loop, this, true);
+ command_queue.set_pump_task_id(reader_task_id);
+ } else {
+ reader_thread.start(&SharedThreadState::static_reader_thread_loop, this);
+ }
writer_thread.start(&SharedThreadState::static_writer_thread_loop, this);
}
void destroy_threads() {
@@ -225,16 +237,20 @@ public:
reader_threadwork.main_start_work();
writer_threadwork.main_start_work();
- reader_thread.wait_to_finish();
+ if (reader_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(reader_task_id);
+ } else {
+ reader_thread.wait_to_finish();
+ }
writer_thread.wait_to_finish();
}
};
-TEST_CASE("[CommandQueue] Test Queue Basics") {
+static void test_command_queue_basic(bool p_use_thread_pool_sync) {
const char *COMMAND_QUEUE_SETTING = "memory/limits/command_queue/multithreading_queue_size_kb";
ProjectSettings::get_singleton()->set_setting(COMMAND_QUEUE_SETTING, 1);
SharedThreadState sts;
- sts.init_threads();
+ sts.init_threads(p_use_thread_pool_sync);
sts.add_msg_to_write(SharedThreadState::TEST_MSG_FUNC1_TRANSFORM);
sts.writer_threadwork.main_start_work();
@@ -272,6 +288,14 @@ TEST_CASE("[CommandQueue] Test Queue Basics") {
ProjectSettings::get_singleton()->property_get_revert(COMMAND_QUEUE_SETTING));
}
+TEST_CASE("[CommandQueue] Test Queue Basics") {
+ test_command_queue_basic(false);
+}
+
+TEST_CASE("[CommandQueue] Test Queue Basics with WorkerThreadPool sync.") {
+ test_command_queue_basic(true);
+}
+
TEST_CASE("[CommandQueue] Test Queue Wrapping to same spot.") {
const char *COMMAND_QUEUE_SETTING = "memory/limits/command_queue/multithreading_queue_size_kb";
ProjectSettings::get_singleton()->set_setting(COMMAND_QUEUE_SETTING, 1);
diff --git a/tests/core/templates/test_local_vector.h b/tests/core/templates/test_local_vector.h
index 2873a9a028..c9544c625b 100644
--- a/tests/core/templates/test_local_vector.h
+++ b/tests/core/templates/test_local_vector.h
@@ -63,7 +63,7 @@ TEST_CASE("[LocalVector] Push Back.") {
CHECK(vector[4] == 4);
}
-TEST_CASE("[LocalVector] Find.") {
+TEST_CASE("[LocalVector] Find, has.") {
LocalVector<int> vector;
vector.push_back(3);
vector.push_back(1);
@@ -85,6 +85,15 @@ TEST_CASE("[LocalVector] Find.") {
CHECK(vector.find(-1) == -1);
CHECK(vector.find(5) == -1);
+
+ CHECK(vector.has(0));
+ CHECK(vector.has(1));
+ CHECK(vector.has(2));
+ CHECK(vector.has(3));
+ CHECK(vector.has(4));
+
+ CHECK(!vector.has(-1));
+ CHECK(!vector.has(5));
}
TEST_CASE("[LocalVector] Remove.") {
diff --git a/tests/core/threads/test_worker_thread_pool.h b/tests/core/threads/test_worker_thread_pool.h
index e9a762b57b..0a0291d11b 100644
--- a/tests/core/threads/test_worker_thread_pool.h
+++ b/tests/core/threads/test_worker_thread_pool.h
@@ -38,6 +38,7 @@
namespace TestWorkerThreadPool {
static LocalVector<SafeNumeric<int>> counter;
+static SafeFlag exit;
static void static_test(void *p_arg) {
counter[(uint64_t)p_arg].increment();
@@ -106,6 +107,72 @@ TEST_CASE("[WorkerThreadPool] Process elements using group tasks") {
}
}
+static void static_test_daemon(void *p_arg) {
+ while (!exit.is_set()) {
+ counter[0].add(1);
+ WorkerThreadPool::get_singleton()->yield();
+ }
+}
+
+static void static_busy_task(void *p_arg) {
+ while (!exit.is_set()) {
+ OS::get_singleton()->delay_usec(1);
+ }
+}
+
+static void static_legit_task(void *p_arg) {
+ *((bool *)p_arg) = counter[0].get() > 0;
+ counter[1].add(1);
+}
+
+TEST_CASE("[WorkerThreadPool] Run a yielding daemon as the only hope for other tasks to run") {
+ exit.clear();
+ counter.clear();
+ counter.resize(2);
+
+ WorkerThreadPool::TaskID daemon_task_id = WorkerThreadPool::get_singleton()->add_native_task(static_test_daemon, nullptr, true);
+
+ int num_threads = WorkerThreadPool::get_singleton()->get_thread_count();
+
+ // Keep all the other threads busy.
+ LocalVector<WorkerThreadPool::TaskID> task_ids;
+ for (int i = 0; i < num_threads - 1; i++) {
+ task_ids.push_back(WorkerThreadPool::get_singleton()->add_native_task(static_busy_task, nullptr, true));
+ }
+
+ LocalVector<WorkerThreadPool::TaskID> legit_task_ids;
+ LocalVector<bool> legit_task_needed_yield;
+ int legit_tasks_count = num_threads * 4;
+ legit_task_needed_yield.resize(legit_tasks_count);
+ for (int i = 0; i < legit_tasks_count; i++) {
+ legit_task_needed_yield[i] = false;
+ task_ids.push_back(WorkerThreadPool::get_singleton()->add_native_task(static_legit_task, &legit_task_needed_yield[i], i >= legit_tasks_count / 2));
+ }
+
+ while (counter[1].get() != legit_tasks_count) {
+ OS::get_singleton()->delay_usec(1);
+ }
+
+ exit.set();
+ for (uint32_t i = 0; i < task_ids.size(); i++) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(task_ids[i]);
+ }
+ WorkerThreadPool::get_singleton()->notify_yield_over(daemon_task_id);
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(daemon_task_id);
+
+ CHECK_MESSAGE(counter[0].get() > 0, "Daemon task should have looped at least once.");
+ CHECK_MESSAGE(counter[1].get() == legit_tasks_count, "All legit tasks should have been able to run.");
+
+ bool all_needed_yield = true;
+ for (int i = 0; i < legit_tasks_count; i++) {
+ if (!legit_task_needed_yield[i]) {
+ all_needed_yield = false;
+ break;
+ }
+ }
+ CHECK_MESSAGE(all_needed_yield, "All legit tasks should have needed the daemon yielding to run.");
+}
+
} // namespace TestWorkerThreadPool
#endif // TEST_WORKER_THREAD_POOL_H
diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h
index 287345e831..c54854e4d7 100644
--- a/tests/core/variant/test_array.h
+++ b/tests/core/variant/test_array.h
@@ -555,6 +555,8 @@ TEST_CASE("[Array] Iteration") {
idx++;
}
+ CHECK_EQ(idx, a1.size());
+
idx = 0;
for (const Variant &E : (const Array &)a1) {
@@ -562,6 +564,8 @@ TEST_CASE("[Array] Iteration") {
idx++;
}
+ CHECK_EQ(idx, a1.size());
+
a1.clear();
}
diff --git a/tests/core/variant/test_dictionary.h b/tests/core/variant/test_dictionary.h
index 5bc56075da..aba20972d9 100644
--- a/tests/core/variant/test_dictionary.h
+++ b/tests/core/variant/test_dictionary.h
@@ -105,7 +105,7 @@ TEST_CASE("[Dictionary] get_key_lists()") {
map[1] = 3;
map.get_key_list(ptr);
CHECK(keys.size() == 1);
- CHECK(int(keys[0]) == 1);
+ CHECK(int(keys.front()->get()) == 1);
map[2] = 4;
map.get_key_list(ptr);
CHECK(keys.size() == 3);
diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h
index 54ca06c6c4..be615975f8 100644
--- a/tests/core/variant/test_variant.h
+++ b/tests/core/variant/test_variant.h
@@ -2034,6 +2034,10 @@ TEST_CASE("[Variant] Identity comparison") {
CHECK(packed_color_array.identity_compare(packed_color_array));
CHECK_FALSE(packed_color_array.identity_compare(PackedColorArray()));
+ Variant packed_vector4_array = PackedVector4Array();
+ CHECK(packed_vector4_array.identity_compare(packed_vector4_array));
+ CHECK_FALSE(packed_vector4_array.identity_compare(PackedVector4Array()));
+
Variant packed_float32_array = PackedFloat32Array();
CHECK(packed_float32_array.identity_compare(packed_float32_array));
CHECK_FALSE(packed_float32_array.identity_compare(PackedFloat32Array()));
diff --git a/tests/create_test.py b/tests/create_test.py
index deb53aca20..deb53aca20 100644..100755
--- a/tests/create_test.py
+++ b/tests/create_test.py
diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h
index 8d8a678e20..e4946995a7 100644
--- a/tests/display_server_mock.h
+++ b/tests/display_server_mock.h
@@ -47,13 +47,16 @@ private:
Callable event_callback;
Callable input_event_callback;
+ String clipboard_text;
+ String primary_clipboard_text;
+
static Vector<String> get_rendering_drivers_func() {
Vector<String> drivers;
drivers.push_back("dummy");
return drivers;
}
- static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) {
+ static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
r_error = OK;
RasterizerDummy::make_current();
return memnew(DisplayServerMock());
@@ -86,7 +89,7 @@ private:
}
void _send_window_event(WindowEvent p_event) {
- if (!event_callback.is_null()) {
+ if (event_callback.is_valid()) {
Variant event = int(p_event);
event_callback.call(event);
}
@@ -97,6 +100,8 @@ public:
switch (p_feature) {
case FEATURE_MOUSE:
case FEATURE_CURSOR_SHAPE:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_CLIPBOARD_PRIMARY:
return true;
default: {
}
@@ -131,6 +136,11 @@ public:
virtual Point2i mouse_get_position() const override { return mouse_position; }
+ virtual void clipboard_set(const String &p_text) override { clipboard_text = p_text; }
+ virtual String clipboard_get() const override { return clipboard_text; }
+ virtual void clipboard_set_primary(const String &p_text) override { primary_clipboard_text = p_text; }
+ virtual String clipboard_get_primary() const override { return primary_clipboard_text; }
+
virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override {
return Size2i(1920, 1080);
}
diff --git a/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl b/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl
index 58ddd08cdd..8ad5a23eb5 100644
--- a/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl
+++ b/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl
@@ -48,4 +48,3 @@ protected:
};
#endif
-
diff --git a/tests/python_build/test_gles3_builder.py b/tests/python_build/test_gles3_builder.py
index 6f16139eb9..b34d33bde7 100644
--- a/tests/python_build/test_gles3_builder.py
+++ b/tests/python_build/test_gles3_builder.py
@@ -2,7 +2,7 @@ import json
import pytest
-from gles3_builders import build_gles3_header, GLES3HeaderStruct
+from gles3_builders import GLES3HeaderStruct, build_gles3_header
@pytest.mark.parametrize(
diff --git a/tests/python_build/test_glsl_builder.py b/tests/python_build/test_glsl_builder.py
index 348ef8441c..9f548855ff 100644
--- a/tests/python_build/test_glsl_builder.py
+++ b/tests/python_build/test_glsl_builder.py
@@ -2,7 +2,7 @@ import json
import pytest
-from glsl_builders import build_raw_header, RAWHeaderStruct, build_rd_header, RDHeaderStruct
+from glsl_builders import RAWHeaderStruct, RDHeaderStruct, build_raw_header, build_rd_header
@pytest.mark.parametrize(
diff --git a/tests/scene/test_animation.h b/tests/scene/test_animation.h
index 89bf296815..6c89592e0d 100644
--- a/tests/scene/test_animation.h
+++ b/tests/scene/test_animation.h
@@ -41,7 +41,7 @@ TEST_CASE("[Animation] Empty animation getters") {
const Ref<Animation> animation = memnew(Animation);
CHECK(animation->get_length() == doctest::Approx(real_t(1.0)));
- CHECK(animation->get_step() == doctest::Approx(real_t(0.1)));
+ CHECK(animation->get_step() == doctest::Approx(real_t(1.0 / 30)));
}
TEST_CASE("[Animation] Create value track") {
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index b0a46b8107..c02830b6df 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -36,6 +36,15 @@
#include "tests/test_macros.h"
namespace TestCodeEdit {
+static inline Array build_array() {
+ return Array();
+}
+template <typename... Targs>
+static inline Array build_array(Variant item, Targs... Fargs) {
+ Array a = build_array(Fargs...);
+ a.push_front(item);
+ return a;
+}
TEST_CASE("[SceneTree][CodeEdit] line gutters") {
CodeEdit *code_edit = memnew(CodeEdit);
@@ -67,10 +76,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
ERR_PRINT_ON;
- Array arg1;
- arg1.push_back(0);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(0));
code_edit->set_line_as_breakpoint(0, true);
CHECK(code_edit->is_line_breakpointed(0));
@@ -86,10 +92,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
code_edit->clear_breakpointed_lines();
SIGNAL_CHECK_FALSE("breakpoint_toggled");
- Array arg1;
- arg1.push_back(0);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(0));
code_edit->set_line_as_breakpoint(0, true);
CHECK(code_edit->is_line_breakpointed(0));
@@ -101,10 +104,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
}
SUBCASE("[CodeEdit] breakpoints and set text") {
- Array arg1;
- arg1.push_back(0);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(0));
code_edit->set_text("test\nline");
code_edit->set_line_as_breakpoint(0, true);
@@ -121,7 +121,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
code_edit->clear_breakpointed_lines();
SIGNAL_DISCARD("breakpoint_toggled")
- ((Array)args[0])[0] = 1;
+ args = build_array(build_array(1));
code_edit->set_text("test\nline");
code_edit->set_line_as_breakpoint(1, true);
CHECK(code_edit->is_line_breakpointed(1));
@@ -137,10 +137,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
}
SUBCASE("[CodeEdit] breakpoints and clear") {
- Array arg1;
- arg1.push_back(0);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(0));
code_edit->set_text("test\nline");
code_edit->set_line_as_breakpoint(0, true);
@@ -157,7 +154,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
code_edit->clear_breakpointed_lines();
SIGNAL_DISCARD("breakpoint_toggled")
- ((Array)args[0])[0] = 1;
+ args = build_array(build_array(1));
code_edit->set_text("test\nline");
code_edit->set_line_as_breakpoint(1, true);
CHECK(code_edit->is_line_breakpointed(1));
@@ -173,21 +170,15 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
}
SUBCASE("[CodeEdit] breakpoints and new lines no text") {
- Array arg1;
- arg1.push_back(0);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(0));
/* No text moves breakpoint. */
code_edit->set_line_as_breakpoint(0, true);
CHECK(code_edit->is_line_breakpointed(0));
SIGNAL_CHECK("breakpoint_toggled", args);
- /* Normal. */
- ((Array)args[0])[0] = 0;
- Array arg2;
- arg2.push_back(1);
- args.push_back(arg2);
+ // Normal.
+ args = build_array(build_array(0), build_array(1));
SEND_GUI_ACTION("ui_text_newline");
CHECK(code_edit->get_line_count() == 2);
@@ -195,18 +186,16 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
CHECK(code_edit->is_line_breakpointed(1));
SIGNAL_CHECK("breakpoint_toggled", args);
- /* Non-Breaking. */
- ((Array)args[0])[0] = 1;
- ((Array)args[1])[0] = 2;
+ // Non-Breaking.
+ args = build_array(build_array(1), build_array(2));
SEND_GUI_ACTION("ui_text_newline_blank");
CHECK(code_edit->get_line_count() == 3);
CHECK_FALSE(code_edit->is_line_breakpointed(1));
CHECK(code_edit->is_line_breakpointed(2));
SIGNAL_CHECK("breakpoint_toggled", args);
- /* Above. */
- ((Array)args[0])[0] = 2;
- ((Array)args[1])[0] = 3;
+ // Above.
+ args = build_array(build_array(2), build_array(3));
SEND_GUI_ACTION("ui_text_newline_above");
CHECK(code_edit->get_line_count() == 4);
CHECK_FALSE(code_edit->is_line_breakpointed(2));
@@ -215,10 +204,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
}
SUBCASE("[CodeEdit] breakpoints and new lines with text") {
- Array arg1;
- arg1.push_back(0);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(0));
/* Having text does not move breakpoint. */
code_edit->insert_text_at_caret("text");
@@ -241,11 +227,8 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
CHECK_FALSE(code_edit->is_line_breakpointed(1));
SIGNAL_CHECK_FALSE("breakpoint_toggled");
- /* Above does move. */
- ((Array)args[0])[0] = 0;
- Array arg2;
- arg2.push_back(1);
- args.push_back(arg2);
+ // Above does move.
+ args = build_array(build_array(0), build_array(1));
code_edit->set_caret_line(0);
SEND_GUI_ACTION("ui_text_newline_above");
@@ -256,10 +239,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
}
SUBCASE("[CodeEdit] breakpoints and backspace") {
- Array arg1;
- arg1.push_back(1);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(1));
code_edit->set_text("\n\n");
code_edit->set_line_as_breakpoint(1, true);
@@ -281,8 +261,8 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
ERR_PRINT_ON;
SIGNAL_CHECK("breakpoint_toggled", args);
- /* Backspace above breakpointed line moves it. */
- ((Array)args[0])[0] = 2;
+ // Backspace above breakpointed line moves it.
+ args = build_array(build_array(2));
code_edit->set_text("\n\n");
code_edit->set_line_as_breakpoint(2, true);
@@ -291,9 +271,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
code_edit->set_caret_line(1);
- Array arg2;
- arg2.push_back(1);
- args.push_back(arg2);
+ args = build_array(build_array(2), build_array(1));
SEND_GUI_ACTION("ui_text_backspace");
ERR_PRINT_OFF;
CHECK_FALSE(code_edit->is_line_breakpointed(2));
@@ -303,10 +281,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
}
SUBCASE("[CodeEdit] breakpoints and delete") {
- Array arg1;
- arg1.push_back(1);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(1));
code_edit->set_text("\n\n");
code_edit->set_line_as_breakpoint(1, true);
@@ -329,8 +304,8 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
ERR_PRINT_ON;
SIGNAL_CHECK("breakpoint_toggled", args);
- /* Delete above breakpointed line moves it. */
- ((Array)args[0])[0] = 2;
+ // Delete above breakpointed line moves it.
+ args = build_array(build_array(2));
code_edit->set_text("\n\n");
code_edit->set_line_as_breakpoint(2, true);
@@ -339,9 +314,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
code_edit->set_caret_line(0);
- Array arg2;
- arg2.push_back(1);
- args.push_back(arg2);
+ args = build_array(build_array(2), build_array(1));
SEND_GUI_ACTION("ui_text_delete");
ERR_PRINT_OFF;
CHECK_FALSE(code_edit->is_line_breakpointed(2));
@@ -351,10 +324,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
}
SUBCASE("[CodeEdit] breakpoints and delete selection") {
- Array arg1;
- arg1.push_back(1);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(1));
code_edit->set_text("\n\n");
code_edit->set_line_as_breakpoint(1, true);
@@ -367,8 +337,8 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
CHECK_FALSE(code_edit->is_line_breakpointed(0));
SIGNAL_CHECK("breakpoint_toggled", args);
- /* Should handle breakpoint move when deleting selection by adding less text then removed. */
- ((Array)args[0])[0] = 9;
+ // Should handle breakpoint move when deleting selection by adding less text then removed.
+ args = build_array(build_array(9));
code_edit->set_text("\n\n\n\n\n\n\n\n\n");
code_edit->set_line_as_breakpoint(9, true);
@@ -377,9 +347,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
code_edit->select(0, 0, 6, 0);
- Array arg2;
- arg2.push_back(4);
- args.push_back(arg2);
+ args = build_array(build_array(9), build_array(4));
SEND_GUI_ACTION("ui_text_newline");
ERR_PRINT_OFF;
CHECK_FALSE(code_edit->is_line_breakpointed(9));
@@ -387,9 +355,8 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
CHECK(code_edit->is_line_breakpointed(4));
SIGNAL_CHECK("breakpoint_toggled", args);
- /* Should handle breakpoint move when deleting selection by adding more text then removed. */
- ((Array)args[0])[0] = 9;
- ((Array)args[1])[0] = 14;
+ // Should handle breakpoint move when deleting selection by adding more text then removed.
+ args = build_array(build_array(9), build_array(14));
code_edit->insert_text_at_caret("\n\n\n\n\n");
MessageQueue::get_singleton()->flush();
@@ -404,10 +371,7 @@ TEST_CASE("[SceneTree][CodeEdit] line gutters") {
}
SUBCASE("[CodeEdit] breakpoints and undo") {
- Array arg1;
- arg1.push_back(1);
- Array args;
- args.push_back(arg1);
+ Array args = build_array(build_array(1));
code_edit->set_text("\n\n");
code_edit->set_line_as_breakpoint(1, true);
@@ -1849,17 +1813,47 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->do_indent();
CHECK(code_edit->get_line(0) == "test\t");
- /* Indent lines does entire line and works without selection. */
+ // Insert in place with multiple carets.
+ code_edit->set_text("test text");
+ code_edit->set_caret_column(5);
+ code_edit->add_caret(0, 7);
+ code_edit->add_caret(0, 2);
+ code_edit->do_indent();
+ CHECK(code_edit->get_line(0) == "te\tst \tte\txt");
+ CHECK(code_edit->get_caret_count() == 3);
+ CHECK(code_edit->get_caret_column(0) == 7);
+ CHECK(code_edit->get_caret_column(1) == 10);
+ CHECK(code_edit->get_caret_column(2) == 3);
+ code_edit->remove_secondary_carets();
+
+ // Indent lines does entire line and works without selection.
code_edit->set_text("");
code_edit->insert_text_at_caret("test");
code_edit->indent_lines();
CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->get_caret_column() == 5);
/* Selection does entire line. */
code_edit->set_text("test");
code_edit->select_all();
code_edit->do_indent();
CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 5);
+
+ // Selection does entire line, right to left selection.
+ code_edit->set_text("test");
+ code_edit->select(0, 4, 0, 0);
+ code_edit->do_indent();
+ CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 5);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
/* Handles multiple lines. */
code_edit->set_text("test\ntext");
@@ -1867,6 +1861,11 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->do_indent();
CHECK(code_edit->get_line(0) == "\ttest");
CHECK(code_edit->get_line(1) == "\ttext");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 5);
/* Do not indent line if last col is zero. */
code_edit->set_text("test\ntext");
@@ -1874,6 +1873,11 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->do_indent();
CHECK(code_edit->get_line(0) == "\ttest");
CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
/* Indent even if last column of first line. */
code_edit->set_text("test\ntext");
@@ -1881,15 +1885,53 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->do_indent();
CHECK(code_edit->get_line(0) == "\ttest");
CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 5);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Indent even if last column of first line, reversed.
+ code_edit->set_text("test\ntext");
+ code_edit->select(1, 0, 0, 4);
+ code_edit->do_indent();
+ CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 5);
/* Check selection is adjusted. */
code_edit->set_text("test");
code_edit->select(0, 1, 0, 2);
code_edit->do_indent();
- CHECK(code_edit->get_selection_from_column() == 2);
- CHECK(code_edit->get_selection_to_column() == 3);
CHECK(code_edit->get_line(0) == "\ttest");
- code_edit->undo();
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 3);
+
+ // Indent once with multiple selections.
+ code_edit->set_text("test");
+ code_edit->select(0, 1, 0, 2);
+ code_edit->add_caret(0, 4);
+ code_edit->select(0, 4, 0, 3, 1);
+ code_edit->do_indent();
+ CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 0);
+ CHECK(code_edit->get_selection_origin_column(0) == 2);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 3);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_selection_origin_line(1) == 0);
+ CHECK(code_edit->get_selection_origin_column(1) == 5);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 4);
}
SUBCASE("[CodeEdit] indent spaces") {
@@ -1922,23 +1964,58 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->do_indent();
CHECK(code_edit->get_line(0) == "test ");
- /* Indent lines does entire line and works without selection. */
+ // Insert in place with multiple carets.
+ code_edit->set_text("test text");
+ code_edit->set_caret_column(5);
+ code_edit->add_caret(0, 7);
+ code_edit->add_caret(0, 2);
+ code_edit->do_indent();
+ CHECK(code_edit->get_line(0) == "te st te xt");
+ CHECK(code_edit->get_caret_count() == 3);
+ CHECK(code_edit->get_caret_column(0) == 10);
+ CHECK(code_edit->get_caret_column(1) == 14);
+ CHECK(code_edit->get_caret_column(2) == 4);
+ code_edit->remove_secondary_carets();
+
+ // Indent lines does entire line and works without selection.
code_edit->set_text("");
code_edit->insert_text_at_caret("test");
code_edit->indent_lines();
CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_caret_column() == 8);
/* Selection does entire line. */
code_edit->set_text("test");
code_edit->select_all();
code_edit->do_indent();
CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 8);
+
+ // Selection does entire line, right to left selection.
+ code_edit->set_text("test");
+ code_edit->select(0, 4, 0, 0);
+ code_edit->do_indent();
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
/* single indent only add required spaces. */
code_edit->set_text(" test");
code_edit->select_all();
code_edit->do_indent();
CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 8);
/* Handles multiple lines. */
code_edit->set_text("test\ntext");
@@ -1946,6 +2023,11 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->do_indent();
CHECK(code_edit->get_line(0) == " test");
CHECK(code_edit->get_line(1) == " text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 8);
/* Do not indent line if last col is zero. */
code_edit->set_text("test\ntext");
@@ -1953,6 +2035,11 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->do_indent();
CHECK(code_edit->get_line(0) == " test");
CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
/* Indent even if last column of first line. */
code_edit->set_text("test\ntext");
@@ -1960,14 +2047,53 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->do_indent();
CHECK(code_edit->get_line(0) == " test");
CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Indent even if last column of first line, right to left selection.
+ code_edit->set_text("test\ntext");
+ code_edit->select(1, 0, 0, 4);
+ code_edit->do_indent();
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 8);
/* Check selection is adjusted. */
code_edit->set_text("test");
code_edit->select(0, 1, 0, 2);
code_edit->do_indent();
- CHECK(code_edit->get_selection_from_column() == 5);
- CHECK(code_edit->get_selection_to_column() == 6);
CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 5);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 6);
+
+ // Indent once with multiple selections.
+ code_edit->set_text("test");
+ code_edit->select(0, 1, 0, 2);
+ code_edit->add_caret(0, 4);
+ code_edit->select(0, 4, 0, 3, 1);
+ code_edit->do_indent();
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 0);
+ CHECK(code_edit->get_selection_origin_column(0) == 5);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 6);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_selection_origin_line(1) == 0);
+ CHECK(code_edit->get_selection_origin_column(1) == 8);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 7);
}
SUBCASE("[CodeEdit] unindent tabs") {
@@ -2003,11 +2129,28 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->insert_text_at_caret("\ttest");
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == "test");
+ CHECK(code_edit->get_caret_column() == 4);
+
+ // Unindent lines once with multiple carets.
+ code_edit->set_text("\t\ttest");
+ code_edit->set_caret_column(1);
+ code_edit->add_caret(0, 3);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 2);
+ code_edit->remove_secondary_carets();
/* Caret on col zero unindent line. */
code_edit->set_text("\t\ttest");
+ code_edit->set_caret_column(0);
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->get_caret_column() == 0);
/* Check input action. */
code_edit->set_text("\t\ttest");
@@ -2019,13 +2162,34 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->select_all();
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 5);
- /* Handles multiple lines. */
- code_edit->set_text("\ttest\n\ttext");
+ // Selection does entire line, right to left selection.
+ code_edit->set_text("\t\ttest");
+ code_edit->select(0, 6, 0, 0);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 5);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Handles multiple lines.
+ code_edit->set_text("\t\ttest\n\t\ttext");
code_edit->select_all();
code_edit->unindent_lines();
- CHECK(code_edit->get_line(0) == "test");
- CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->get_line(1) == "\ttext");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 5);
/* Do not unindent line if last col is zero. */
code_edit->set_text("\ttest\n\ttext");
@@ -2033,6 +2197,23 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == "test");
CHECK(code_edit->get_line(1) == "\ttext");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Do not unindent line if last col is zero, right to left selection.
+ code_edit->set_text("\ttest\n\ttext");
+ code_edit->select(1, 0, 0, 0);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == "test");
+ CHECK(code_edit->get_line(1) == "\ttext");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
/* Unindent even if last column of first line. */
code_edit->set_text("\ttest\n\ttext");
@@ -2040,14 +2221,50 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == "test");
CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 4);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
/* Check selection is adjusted. */
code_edit->set_text("\ttest");
code_edit->select(0, 1, 0, 2);
code_edit->unindent_lines();
- CHECK(code_edit->get_selection_from_column() == 0);
- CHECK(code_edit->get_selection_to_column() == 1);
CHECK(code_edit->get_line(0) == "test");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Deselect if only the tab was selected.
+ code_edit->set_text("\ttest");
+ code_edit->select(0, 0, 0, 1);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == "test");
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Unindent once with multiple selections.
+ code_edit->set_text("\t\ttest");
+ code_edit->select(0, 1, 0, 2);
+ code_edit->add_caret(0, 4);
+ code_edit->select(0, 4, 0, 3, 1);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 0);
+ CHECK(code_edit->get_selection_origin_column(0) == 0);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 1);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_selection_origin_line(1) == 0);
+ CHECK(code_edit->get_selection_origin_column(1) == 3);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 2);
}
SUBCASE("[CodeEdit] unindent spaces") {
@@ -2089,11 +2306,28 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->insert_text_at_caret(" test");
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == "test");
+ CHECK(code_edit->get_caret_column() == 4);
+
+ // Unindent lines once with multiple carets.
+ code_edit->set_text(" test");
+ code_edit->set_caret_column(1);
+ code_edit->add_caret(0, 9);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 5);
+ code_edit->remove_secondary_carets();
/* Caret on col zero unindent line. */
code_edit->set_text(" test");
+ code_edit->set_caret_column(0);
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_caret_column() == 0);
/* Only as far as needed */
code_edit->set_text(" test");
@@ -2110,13 +2344,34 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->select_all();
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 8);
- /* Handles multiple lines. */
- code_edit->set_text(" test\n text");
+ // Selection does entire line, right to left selection.
+ code_edit->set_text(" test");
+ code_edit->select(0, 12, 0, 0);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Handles multiple lines.
+ code_edit->set_text(" test\n text");
code_edit->select_all();
code_edit->unindent_lines();
- CHECK(code_edit->get_line(0) == "test");
- CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_line(1) == " text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 8);
/* Do not unindent line if last col is zero. */
code_edit->set_text(" test\n text");
@@ -2124,6 +2379,23 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == "test");
CHECK(code_edit->get_line(1) == " text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Do not unindent line if last col is zero, right to left selection.
+ code_edit->set_text(" test\n text");
+ code_edit->select(1, 0, 0, 0);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == "test");
+ CHECK(code_edit->get_line(1) == " text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
/* Unindent even if last column of first line. */
code_edit->set_text(" test\n text");
@@ -2131,14 +2403,48 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->unindent_lines();
CHECK(code_edit->get_line(0) == "test");
CHECK(code_edit->get_line(1) == "text");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 1);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
/* Check selection is adjusted. */
code_edit->set_text(" test");
code_edit->select(0, 4, 0, 5);
code_edit->unindent_lines();
- CHECK(code_edit->get_selection_from_column() == 0);
- CHECK(code_edit->get_selection_to_column() == 1);
CHECK(code_edit->get_line(0) == "test");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Deselect if only the tab was selected.
+ code_edit->set_text(" test");
+ code_edit->select(0, 0, 0, 4);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == "test");
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Unindent once with multiple selections.
+ code_edit->set_text(" test");
+ code_edit->select(0, 1, 0, 2);
+ code_edit->add_caret(0, 4);
+ code_edit->select(0, 12, 0, 10, 1);
+ code_edit->unindent_lines();
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK_FALSE(code_edit->has_selection(0));
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_selection_origin_line(1) == 0);
+ CHECK(code_edit->get_selection_origin_column(1) == 8);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 6);
}
SUBCASE("[CodeEdit] auto indent") {
@@ -2153,6 +2459,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline");
CHECK(code_edit->get_line(0) == "test:");
CHECK(code_edit->get_line(1) == "\t");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
/* new blank line should still indent. */
code_edit->set_text("");
@@ -2160,6 +2468,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline_blank");
CHECK(code_edit->get_line(0) == "test:");
CHECK(code_edit->get_line(1) == "\t");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
/* new line above should not indent. */
code_edit->set_text("");
@@ -2167,6 +2477,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline_above");
CHECK(code_edit->get_line(0) == "");
CHECK(code_edit->get_line(1) == "test:");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
/* Whitespace between symbol and caret is okay. */
code_edit->set_text("");
@@ -2174,6 +2486,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline");
CHECK(code_edit->get_line(0) == "test: ");
CHECK(code_edit->get_line(1) == "\t");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
/* Comment between symbol and caret is okay. */
code_edit->add_comment_delimiter("#", "");
@@ -2183,6 +2497,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
CHECK(code_edit->get_line(0) == "test: # comment");
CHECK(code_edit->get_line(1) == "\t");
code_edit->remove_comment_delimiter("#");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
/* Strings between symbol and caret are not okay. */
code_edit->add_string_delimiter("#", "");
@@ -2192,6 +2508,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
CHECK(code_edit->get_line(0) == "test: # string");
CHECK(code_edit->get_line(1) == "");
code_edit->remove_string_delimiter("#");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
/* Non-whitespace prevents auto-indentation. */
code_edit->add_comment_delimiter("#", "");
@@ -2201,6 +2519,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
CHECK(code_edit->get_line(0) == "test := 0 # comment");
CHECK(code_edit->get_line(1) == "");
code_edit->remove_comment_delimiter("#");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
/* Even when there's no comments. */
code_edit->set_text("");
@@ -2208,6 +2528,53 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline");
CHECK(code_edit->get_line(0) == "test := 0");
CHECK(code_edit->get_line(1) == "");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Preserve current indentation.
+ code_edit->set_text("\ttest");
+ code_edit->set_caret_column(3);
+ SEND_GUI_ACTION("ui_text_newline");
+ CHECK(code_edit->get_line(0) == "\tte");
+ CHECK(code_edit->get_line(1) == "\tst");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Preserve current indentation blank.
+ code_edit->set_text("\ttest");
+ code_edit->set_caret_column(3);
+ SEND_GUI_ACTION("ui_text_newline_blank");
+ CHECK(code_edit->get_line(0) == "\ttest");
+ CHECK(code_edit->get_line(1) == "\t");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Preserve current indentation above.
+ code_edit->set_text("\ttest");
+ code_edit->set_caret_column(3);
+ SEND_GUI_ACTION("ui_text_newline_above");
+ CHECK(code_edit->get_line(0) == "\t");
+ CHECK(code_edit->get_line(1) == "\ttest");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Increase existing indentation.
+ code_edit->set_text("\ttest:");
+ code_edit->set_caret_column(6);
+ SEND_GUI_ACTION("ui_text_newline");
+ CHECK(code_edit->get_line(0) == "\ttest:");
+ CHECK(code_edit->get_line(1) == "\t\t");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 2);
+
+ // Increase existing indentation blank.
+ code_edit->set_text("\ttest:");
+ code_edit->set_caret_column(3);
+ SEND_GUI_ACTION("ui_text_newline_blank");
+ CHECK(code_edit->get_line(0) == "\ttest:");
+ CHECK(code_edit->get_line(1) == "\t\t");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 2);
/* If between brace pairs an extra line is added. */
code_edit->set_text("");
@@ -2217,6 +2584,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
CHECK(code_edit->get_line(0) == "test{");
CHECK(code_edit->get_line(1) == "\t");
CHECK(code_edit->get_line(2) == "}");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
/* Except when we are going above. */
code_edit->set_text("");
@@ -2225,6 +2594,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline_above");
CHECK(code_edit->get_line(0) == "");
CHECK(code_edit->get_line(1) == "test{}");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
/* or below. */
code_edit->set_text("");
@@ -2233,6 +2604,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline_blank");
CHECK(code_edit->get_line(0) == "test{}");
CHECK(code_edit->get_line(1) == "");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
}
SUBCASE("[CodeEdit] auto indent spaces") {
@@ -2246,6 +2619,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline");
CHECK(code_edit->get_line(0) == "test:");
CHECK(code_edit->get_line(1) == " ");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 4);
/* new blank line should still indent. */
code_edit->set_text("");
@@ -2253,6 +2628,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline_blank");
CHECK(code_edit->get_line(0) == "test:");
CHECK(code_edit->get_line(1) == " ");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 4);
/* new line above should not indent. */
code_edit->set_text("");
@@ -2260,6 +2637,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline_above");
CHECK(code_edit->get_line(0) == "");
CHECK(code_edit->get_line(1) == "test:");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
/* Whitespace between symbol and caret is okay. */
code_edit->set_text("");
@@ -2267,6 +2646,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline");
CHECK(code_edit->get_line(0) == "test: ");
CHECK(code_edit->get_line(1) == " ");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 4);
/* Comment between symbol and caret is okay. */
code_edit->add_comment_delimiter("#", "");
@@ -2276,6 +2657,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
CHECK(code_edit->get_line(0) == "test: # comment");
CHECK(code_edit->get_line(1) == " ");
code_edit->remove_comment_delimiter("#");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 4);
/* Strings between symbol and caret are not okay. */
code_edit->add_string_delimiter("#", "");
@@ -2285,6 +2668,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
CHECK(code_edit->get_line(0) == "test: # string");
CHECK(code_edit->get_line(1) == "");
code_edit->remove_string_delimiter("#");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
/* Non-whitespace prevents auto-indentation. */
code_edit->add_comment_delimiter("#", "");
@@ -2294,6 +2679,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
CHECK(code_edit->get_line(0) == "test := 0 # comment");
CHECK(code_edit->get_line(1) == "");
code_edit->remove_comment_delimiter("#");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
/* Even when there's no comments. */
code_edit->set_text("");
@@ -2301,6 +2688,53 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline");
CHECK(code_edit->get_line(0) == "test := 0");
CHECK(code_edit->get_line(1) == "");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Preserve current indentation.
+ code_edit->set_text(" test");
+ code_edit->set_caret_column(6);
+ SEND_GUI_ACTION("ui_text_newline");
+ CHECK(code_edit->get_line(0) == " te");
+ CHECK(code_edit->get_line(1) == " st");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 4);
+
+ // Preserve current indentation blank.
+ code_edit->set_text(" test");
+ code_edit->set_caret_column(6);
+ SEND_GUI_ACTION("ui_text_newline_blank");
+ CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->get_line(1) == " ");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 4);
+
+ // Preserve current indentation above.
+ code_edit->set_text(" test");
+ code_edit->set_caret_column(6);
+ SEND_GUI_ACTION("ui_text_newline_above");
+ CHECK(code_edit->get_line(0) == " ");
+ CHECK(code_edit->get_line(1) == " test");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 4);
+
+ // Increase existing indentation.
+ code_edit->set_text(" test:");
+ code_edit->set_caret_column(9);
+ SEND_GUI_ACTION("ui_text_newline");
+ CHECK(code_edit->get_line(0) == " test:");
+ CHECK(code_edit->get_line(1) == " ");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 8);
+
+ // Increase existing indentation blank.
+ code_edit->set_text(" test:");
+ code_edit->set_caret_column(9);
+ SEND_GUI_ACTION("ui_text_newline");
+ CHECK(code_edit->get_line(0) == " test:");
+ CHECK(code_edit->get_line(1) == " ");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 8);
/* If between brace pairs an extra line is added. */
code_edit->set_text("");
@@ -2310,6 +2744,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
CHECK(code_edit->get_line(0) == "test{");
CHECK(code_edit->get_line(1) == " ");
CHECK(code_edit->get_line(2) == "}");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 4);
/* Except when we are going above. */
code_edit->set_text("");
@@ -2318,6 +2754,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline_above");
CHECK(code_edit->get_line(0) == "");
CHECK(code_edit->get_line(1) == "test{}");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
/* or below. */
code_edit->set_text("");
@@ -2326,6 +2764,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
SEND_GUI_ACTION("ui_text_newline_blank");
CHECK(code_edit->get_line(0) == "test{}");
CHECK(code_edit->get_line(1) == "");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
/* If there is something after a colon
and there is a colon in the comment it
@@ -2337,6 +2777,8 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
CHECK(code_edit->get_line(0) == "test:test#:");
CHECK(code_edit->get_line(1) == "");
code_edit->remove_comment_delimiter("#");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
}
}
@@ -2345,64 +2787,50 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->set_indent_using_spaces(false);
// Only line.
- code_edit->insert_text_at_caret(" test");
- code_edit->set_caret_line(0);
- code_edit->set_caret_column(8);
- code_edit->select(0, 8, 0, 9);
+ code_edit->set_text(" test");
+ code_edit->select(0, 9, 0, 8);
code_edit->convert_indent();
CHECK(code_edit->get_line(0) == "\t\ttest");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 3);
CHECK(code_edit->get_caret_column() == 2);
- CHECK(code_edit->get_selection_from_column() == 2);
- CHECK(code_edit->get_selection_to_column() == 3);
// First line.
- code_edit->set_text("");
- code_edit->insert_text_at_caret(" test\n");
- code_edit->set_caret_line(0);
- code_edit->set_caret_column(8);
+ code_edit->set_text(" test\n");
code_edit->select(0, 8, 0, 9);
code_edit->convert_indent();
CHECK(code_edit->get_line(0) == "\t\ttest");
- CHECK(code_edit->get_caret_column() == 2);
- CHECK(code_edit->get_selection_from_column() == 2);
- CHECK(code_edit->get_selection_to_column() == 3);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK(code_edit->get_caret_column() == 3);
// Middle line.
- code_edit->set_text("");
- code_edit->insert_text_at_caret("\n test\n");
- code_edit->set_caret_line(1);
- code_edit->set_caret_column(8);
+ code_edit->set_text("\n test\n");
code_edit->select(1, 8, 1, 9);
code_edit->convert_indent();
CHECK(code_edit->get_line(1) == "\t\ttest");
- CHECK(code_edit->get_caret_column() == 2);
- CHECK(code_edit->get_selection_from_column() == 2);
- CHECK(code_edit->get_selection_to_column() == 3);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK(code_edit->get_caret_column() == 3);
// End line.
- code_edit->set_text("");
- code_edit->insert_text_at_caret("\n test");
- code_edit->set_caret_line(1);
- code_edit->set_caret_column(8);
+ code_edit->set_text("\n test");
code_edit->select(1, 8, 1, 9);
code_edit->convert_indent();
CHECK(code_edit->get_line(1) == "\t\ttest");
- CHECK(code_edit->get_caret_column() == 2);
- CHECK(code_edit->get_selection_from_column() == 2);
- CHECK(code_edit->get_selection_to_column() == 3);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK(code_edit->get_caret_column() == 3);
// Within provided range.
- code_edit->set_text("");
- code_edit->insert_text_at_caret(" test\n test\n");
- code_edit->set_caret_line(1);
- code_edit->set_caret_column(8);
+ code_edit->set_text(" test\n test\n");
code_edit->select(1, 8, 1, 9);
code_edit->convert_indent(1, 1);
CHECK(code_edit->get_line(0) == " test");
CHECK(code_edit->get_line(1) == "\t\ttest");
- CHECK(code_edit->get_caret_column() == 2);
- CHECK(code_edit->get_selection_from_column() == 2);
- CHECK(code_edit->get_selection_to_column() == 3);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK(code_edit->get_caret_column() == 3);
}
SUBCASE("[CodeEdit] convert indent to spaces") {
@@ -2410,64 +2838,50 @@ TEST_CASE("[SceneTree][CodeEdit] indent") {
code_edit->set_indent_using_spaces(true);
// Only line.
- code_edit->insert_text_at_caret("\t\ttest");
- code_edit->set_caret_line(0);
- code_edit->set_caret_column(2);
- code_edit->select(0, 2, 0, 3);
+ code_edit->set_text("\t\ttest");
+ code_edit->select(0, 3, 0, 2);
code_edit->convert_indent();
CHECK(code_edit->get_line(0) == " test");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 9);
CHECK(code_edit->get_caret_column() == 8);
- CHECK(code_edit->get_selection_from_column() == 8);
- CHECK(code_edit->get_selection_to_column() == 9);
// First line.
- code_edit->set_text("");
- code_edit->insert_text_at_caret("\t\ttest\n");
- code_edit->set_caret_line(0);
- code_edit->set_caret_column(2);
+ code_edit->set_text("\t\ttest\n");
code_edit->select(0, 2, 0, 3);
code_edit->convert_indent();
CHECK(code_edit->get_line(0) == " test");
- CHECK(code_edit->get_caret_column() == 8);
- CHECK(code_edit->get_selection_from_column() == 8);
- CHECK(code_edit->get_selection_to_column() == 9);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK(code_edit->get_caret_column() == 9);
// Middle line.
- code_edit->set_text("");
- code_edit->insert_text_at_caret("\n\t\ttest\n");
- code_edit->set_caret_line(1);
- code_edit->set_caret_column(2);
+ code_edit->set_text("\n\t\ttest\n");
code_edit->select(1, 2, 1, 3);
code_edit->convert_indent();
CHECK(code_edit->get_line(1) == " test");
- CHECK(code_edit->get_caret_column() == 8);
- CHECK(code_edit->get_selection_from_column() == 8);
- CHECK(code_edit->get_selection_to_column() == 9);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK(code_edit->get_caret_column() == 9);
// End line.
- code_edit->set_text("");
- code_edit->insert_text_at_caret("\n\t\ttest");
- code_edit->set_caret_line(1);
- code_edit->set_caret_column(2);
+ code_edit->set_text("\n\t\ttest");
code_edit->select(1, 2, 1, 3);
code_edit->convert_indent();
CHECK(code_edit->get_line(1) == " test");
- CHECK(code_edit->get_caret_column() == 8);
- CHECK(code_edit->get_selection_from_column() == 8);
- CHECK(code_edit->get_selection_to_column() == 9);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK(code_edit->get_caret_column() == 9);
// Within provided range.
- code_edit->set_text("");
- code_edit->insert_text_at_caret("\ttest\n\t\ttest\n");
- code_edit->set_caret_line(1);
- code_edit->set_caret_column(2);
+ code_edit->set_text("\ttest\n\t\ttest\n");
code_edit->select(1, 2, 1, 3);
code_edit->convert_indent(1, 1);
CHECK(code_edit->get_line(0) == "\ttest");
CHECK(code_edit->get_line(1) == " test");
- CHECK(code_edit->get_caret_column() == 8);
- CHECK(code_edit->get_selection_from_column() == 8);
- CHECK(code_edit->get_selection_to_column() == 9);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK(code_edit->get_caret_column() == 9);
// Outside of range.
ERR_PRINT_OFF;
@@ -2484,6 +2898,7 @@ TEST_CASE("[SceneTree][CodeEdit] folding") {
CodeEdit *code_edit = memnew(CodeEdit);
SceneTree::get_singleton()->get_root()->add_child(code_edit);
code_edit->grab_focus();
+ code_edit->set_line_folding_enabled(true);
SUBCASE("[CodeEdit] folding settings") {
code_edit->set_line_folding_enabled(true);
@@ -2494,8 +2909,6 @@ TEST_CASE("[SceneTree][CodeEdit] folding") {
}
SUBCASE("[CodeEdit] folding") {
- code_edit->set_line_folding_enabled(true);
-
// No indent.
code_edit->set_text("line1\nline2\nline3");
for (int i = 0; i < 2; i++) {
@@ -2862,6 +3275,100 @@ TEST_CASE("[SceneTree][CodeEdit] folding") {
CHECK(code_edit->get_next_visible_line_offset_from(1, 1) == 4);
}
+ SUBCASE("[CodeEdit] folding carets") {
+ // Folding a line moves all carets that would be hidden.
+ code_edit->set_text("test\n\tline1\n\t\tline 2\n");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(0);
+ code_edit->add_caret(1, 3);
+ code_edit->add_caret(2, 8);
+ code_edit->add_caret(2, 1);
+ code_edit->select(2, 0, 2, 1, 3);
+
+ code_edit->fold_line(0);
+ CHECK(code_edit->is_line_folded(0));
+ CHECK_FALSE(code_edit->is_line_folded(1));
+ CHECK(code_edit->get_caret_count() == 1);
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 4);
+
+ // Undoing an action that puts the caret on a folded line unfolds it.
+ code_edit->set_text("test\n\tline1");
+ code_edit->select(1, 1, 1, 2);
+ code_edit->duplicate_selection();
+ CHECK(code_edit->get_text() == "test\n\tlline1");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 3);
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ code_edit->fold_line(0);
+ CHECK(code_edit->is_line_folded(0));
+ CHECK_FALSE(code_edit->is_line_folded(1));
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 4);
+
+ code_edit->undo();
+ CHECK(code_edit->get_text() == "test\n\tline1");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 2);
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 1);
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ CHECK_FALSE(code_edit->is_line_folded(1));
+
+ // Redoing doesn't refold.
+ code_edit->redo();
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 3);
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ CHECK_FALSE(code_edit->is_line_folded(1));
+ }
+
+ SUBCASE("[CodeEdit] toggle folding carets") {
+ code_edit->set_text("test\n\tline1\ntest2\n\tline2");
+
+ // Fold lines with carets on them.
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(1);
+ code_edit->toggle_foldable_lines_at_carets();
+ CHECK(code_edit->is_line_folded(0));
+ CHECK_FALSE(code_edit->is_line_folded(2));
+
+ // Toggle fold on lines with carets.
+ code_edit->add_caret(2, 0);
+ code_edit->toggle_foldable_lines_at_carets();
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ CHECK(code_edit->is_line_folded(2));
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 1);
+ CHECK(code_edit->get_caret_line(1) == 2);
+ CHECK(code_edit->get_caret_column(1) == 0);
+
+ // Multiple carets as part of one fold.
+ code_edit->unfold_all_lines();
+ code_edit->remove_secondary_carets();
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(1);
+ code_edit->add_caret(0, 4);
+ code_edit->add_caret(1, 2);
+ code_edit->toggle_foldable_lines_at_carets();
+ CHECK(code_edit->is_line_folded(0));
+ CHECK_FALSE(code_edit->is_line_folded(2));
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 1);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 4);
+ }
+
memdelete(code_edit);
}
@@ -2870,7 +3377,7 @@ TEST_CASE("[SceneTree][CodeEdit] region folding") {
SceneTree::get_singleton()->get_root()->add_child(code_edit);
code_edit->grab_focus();
- SUBCASE("[CodeEdit] region folding") {
+ SUBCASE("[CodeEdit] region tags") {
code_edit->set_line_folding_enabled(true);
// Region tag detection.
@@ -2907,16 +3414,51 @@ TEST_CASE("[SceneTree][CodeEdit] region folding") {
ERR_PRINT_ON;
CHECK(code_edit->get_code_region_start_tag() == "region");
CHECK(code_edit->get_code_region_end_tag() == "endregion");
+ }
- // Region creation with selection adds start / close region lines.
+ SUBCASE("[CodeEdit] create code region") {
+ code_edit->set_line_folding_enabled(true);
+
+ // Region creation with selection adds start and close region lines. Region name is selected and the region is folded.
code_edit->set_text("line1\nline2\nline3");
code_edit->clear_comment_delimiters();
code_edit->add_comment_delimiter("#", "");
code_edit->select(1, 0, 1, 4);
code_edit->create_code_region();
CHECK(code_edit->is_line_code_region_start(1));
- CHECK(code_edit->get_line(2).contains("line2"));
CHECK(code_edit->is_line_code_region_end(3));
+ CHECK(code_edit->get_text() == "line1\n#region New Code Region\nline2\n#endregion\nline3");
+ CHECK(code_edit->get_caret_count() == 1);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selected_text() == "New Code Region");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 23);
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK(code_edit->is_line_folded(1));
+
+ // Undo region creation. Line get unfolded.
+ code_edit->undo();
+ CHECK(code_edit->get_text() == "line1\nline2\nline3");
+ CHECK(code_edit->get_caret_count() == 1);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 4);
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK_FALSE(code_edit->is_line_folded(1));
+
+ // Redo region creation.
+ code_edit->redo();
+ CHECK(code_edit->get_text() == "line1\n#region New Code Region\nline2\n#endregion\nline3");
+ CHECK(code_edit->get_caret_count() == 1);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selected_text() == "New Code Region");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 23);
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK_FALSE(code_edit->is_line_folded(1));
// Region creation without any selection has no effect.
code_edit->set_text("line1\nline2\nline3");
@@ -2925,7 +3467,7 @@ TEST_CASE("[SceneTree][CodeEdit] region folding") {
code_edit->create_code_region();
CHECK(code_edit->get_text() == "line1\nline2\nline3");
- // Region creation with multiple selections.
+ // Region creation with multiple selections. Secondary carets are removed and the first region name is selected.
code_edit->set_text("line1\nline2\nline3");
code_edit->clear_comment_delimiters();
code_edit->add_comment_delimiter("#", "");
@@ -2934,6 +3476,25 @@ TEST_CASE("[SceneTree][CodeEdit] region folding") {
code_edit->select(2, 0, 2, 5, 1);
code_edit->create_code_region();
CHECK(code_edit->get_text() == "#region New Code Region\nline1\n#endregion\nline2\n#region New Code Region\nline3\n#endregion");
+ CHECK(code_edit->get_caret_count() == 1);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selected_text() == "New Code Region");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 23);
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 8);
+
+ // Region creation with mixed selection and non-selection carets. Regular carets are ignored.
+ code_edit->set_text("line1\nline2\nline3");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->select(0, 0, 0, 4, 0);
+ code_edit->add_caret(2, 5);
+ code_edit->create_code_region();
+ CHECK(code_edit->get_text() == "#region New Code Region\nline1\n#endregion\nline2\nline3");
+ CHECK(code_edit->get_caret_count() == 1);
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selected_text() == "New Code Region");
// Two selections on the same line create only one region.
code_edit->set_text("test line1\ntest line2\ntest line3");
@@ -2960,6 +3521,10 @@ TEST_CASE("[SceneTree][CodeEdit] region folding") {
code_edit->add_comment_delimiter("/*", "*/");
code_edit->create_code_region();
CHECK(code_edit->get_text() == "line1\nline2\nline3");
+ }
+
+ SUBCASE("[CodeEdit] region comment delimiters") {
+ code_edit->set_line_folding_enabled(true);
// Choose one line comment delimiter.
code_edit->set_text("//region region_name\nline2\n//endregion");
@@ -2993,6 +3558,10 @@ TEST_CASE("[SceneTree][CodeEdit] region folding") {
code_edit->clear_comment_delimiters();
CHECK_FALSE(code_edit->is_line_code_region_start(0));
CHECK_FALSE(code_edit->is_line_code_region_end(2));
+ }
+
+ SUBCASE("[CodeEdit] fold region") {
+ code_edit->set_line_folding_enabled(true);
// Fold region.
code_edit->clear_comment_delimiters();
@@ -3895,10 +4464,7 @@ TEST_CASE("[SceneTree][CodeEdit] symbol lookup") {
SEND_GUI_KEY_EVENT(Key::CTRL);
#endif
- Array signal_args;
- Array arg;
- arg.push_back("some");
- signal_args.push_back(arg);
+ Array signal_args = build_array(build_array("some"));
SIGNAL_CHECK("symbol_validate", signal_args);
SIGNAL_UNWATCH(code_edit, "symbol_validate");
@@ -3928,178 +4494,980 @@ TEST_CASE("[SceneTree][CodeEdit] line length guidelines") {
memdelete(code_edit);
}
-TEST_CASE("[SceneTree][CodeEdit] Backspace delete") {
+TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
CodeEdit *code_edit = memnew(CodeEdit);
SceneTree::get_singleton()->get_root()->add_child(code_edit);
code_edit->grab_focus();
- /* Backspace with selection on first line. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("test backspace");
- code_edit->select(0, 0, 0, 5);
- code_edit->backspace();
- CHECK(code_edit->get_line(0) == "backspace");
-
- /* Backspace with selection on first line and caret at the beginning of file. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("test backspace");
- code_edit->select(0, 0, 0, 5);
- code_edit->set_caret_column(0);
- code_edit->backspace();
- CHECK(code_edit->get_line(0) == "backspace");
-
- /* Move caret up to the previous line on backspace if caret is at the first column. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("line 1\nline 2");
- code_edit->set_caret_line(1);
- code_edit->set_caret_column(0);
- code_edit->backspace();
- CHECK(code_edit->get_line(0) == "line 1line 2");
- CHECK(code_edit->get_caret_line() == 0);
- CHECK(code_edit->get_caret_column() == 6);
-
- /* Backspace delete all text if all text is selected. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("line 1\nline 2\nline 3");
- code_edit->select_all();
- code_edit->backspace();
- CHECK(code_edit->get_text().is_empty());
-
- /* Backspace at the beginning without selection has no effect. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("line 1\nline 2\nline 3");
- code_edit->set_caret_line(0);
- code_edit->set_caret_column(0);
- code_edit->backspace();
- CHECK(code_edit->get_text() == "line 1\nline 2\nline 3");
+ SUBCASE("[SceneTree][CodeEdit] backspace") {
+ // Backspace with selection on first line.
+ code_edit->set_text("test backspace");
+ code_edit->select(0, 0, 0, 5);
+ code_edit->backspace();
+ CHECK(code_edit->get_line(0) == "backspace");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
- memdelete(code_edit);
-}
+ // Backspace with selection on first line and caret at the beginning of file.
+ code_edit->set_text("test backspace");
+ code_edit->select(0, 5, 0, 0);
+ code_edit->backspace();
+ CHECK(code_edit->get_line(0) == "backspace");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
-TEST_CASE("[SceneTree][CodeEdit] New Line") {
- CodeEdit *code_edit = memnew(CodeEdit);
- SceneTree::get_singleton()->get_root()->add_child(code_edit);
- code_edit->grab_focus();
+ // Move caret up to the previous line on backspace if caret is at the first column.
+ code_edit->set_text("line 1\nline 2");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(0);
+ code_edit->backspace();
+ CHECK(code_edit->get_line(0) == "line 1line 2");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 6);
- /* Add a new line. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("test new line");
- code_edit->set_caret_line(0);
- code_edit->set_caret_column(13);
- SEND_GUI_ACTION("ui_text_newline");
- CHECK(code_edit->get_line(0) == "test new line");
- CHECK(code_edit->get_line(1) == "");
-
- /* Split line with new line. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("test new line");
- code_edit->set_caret_line(0);
- code_edit->set_caret_column(5);
- SEND_GUI_ACTION("ui_text_newline");
- CHECK(code_edit->get_line(0) == "test ");
- CHECK(code_edit->get_line(1) == "new line");
-
- /* Delete selection and split with new line. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("test new line");
- code_edit->select(0, 0, 0, 5);
- SEND_GUI_ACTION("ui_text_newline");
- CHECK(code_edit->get_line(0) == "");
- CHECK(code_edit->get_line(1) == "new line");
-
- /* Blank new line below with selection should not split. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("test new line");
- code_edit->select(0, 0, 0, 5);
- SEND_GUI_ACTION("ui_text_newline_blank");
- CHECK(code_edit->get_line(0) == "test new line");
- CHECK(code_edit->get_line(1) == "");
-
- /* Blank new line above with selection should not split. */
- code_edit->set_text("");
- code_edit->insert_text_at_caret("test new line");
- code_edit->select(0, 0, 0, 5);
- SEND_GUI_ACTION("ui_text_newline_above");
- CHECK(code_edit->get_line(0) == "");
- CHECK(code_edit->get_line(1) == "test new line");
+ // Multiple carets with a caret at the first column.
+ code_edit->set_text("line 1\nline 2");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(2);
+ code_edit->add_caret(1, 0);
+ code_edit->add_caret(1, 5);
+ code_edit->backspace();
+ CHECK(code_edit->get_text() == "line 1lne2");
+ CHECK(code_edit->get_caret_count() == 3);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 7);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 6);
+ CHECK(code_edit->get_caret_line(2) == 0);
+ CHECK(code_edit->get_caret_column(2) == 9);
+ code_edit->remove_secondary_carets();
+
+ // Multiple carets close together.
+ code_edit->set_text("line 1\nline 2");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(2);
+ code_edit->add_caret(1, 1);
+ code_edit->backspace();
+ CHECK(code_edit->get_text() == "line 1\nne 2");
+ CHECK(code_edit->get_caret_count() == 1);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
- memdelete(code_edit);
-}
+ // Backspace delete all text if all text is selected.
+ code_edit->set_text("line 1\nline 2\nline 3");
+ code_edit->select_all();
+ code_edit->backspace();
+ CHECK(code_edit->get_text().is_empty());
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
-TEST_CASE("[SceneTree][CodeEdit] Duplicate Lines") {
- CodeEdit *code_edit = memnew(CodeEdit);
- SceneTree::get_singleton()->get_root()->add_child(code_edit);
- code_edit->grab_focus();
+ // Backspace at the beginning without selection has no effect.
+ code_edit->set_text("line 1\nline 2\nline 3");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(0);
+ code_edit->backspace();
+ CHECK(code_edit->get_text() == "line 1\nline 2\nline 3");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+ }
+
+ SUBCASE("[TextEdit] cut") {
+ DisplayServerMock *DS = (DisplayServerMock *)(DisplayServer::get_singleton());
+ code_edit->set_line_folding_enabled(true);
+
+ // Cut without a selection removes the entire line.
+ code_edit->set_text("this is\nsome\n");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(6);
+
+ code_edit->cut();
+ CHECK(DS->clipboard_get() == "this is\n");
+ CHECK(code_edit->get_text() == "some\n");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 3); // In the default font, this is the same position.
+
+ // Undo restores the cut text.
+ code_edit->undo();
+ CHECK(DS->clipboard_get() == "this is\n");
+ CHECK(code_edit->get_text() == "this is\nsome\n");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 6);
+
+ // Redo.
+ code_edit->redo();
+ CHECK(DS->clipboard_get() == "this is\n");
+ CHECK(code_edit->get_text() == "some\n");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 3);
+
+ // Cut unfolds the line.
+ code_edit->set_text("this is\n\tsome\n");
+ code_edit->fold_line(0);
+ CHECK(code_edit->is_line_folded(0));
+
+ code_edit->cut();
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ CHECK(DS->clipboard_get() == "this is\n");
+ CHECK(code_edit->get_text() == "\tsome\n");
+ CHECK(code_edit->get_caret_line() == 0);
+
+ // Cut with a selection removes just the selection.
+ code_edit->set_text("this is\nsome\n");
+ code_edit->select(0, 5, 0, 7);
+
+ SEND_GUI_ACTION("ui_cut");
+ CHECK(code_edit->get_viewport()->is_input_handled());
+ CHECK(DS->clipboard_get() == "is");
+ CHECK(code_edit->get_text() == "this \nsome\n");
+ CHECK_FALSE(code_edit->get_caret_line());
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 5);
+
+ // Cut does not change the text if not editable. Text is still added to clipboard.
+ code_edit->set_text("this is\nsome\n");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(5);
+
+ code_edit->set_editable(false);
+ code_edit->cut();
+ code_edit->set_editable(true);
+ CHECK(DS->clipboard_get() == "this is\n");
+ CHECK(code_edit->get_text() == "this is\nsome\n");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 5);
+
+ // Cut line with multiple carets.
+ code_edit->set_text("this is\nsome\n");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(3);
+ code_edit->add_caret(0, 2);
+ code_edit->add_caret(0, 4);
+ code_edit->add_caret(2, 0);
+
+ code_edit->cut();
+ CHECK(DS->clipboard_get() == "this is\n\n");
+ CHECK(code_edit->get_text() == "some");
+ CHECK(code_edit->get_caret_count() == 3);
+ CHECK_FALSE(code_edit->has_selection(0));
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 2); // In the default font, this is the same position.
+ // The previous caret at index 1 was merged.
+ CHECK_FALSE(code_edit->has_selection(1));
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 3); // In the default font, this is the same position.
+ CHECK_FALSE(code_edit->has_selection(2));
+ CHECK(code_edit->get_caret_line(2) == 0);
+ CHECK(code_edit->get_caret_column(2) == 4);
+ code_edit->remove_secondary_carets();
+
+ // Cut on the only line removes the contents.
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(2);
+
+ code_edit->cut();
+ CHECK(DS->clipboard_get() == "some\n");
+ CHECK(code_edit->get_text() == "");
+ CHECK(code_edit->get_line_count() == 1);
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Cut empty line.
+ code_edit->cut();
+ CHECK(DS->clipboard_get() == "\n");
+ CHECK(code_edit->get_text() == "");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Cut multiple lines, in order.
+ code_edit->set_text("this is\nsome\ntext to\nbe\n\ncut");
+ code_edit->set_caret_line(2);
+ code_edit->set_caret_column(7);
+ code_edit->add_caret(3, 0);
+ code_edit->add_caret(0, 2);
+
+ code_edit->cut();
+ CHECK(DS->clipboard_get() == "this is\ntext to\nbe\n");
+ CHECK(code_edit->get_text() == "some\n\ncut");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->get_caret_line(0) == 1);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 2);
+ code_edit->remove_secondary_carets();
+
+ // Cut multiple selections, in order. Ignores regular carets.
+ code_edit->set_text("this is\nsome\ntext to\nbe\n\ncut");
+ code_edit->add_caret(3, 0);
+ code_edit->add_caret(0, 2);
+ code_edit->add_caret(2, 0);
+ code_edit->select(1, 0, 1, 2, 0);
+ code_edit->select(3, 0, 4, 0, 1);
+ code_edit->select(0, 5, 0, 3, 2);
+
+ code_edit->cut();
+ CHECK(DS->clipboard_get() == "s \nso\nbe\n");
+ CHECK(code_edit->get_text() == "thiis\nme\ntext to\n\ncut");
+ CHECK(code_edit->get_caret_count() == 4);
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line(0) == 1);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->get_caret_line(1) == 3);
+ CHECK(code_edit->get_caret_column(1) == 0);
+ CHECK(code_edit->get_caret_line(2) == 0);
+ CHECK(code_edit->get_caret_column(2) == 3);
+ CHECK(code_edit->get_caret_line(3) == 2);
+ CHECK(code_edit->get_caret_column(3) == 0);
+ }
+
+ SUBCASE("[SceneTree][CodeEdit] new line") {
+ // Add a new line.
+ code_edit->set_text("test new line");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(13);
+ SEND_GUI_ACTION("ui_text_newline");
+ CHECK(code_edit->get_line(0) == "test new line");
+ CHECK(code_edit->get_line(1) == "");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Split line with new line.
+ code_edit->set_text("test new line");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(5);
+ SEND_GUI_ACTION("ui_text_newline");
+ CHECK(code_edit->get_line(0) == "test ");
+ CHECK(code_edit->get_line(1) == "new line");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Delete selection and split with new line.
+ code_edit->set_text("test new line");
+ code_edit->select(0, 0, 0, 5);
+ SEND_GUI_ACTION("ui_text_newline");
+ CHECK(code_edit->get_line(0) == "");
+ CHECK(code_edit->get_line(1) == "new line");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Blank new line below with selection should not split.
+ code_edit->set_text("test new line");
+ code_edit->select(0, 0, 0, 5);
+ SEND_GUI_ACTION("ui_text_newline_blank");
+ CHECK(code_edit->get_line(0) == "test new line");
+ CHECK(code_edit->get_line(1) == "");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Blank new line above with selection should not split.
+ code_edit->set_text("test new line");
+ code_edit->select(0, 0, 0, 5);
+ SEND_GUI_ACTION("ui_text_newline_above");
+ CHECK(code_edit->get_line(0) == "");
+ CHECK(code_edit->get_line(1) == "test new line");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Multiple new lines with multiple carets.
+ code_edit->set_text("test new line");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(5);
+ code_edit->add_caret(0, 8);
+ SEND_GUI_ACTION("ui_text_newline");
+ CHECK(code_edit->get_line(0) == "test ");
+ CHECK(code_edit->get_line(1) == "new");
+ CHECK(code_edit->get_line(2) == " line");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->get_caret_line(0) == 1);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->get_caret_line(1) == 2);
+ CHECK(code_edit->get_caret_column(1) == 0);
+
+ // Multiple blank new lines with multiple carets.
+ code_edit->set_text("test new line");
+ code_edit->remove_secondary_carets();
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(5);
+ code_edit->add_caret(0, 8);
+ SEND_GUI_ACTION("ui_text_newline_blank");
+ CHECK(code_edit->get_line(0) == "test new line");
+ CHECK(code_edit->get_line(1) == "");
+ CHECK(code_edit->get_line(2) == "");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->get_caret_line(0) == 2);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->get_caret_line(1) == 1);
+ CHECK(code_edit->get_caret_column(1) == 0);
+
+ // Multiple new lines above with multiple carets.
+ code_edit->set_text("test new line");
+ code_edit->remove_secondary_carets();
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(5);
+ code_edit->add_caret(0, 8);
+ SEND_GUI_ACTION("ui_text_newline_above");
+ CHECK(code_edit->get_line(0) == "");
+ CHECK(code_edit->get_line(1) == "");
+ CHECK(code_edit->get_line(2) == "test new line");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->get_caret_line(1) == 1);
+ CHECK(code_edit->get_caret_column(1) == 0);
+
+ // See '[CodeEdit] auto indent' tests for tests about new line with indentation.
+ }
+
+ SUBCASE("[SceneTree][CodeEdit] move lines up") {
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+
+ // Move line up with caret on it.
+ code_edit->set_caret_line(2);
+ code_edit->set_caret_column(1);
+ code_edit->move_lines_up();
+ CHECK(code_edit->get_text() == "test\nto\nlines\n\nmove\naround");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Undo.
+ code_edit->undo();
+ CHECK(code_edit->get_text() == "test\nlines\nto\n\nmove\naround");
+ CHECK(code_edit->get_caret_line() == 2);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Redo.
+ code_edit->redo();
+ CHECK(code_edit->get_text() == "test\nto\nlines\n\nmove\naround");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Does nothing at the first line.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(1);
+ code_edit->move_lines_up();
+ CHECK(code_edit->get_text() == "test\nlines\nto\n\nmove\naround");
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Works on empty line.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->set_caret_line(3);
+ code_edit->set_caret_column(0);
+ code_edit->move_lines_up();
+ CHECK(code_edit->get_text() == "test\nlines\n\nto\nmove\naround");
+ CHECK(code_edit->get_caret_line() == 2);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Move multiple lines up with selection.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(4, 0, 5, 1);
+ code_edit->move_lines_up();
+ CHECK(code_edit->get_text() == "test\nlines\nto\nmove\naround\n");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 3);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 4);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Does not affect line with selection end at column 0.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(4, 0, 5, 0);
+ code_edit->move_lines_up();
+ CHECK(code_edit->get_text() == "test\nlines\nto\nmove\n\naround");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 3);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 4);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Move multiple lines up with selection, right to left selection.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(5, 2, 4, 1);
+ code_edit->move_lines_up();
+ CHECK(code_edit->get_text() == "test\nlines\nto\nmove\naround\n");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 4);
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK(code_edit->get_caret_line() == 3);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Move multiple lines with multiple carets. A line with multiple carets is only moved once.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(5, 2, 5, 4);
+ code_edit->add_caret(4, 0);
+ code_edit->add_caret(4, 4);
+ code_edit->move_lines_up();
+ CHECK(code_edit->get_text() == "test\nlines\nto\nmove\naround\n");
+ CHECK(code_edit->get_caret_count() == 3);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 4);
+ CHECK(code_edit->get_selection_origin_column(0) == 2);
+ CHECK(code_edit->get_caret_line(0) == 4);
+ CHECK(code_edit->get_caret_column(0) == 4);
+ CHECK_FALSE(code_edit->has_selection(1));
+ CHECK(code_edit->get_caret_line(1) == 3);
+ CHECK(code_edit->get_caret_column(1) == 0);
+ CHECK_FALSE(code_edit->has_selection(2));
+ CHECK(code_edit->get_caret_line(2) == 3);
+ CHECK(code_edit->get_caret_column(2) == 4);
+
+ // Move multiple separate lines with multiple selections.
+ code_edit->remove_secondary_carets();
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(2, 2, 1, 4);
+ code_edit->add_caret(5, 0);
+ code_edit->select(5, 0, 5, 1, 1);
+ code_edit->move_lines_up();
+ CHECK(code_edit->get_text() == "lines\nto\ntest\n\naround\nmove");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 1);
+ CHECK(code_edit->get_selection_origin_column(0) == 2);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 4);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_selection_origin_line(1) == 4);
+ CHECK(code_edit->get_selection_origin_column(1) == 0);
+ CHECK(code_edit->get_caret_line(1) == 4);
+ CHECK(code_edit->get_caret_column(1) == 1);
+ }
+
+ SUBCASE("[SceneTree][CodeEdit] move lines down") {
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+
+ // Move line down with caret on it.
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(1);
+ code_edit->move_lines_down();
+ CHECK(code_edit->get_text() == "test\nto\nlines\n\nmove\naround");
+ CHECK(code_edit->get_caret_line() == 2);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Undo.
+ code_edit->undo();
+ CHECK(code_edit->get_text() == "test\nlines\nto\n\nmove\naround");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Redo.
+ code_edit->redo();
+ CHECK(code_edit->get_text() == "test\nto\nlines\n\nmove\naround");
+ CHECK(code_edit->get_caret_line() == 2);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Does nothing at the last line.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->set_caret_line(5);
+ code_edit->set_caret_column(1);
+ code_edit->move_lines_down();
+ CHECK(code_edit->get_text() == "test\nlines\nto\n\nmove\naround");
+ CHECK(code_edit->get_caret_line() == 5);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Works on empty line.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->set_caret_line(3);
+ code_edit->set_caret_column(0);
+ code_edit->move_lines_down();
+ CHECK(code_edit->get_text() == "test\nlines\nto\nmove\n\naround");
+ CHECK(code_edit->get_caret_line() == 4);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Move multiple lines down with selection.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(1, 0, 2, 1);
+ code_edit->move_lines_down();
+ CHECK(code_edit->get_text() == "test\n\nlines\nto\nmove\naround");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 2);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 3);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Does not affect line with selection end at column 0.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(1, 0, 2, 0);
+ code_edit->move_lines_down();
+ CHECK(code_edit->get_text() == "test\nto\nlines\n\nmove\naround");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 2);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->get_caret_line() == 3);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Move multiple lines down with selection, right to left selection.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(2, 2, 1, 1);
+ code_edit->move_lines_down();
+ CHECK(code_edit->get_text() == "test\n\nlines\nto\nmove\naround");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 3);
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK(code_edit->get_caret_line() == 2);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Move multiple lines with multiple carets. A line with multiple carets is only moved once.
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(1, 2, 1, 4);
+ code_edit->add_caret(0, 0);
+ code_edit->add_caret(0, 1);
+ code_edit->move_lines_down();
+ CHECK(code_edit->get_text() == "to\ntest\nlines\n\nmove\naround");
+ CHECK(code_edit->get_caret_count() == 3);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 2);
+ CHECK(code_edit->get_selection_origin_column(0) == 2);
+ CHECK(code_edit->get_caret_line(0) == 2);
+ CHECK(code_edit->get_caret_column(0) == 4);
+ CHECK_FALSE(code_edit->has_selection(1));
+ CHECK(code_edit->get_caret_line(1) == 1);
+ CHECK(code_edit->get_caret_column(1) == 0);
+ CHECK_FALSE(code_edit->has_selection(2));
+ CHECK(code_edit->get_caret_line(2) == 1);
+ CHECK(code_edit->get_caret_column(2) == 1);
+
+ // Move multiple separate lines with multiple selections.
+ code_edit->remove_secondary_carets();
+ code_edit->set_text("test\nlines\nto\n\nmove\naround");
+ code_edit->select(0, 2, 1, 4);
+ code_edit->add_caret(4, 0);
+ code_edit->select(4, 0, 4, 2, 1);
+ code_edit->move_lines_down();
+ CHECK(code_edit->get_text() == "to\ntest\nlines\n\naround\nmove");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 1);
+ CHECK(code_edit->get_selection_origin_column(0) == 2);
+ CHECK(code_edit->get_caret_line(0) == 2);
+ CHECK(code_edit->get_caret_column(0) == 4);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_selection_origin_line(1) == 5);
+ CHECK(code_edit->get_selection_origin_column(1) == 0);
+ CHECK(code_edit->get_caret_line(1) == 5);
+ CHECK(code_edit->get_caret_column(1) == 2);
+ }
+
+ SUBCASE("[SceneTree][CodeEdit] delete lines") {
+ code_edit->set_text("test\nlines\nto\n\ndelete");
+
+ // Delete line with caret on it.
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(1);
+ code_edit->delete_lines();
+ CHECK(code_edit->get_text() == "test\nto\n\ndelete");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Undo.
+ code_edit->undo();
+ CHECK(code_edit->get_text() == "test\nlines\nto\n\ndelete");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Redo.
+ code_edit->redo();
+ CHECK(code_edit->get_text() == "test\nto\n\ndelete");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Delete empty line.
+ code_edit->set_caret_line(2);
+ code_edit->set_caret_column(0);
+ code_edit->delete_lines();
+ CHECK(code_edit->get_text() == "test\nto\ndelete");
+ CHECK(code_edit->get_caret_line() == 2);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Deletes only one line when there are multiple carets on it. Carets move down and the column gets clamped.
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(0);
+ code_edit->add_caret(0, 1);
+ code_edit->add_caret(0, 4);
+ code_edit->delete_lines();
+ CHECK(code_edit->get_text() == "to\ndelete");
+ CHECK(code_edit->get_caret_count() == 3);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->get_caret_line(1) == 0);
+ CHECK(code_edit->get_caret_column(1) == 1);
+ CHECK(code_edit->get_caret_line(2) == 0);
+ CHECK(code_edit->get_caret_column(2) == 2);
+
+ // Delete multiple lines with selection.
+ code_edit->remove_secondary_carets();
+ code_edit->set_text("test\nlines\nto\n\ndelete");
+ code_edit->select(0, 1, 2, 1);
+ code_edit->delete_lines();
+ CHECK(code_edit->get_text() == "\ndelete");
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Does not affect line with selection end at column 0.
+ code_edit->set_text("test\nlines\nto\n\ndelete");
+ code_edit->select(0, 1, 1, 0);
+ code_edit->delete_lines();
+ CHECK(code_edit->get_text() == "lines\nto\n\ndelete");
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Delete multiple lines with multiple carets.
+ code_edit->set_text("test\nlines\nto\n\ndelete");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(2);
+ code_edit->add_caret(1, 0);
+ code_edit->add_caret(4, 5);
+ code_edit->delete_lines();
+ CHECK(code_edit->get_text() == "to\n");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 0);
+ CHECK(code_edit->get_caret_line(1) == 1);
+ CHECK(code_edit->get_caret_column(1) == 0);
+
+ // Delete multiple separate lines with multiple selections.
+ code_edit->remove_secondary_carets();
+ code_edit->set_text("test\nlines\nto\n\ndelete");
+ code_edit->add_caret(4, 5);
+ code_edit->select(0, 1, 1, 1);
+ code_edit->select(5, 5, 4, 0, 1);
+ code_edit->delete_lines();
+ CHECK(code_edit->get_text() == "to\n");
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->get_caret_line(0) == 0);
+ CHECK(code_edit->get_caret_column(0) == 1);
+ CHECK(code_edit->get_caret_line(1) == 1);
+ CHECK(code_edit->get_caret_column(1) == 0);
+
+ // Deletes contents when there is only one line.
+ code_edit->remove_secondary_carets();
+ code_edit->set_text("test");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(4);
+ code_edit->delete_lines();
+ CHECK(code_edit->get_text() == "");
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_caret_column() == 0);
+ }
+
+ SUBCASE("[SceneTree][CodeEdit] duplicate selection") {
+ code_edit->set_text("test\nlines\nto\n\nduplicate");
+
+ // Duplicate selected text.
+ code_edit->select(0, 1, 1, 2);
+ code_edit->duplicate_selection();
+ CHECK(code_edit->get_text() == "test\nliest\nlines\nto\n\nduplicate");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK(code_edit->get_caret_line() == 2);
+ CHECK(code_edit->get_caret_column() == 2);
+
+ // Undo.
+ code_edit->undo();
+ CHECK(code_edit->get_text() == "test\nlines\nto\n\nduplicate");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_selection_origin_column() == 1);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 2);
+
+ // Redo.
+ code_edit->redo();
+ CHECK(code_edit->get_text() == "test\nliest\nlines\nto\n\nduplicate");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_selection_origin_column() == 2);
+ CHECK(code_edit->get_caret_line() == 2);
+ CHECK(code_edit->get_caret_column() == 2);
+
+ // Duplicate selected text, right to left selection.
+ code_edit->set_text("test\nlines\nto\n\nduplicate");
+ code_edit->select(1, 1, 0, 2);
+ code_edit->duplicate_selection();
+ CHECK(code_edit->get_text() == "test\nlst\nlines\nto\n\nduplicate");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 2);
+ CHECK(code_edit->get_selection_origin_column() == 1);
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 1);
+
+ // Duplicate line if there is no selection.
+ code_edit->deselect();
+ code_edit->set_text("test\nlines\nto\n\nduplicate");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(2);
+ code_edit->duplicate_selection();
+ CHECK(code_edit->get_text() == "test\nlines\nlines\nto\n\nduplicate");
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line() == 2);
+ CHECK(code_edit->get_caret_column() == 2);
+
+ // Duplicate multiple lines.
+ code_edit->deselect();
+ code_edit->set_text("test\nlines\nto\n\nduplicate");
+ code_edit->set_caret_line(1);
+ code_edit->set_caret_column(2);
+ code_edit->add_caret(5, 0);
+ code_edit->add_caret(0, 4);
+ code_edit->duplicate_selection();
+ CHECK(code_edit->get_text() == "test\ntest\nlines\nlines\nto\n\nduplicate\nduplicate");
+ CHECK(code_edit->get_caret_count() == 3);
+ CHECK_FALSE(code_edit->has_selection());
+ CHECK(code_edit->get_caret_line(0) == 3);
+ CHECK(code_edit->get_caret_column(0) == 2);
+ CHECK(code_edit->get_caret_line(1) == 7);
+ CHECK(code_edit->get_caret_column(1) == 0);
+ CHECK(code_edit->get_caret_line(2) == 1);
+ CHECK(code_edit->get_caret_column(2) == 4);
+
+ // Duplicate multiple separate selections.
+ code_edit->remove_secondary_carets();
+ code_edit->set_text("test\nlines\nto\n\nduplicate");
+ code_edit->add_caret(4, 4);
+ code_edit->add_caret(0, 1);
+ code_edit->add_caret(0, 4);
+ code_edit->select(2, 0, 2, 1, 0);
+ code_edit->select(3, 0, 4, 4, 1);
+ code_edit->select(0, 1, 0, 0, 2);
+ code_edit->select(0, 2, 0, 4, 3);
+ code_edit->duplicate_selection();
+ CHECK(code_edit->get_text() == "ttestst\nlines\ntto\n\ndupl\nduplicate");
+ CHECK(code_edit->get_caret_count() == 4);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 2);
+ CHECK(code_edit->get_selection_origin_column(0) == 1);
+ CHECK(code_edit->get_caret_line(0) == 2);
+ CHECK(code_edit->get_caret_column(0) == 2);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_selection_origin_line(1) == 4);
+ CHECK(code_edit->get_selection_origin_column(1) == 4);
+ CHECK(code_edit->get_caret_line(1) == 5);
+ CHECK(code_edit->get_caret_column(1) == 4);
+ CHECK(code_edit->has_selection(2));
+ CHECK(code_edit->get_selection_origin_line(2) == 0);
+ CHECK(code_edit->get_selection_origin_column(2) == 2);
+ CHECK(code_edit->get_caret_line(2) == 0);
+ CHECK(code_edit->get_caret_column(2) == 1);
+ CHECK(code_edit->has_selection(3));
+ CHECK(code_edit->get_selection_origin_line(3) == 0);
+ CHECK(code_edit->get_selection_origin_column(3) == 5);
+ CHECK(code_edit->get_caret_line(3) == 0);
+ CHECK(code_edit->get_caret_column(3) == 7);
+
+ // Duplicate adjacent selections.
+ code_edit->remove_secondary_carets();
+ code_edit->set_text("test\nlines\nto\n\nduplicate");
+ code_edit->add_caret(1, 2);
+ code_edit->select(1, 0, 1, 1, 0);
+ code_edit->select(1, 1, 1, 4, 1);
+ code_edit->duplicate_selection();
+ CHECK(code_edit->get_text() == "test\nllineines\nto\n\nduplicate");
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 1);
+ CHECK(code_edit->get_selection_origin_column(0) == 1);
+ CHECK(code_edit->get_caret_line(0) == 1);
+ CHECK(code_edit->get_caret_column(0) == 2);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_selection_origin_line(1) == 1);
+ CHECK(code_edit->get_selection_origin_column(1) == 5);
+ CHECK(code_edit->get_caret_line(1) == 1);
+ CHECK(code_edit->get_caret_column(1) == 8);
+
+ // Duplicate lines then duplicate selections when there are both selections and non-selections.
+ code_edit->remove_secondary_carets();
+ code_edit->set_text("test duplicate");
+ code_edit->select(0, 14, 0, 13, 0);
+ code_edit->add_caret(0, 8);
+ code_edit->add_caret(0, 4);
+ code_edit->select(0, 2, 0, 4, 2);
+ code_edit->duplicate_selection();
+ CHECK(code_edit->get_text() == "test duplicate\ntestst duplicatee");
+ CHECK(code_edit->get_caret_count() == 3);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 1);
+ CHECK(code_edit->get_selection_origin_column(0) == 17);
+ CHECK(code_edit->get_caret_line(0) == 1);
+ CHECK(code_edit->get_caret_column(0) == 16);
+ CHECK_FALSE(code_edit->has_selection(1));
+ CHECK(code_edit->get_caret_line(1) == 1);
+ CHECK(code_edit->get_caret_column(1) == 10);
+ CHECK(code_edit->has_selection(2));
+ CHECK(code_edit->get_selection_origin_line(2) == 1);
+ CHECK(code_edit->get_selection_origin_column(2) == 4);
+ CHECK(code_edit->get_caret_line(2) == 1);
+ CHECK(code_edit->get_caret_column(2) == 6);
+ }
+
+ SUBCASE("[SceneTree][CodeEdit] duplicate lines") {
+ String reset_text = R"(extends Node
+
+func _ready():
+ var a := len(OS.get_cmdline_args())
+ var b := get_child_count()
+ var c := a + b
+ for i in range(c):
+ print("This is the solution: ", sin(i))
+ var pos = get_index() - 1
+ print("Make sure this exits: %b" % pos)
+)";
+
+ code_edit->set_text(reset_text);
- code_edit->set_text(R"(extends Node
+ // Duplicate a single line without selection.
+ code_edit->set_caret_line(0);
+ code_edit->duplicate_lines();
+ CHECK(code_edit->get_line(0) == "extends Node");
+ CHECK(code_edit->get_line(1) == "extends Node");
+ CHECK(code_edit->get_line(2) == "");
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ // Duplicate multiple lines with selection.
+ code_edit->set_text(reset_text);
+ code_edit->select(4, 8, 6, 15);
+ code_edit->duplicate_lines();
+ CHECK(code_edit->get_text() == R"(extends Node
+
+func _ready():
+ var a := len(OS.get_cmdline_args())
+ var b := get_child_count()
+ var c := a + b
+ for i in range(c):
+ var b := get_child_count()
+ var c := a + b
+ for i in range(c):
+ print("This is the solution: ", sin(i))
+ var pos = get_index() - 1
+ print("Make sure this exits: %b" % pos)
+)");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 7);
+ CHECK(code_edit->get_selection_origin_column() == 8);
+ CHECK(code_edit->get_caret_line() == 9);
+ CHECK(code_edit->get_caret_column() == 15);
+
+ // Duplicate multiple lines with right to left selection.
+ code_edit->set_text(reset_text);
+ code_edit->select(6, 15, 4, 8);
+ code_edit->duplicate_lines();
+ CHECK(code_edit->get_text() == R"(extends Node
func _ready():
var a := len(OS.get_cmdline_args())
var b := get_child_count()
var c := a + b
for i in range(c):
+ var b := get_child_count()
+ var c := a + b
+ for i in range(c):
print("This is the solution: ", sin(i))
var pos = get_index() - 1
print("Make sure this exits: %b" % pos)
)");
+ CHECK(code_edit->has_selection());
+ CHECK(code_edit->get_selection_origin_line() == 9);
+ CHECK(code_edit->get_selection_origin_column() == 15);
+ CHECK(code_edit->get_caret_line() == 7);
+ CHECK(code_edit->get_caret_column() == 8);
+
+ // Duplicate single lines with multiple carets. Multiple carets on a single line only duplicate once.
+ code_edit->remove_secondary_carets();
+ code_edit->deselect();
+ code_edit->set_text(reset_text);
+ code_edit->set_caret_line(3);
+ code_edit->set_caret_column(1);
+ code_edit->add_caret(5, 1);
+ code_edit->add_caret(5, 5);
+ code_edit->add_caret(4, 2);
+ code_edit->duplicate_lines();
+ CHECK(code_edit->get_text() == R"(extends Node
- /* Duplicate a single line without selection. */
- code_edit->set_caret_line(0);
- code_edit->duplicate_lines();
- CHECK(code_edit->get_line(0) == "extends Node");
- CHECK(code_edit->get_line(1) == "extends Node");
- CHECK(code_edit->get_line(2) == "");
-
- /* Duplicate multiple lines with selection. */
- code_edit->set_caret_line(6);
- code_edit->set_caret_column(15);
- code_edit->select(4, 8, 6, 15);
- code_edit->duplicate_lines();
- CHECK(code_edit->get_line(6) == "\tvar c := a + b");
- CHECK(code_edit->get_line(7) == "\tvar a := len(OS.get_cmdline_args())");
- CHECK(code_edit->get_line(8) == "\tvar b := get_child_count()");
- CHECK(code_edit->get_line(9) == "\tvar c := a + b");
- CHECK(code_edit->get_line(10) == "\tfor i in range(c):");
-
- /* Duplicate single lines with multiple carets. */
- code_edit->deselect();
- code_edit->set_caret_line(10);
- code_edit->set_caret_column(1);
- code_edit->add_caret(11, 2);
- code_edit->add_caret(12, 1);
- code_edit->duplicate_lines();
- CHECK(code_edit->get_line(9) == "\tvar c := a + b");
- CHECK(code_edit->get_line(10) == "\tfor i in range(c):");
- CHECK(code_edit->get_line(11) == "\tfor i in range(c):");
- CHECK(code_edit->get_line(12) == "\t\tprint(\"This is the solution: \", sin(i))");
- CHECK(code_edit->get_line(13) == "\t\tprint(\"This is the solution: \", sin(i))");
- CHECK(code_edit->get_line(14) == "\tvar pos = get_index() - 1");
- CHECK(code_edit->get_line(15) == "\tvar pos = get_index() - 1");
- CHECK(code_edit->get_line(16) == "\tprint(\"Make sure this exits: %b\" % pos)");
-
- /* Duplicate multiple lines with multiple carets. */
- code_edit->select(0, 0, 1, 2, 0);
- code_edit->select(3, 0, 4, 2, 1);
- code_edit->select(16, 0, 17, 0, 2);
- code_edit->set_caret_line(1, false, true, 0, 0);
- code_edit->set_caret_column(2, false, 0);
- code_edit->set_caret_line(4, false, true, 0, 1);
- code_edit->set_caret_column(2, false, 1);
- code_edit->set_caret_line(17, false, true, 0, 2);
- code_edit->set_caret_column(0, false, 2);
- code_edit->duplicate_lines();
- CHECK(code_edit->get_line(1) == "extends Node");
- CHECK(code_edit->get_line(2) == "extends Node");
- CHECK(code_edit->get_line(3) == "extends Node");
- CHECK(code_edit->get_line(4) == "");
- CHECK(code_edit->get_line(6) == "\tvar a := len(OS.get_cmdline_args())");
- CHECK(code_edit->get_line(7) == "func _ready():");
- CHECK(code_edit->get_line(8) == "\tvar a := len(OS.get_cmdline_args())");
- CHECK(code_edit->get_line(9) == "\tvar b := get_child_count()");
- CHECK(code_edit->get_line(20) == "\tprint(\"Make sure this exits: %b\" % pos)");
- CHECK(code_edit->get_line(21) == "");
- CHECK(code_edit->get_line(22) == "\tprint(\"Make sure this exits: %b\" % pos)");
- CHECK(code_edit->get_line(23) == "");
+func _ready():
+ var a := len(OS.get_cmdline_args())
+ var a := len(OS.get_cmdline_args())
+ var b := get_child_count()
+ var b := get_child_count()
+ var c := a + b
+ var c := a + b
+ for i in range(c):
+ print("This is the solution: ", sin(i))
+ var pos = get_index() - 1
+ print("Make sure this exits: %b" % pos)
+)");
+ CHECK(code_edit->get_caret_count() == 4);
+ CHECK_FALSE(code_edit->has_selection(0));
+ CHECK(code_edit->get_caret_line(0) == 4);
+ CHECK(code_edit->get_caret_column(0) == 1);
+ CHECK_FALSE(code_edit->has_selection(1));
+ CHECK(code_edit->get_caret_line(1) == 8);
+ CHECK(code_edit->get_caret_column(1) == 1);
+ CHECK_FALSE(code_edit->has_selection(2));
+ CHECK(code_edit->get_caret_line(2) == 8);
+ CHECK(code_edit->get_caret_column(2) == 5);
+ CHECK_FALSE(code_edit->has_selection(3));
+ CHECK(code_edit->get_caret_line(3) == 6);
+ CHECK(code_edit->get_caret_column(3) == 2);
+
+ // Duplicate multiple lines with multiple selections.
+ code_edit->remove_secondary_carets();
+ code_edit->set_text(reset_text);
+ code_edit->add_caret(4, 2);
+ code_edit->add_caret(6, 0);
+ code_edit->add_caret(7, 8);
+ code_edit->select(0, 0, 2, 5, 0);
+ code_edit->select(3, 0, 4, 2, 1);
+ code_edit->select(7, 1, 6, 0, 2);
+ code_edit->select(7, 3, 7, 8, 3);
+ code_edit->duplicate_lines();
+ CHECK(code_edit->get_text() == R"(extends Node
+
+func _ready():
+extends Node
+
+func _ready():
+ var a := len(OS.get_cmdline_args())
+ var b := get_child_count()
+ var a := len(OS.get_cmdline_args())
+ var b := get_child_count()
+ var c := a + b
+ for i in range(c):
+ print("This is the solution: ", sin(i))
+ for i in range(c):
+ print("This is the solution: ", sin(i))
+ var pos = get_index() - 1
+ print("Make sure this exits: %b" % pos)
+)");
+ CHECK(code_edit->get_caret_count() == 4);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_selection_origin_line(0) == 3);
+ CHECK(code_edit->get_selection_origin_column(0) == 0);
+ CHECK(code_edit->get_caret_line(0) == 5);
+ CHECK(code_edit->get_caret_column(0) == 5);
+
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_selection_origin_line(1) == 8);
+ CHECK(code_edit->get_selection_origin_column(1) == 0);
+ CHECK(code_edit->get_caret_line(1) == 9);
+ CHECK(code_edit->get_caret_column(1) == 2);
+
+ CHECK(code_edit->has_selection(2));
+ CHECK(code_edit->get_selection_origin_line(2) == 14);
+ CHECK(code_edit->get_selection_origin_column(2) == 1);
+ CHECK(code_edit->get_caret_line(2) == 13);
+ CHECK(code_edit->get_caret_column(2) == 0);
+
+ CHECK(code_edit->has_selection(3));
+ CHECK(code_edit->get_selection_origin_line(3) == 14);
+ CHECK(code_edit->get_selection_origin_column(3) == 3);
+ CHECK(code_edit->get_caret_line(3) == 14);
+ CHECK(code_edit->get_caret_column(3) == 8);
+ }
memdelete(code_edit);
}
diff --git a/tests/scene/test_curve_2d.h b/tests/scene/test_curve_2d.h
index 099f6fefa9..1248632630 100644
--- a/tests/scene/test_curve_2d.h
+++ b/tests/scene/test_curve_2d.h
@@ -147,13 +147,19 @@ TEST_CASE("[Curve2D] Sampling") {
CHECK(curve->samplef(1) == Vector2(0, 50));
}
- SUBCASE("sample_baked") {
+ SUBCASE("sample_baked, cubic = false") {
CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 0))) == Vector2(0, 0));
CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 25))) == Vector2(0, 25));
CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 50))) == Vector2(0, 50));
}
- SUBCASE("sample_baked_with_rotation") {
+ SUBCASE("sample_baked, cubic = true") {
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 0)), true) == Vector2(0, 0));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 25)), true) == Vector2(0, 25));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 50)), true) == Vector2(0, 50));
+ }
+
+ SUBCASE("sample_baked_with_rotation, cubic = false") {
const real_t pi = 3.14159;
const real_t half_pi = pi * 0.5;
Ref<Curve2D> rot_curve = memnew(Curve2D);
@@ -188,6 +194,41 @@ TEST_CASE("[Curve2D] Sampling") {
CHECK(Math::is_equal_approx(t.get_rotation(), -half_pi));
}
+ SUBCASE("sample_baked_with_rotation, cubic = true") {
+ const real_t pi = 3.14159;
+ const real_t half_pi = pi * 0.5;
+ Ref<Curve2D> rot_curve = memnew(Curve2D);
+ Transform2D t;
+
+ rot_curve->clear_points();
+ rot_curve->add_point(Vector2());
+ rot_curve->add_point(Vector2(50, 0));
+ t = rot_curve->sample_baked_with_rotation(25, true);
+ CHECK(t.get_origin() == Vector2(25, 0));
+ CHECK(Math::is_equal_approx(t.get_rotation(), 0));
+
+ rot_curve->clear_points();
+ rot_curve->add_point(Vector2());
+ rot_curve->add_point(Vector2(0, 50));
+ t = rot_curve->sample_baked_with_rotation(25, true);
+ CHECK(t.get_origin() == Vector2(0, 25));
+ CHECK(Math::is_equal_approx(t.get_rotation(), half_pi));
+
+ rot_curve->clear_points();
+ rot_curve->add_point(Vector2());
+ rot_curve->add_point(Vector2(-50, 0));
+ t = rot_curve->sample_baked_with_rotation(25, true);
+ CHECK(t.get_origin() == Vector2(-25, 0));
+ CHECK(Math::is_equal_approx(t.get_rotation(), pi));
+
+ rot_curve->clear_points();
+ rot_curve->add_point(Vector2());
+ rot_curve->add_point(Vector2(0, -50));
+ t = rot_curve->sample_baked_with_rotation(25, true);
+ CHECK(t.get_origin() == Vector2(0, -25));
+ CHECK(Math::is_equal_approx(t.get_rotation(), -half_pi));
+ }
+
SUBCASE("get_closest_point") {
CHECK(curve->get_closest_point(Vector2(0, 0)) == Vector2(0, 0));
CHECK(curve->get_closest_point(Vector2(0, 25)) == Vector2(0, 25));
diff --git a/tests/scene/test_curve_3d.h b/tests/scene/test_curve_3d.h
index d73bb1ad35..2e60a9c6e6 100644
--- a/tests/scene/test_curve_3d.h
+++ b/tests/scene/test_curve_3d.h
@@ -177,12 +177,30 @@ TEST_CASE("[Curve3D] Sampling") {
CHECK(curve->sample_baked(curve->get_closest_offset(Vector3(0, 50, 0)), true) == Vector3(0, 50, 0));
}
- SUBCASE("sample_baked_with_rotation") {
+ SUBCASE("sample_baked_with_rotation, cubic = false, p_apply_tilt = false") {
CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0)));
CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0)));
CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0)));
}
+ SUBCASE("sample_baked_with_rotation, cubic = false, p_apply_tilt = true") {
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0)), false, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0)), false, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0)), false, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0)));
+ }
+
+ SUBCASE("sample_baked_with_rotation, cubic = true, p_apply_tilt = false") {
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0)), true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0)), true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0)), true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0)));
+ }
+
+ SUBCASE("sample_baked_with_rotation, cubic = true, p_apply_tilt = true") {
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0)), true, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0)), true, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0)), true, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0)));
+ }
+
SUBCASE("sample_baked_tilt") {
CHECK(curve->sample_baked_tilt(curve->get_closest_offset(Vector3(0, 0, 0))) == 0);
CHECK(curve->sample_baked_tilt(curve->get_closest_offset(Vector3(0, 25, 0))) == 0);
diff --git a/tests/scene/test_graph_node.h b/tests/scene/test_graph_node.h
new file mode 100644
index 0000000000..72b8b682c9
--- /dev/null
+++ b/tests/scene/test_graph_node.h
@@ -0,0 +1,59 @@
+/**************************************************************************/
+/* test_graph_node.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_GRAPH_NODE_H
+#define TEST_GRAPH_NODE_H
+
+#include "scene/gui/graph_node.h"
+#include "scene/main/window.h"
+
+#include "tests/test_macros.h"
+
+namespace TestGraphNode {
+
+TEST_CASE("[GraphNode][SceneTree]") {
+ SUBCASE("[GraphNode] Graph Node only child on delete should not cause error.") {
+ // Setup.
+ GraphNode *test_node = memnew(GraphNode);
+ test_child->set_name("Graph Node");
+ Control *test_child = memnew(Control);
+ test_child->set_name("child");
+ test_node->add_child(test_child);
+
+ // Test.
+ CHECK_NOTHROW_MESSAGE(test_node->remove_child(test_child));
+
+ memdelete(test_node);
+ }
+}
+
+} // namespace TestGraphNode
+
+#endif // TEST_GRAPH_NODE_H
diff --git a/tests/scene/test_instance_placeholder.h b/tests/scene/test_instance_placeholder.h
new file mode 100644
index 0000000000..8e8cf7c9df
--- /dev/null
+++ b/tests/scene/test_instance_placeholder.h
@@ -0,0 +1,532 @@
+/**************************************************************************/
+/* test_instance_placeholder.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_INSTANCE_PLACEHOLDER_H
+#define TEST_INSTANCE_PLACEHOLDER_H
+
+#include "scene/main/instance_placeholder.h"
+#include "scene/resources/packed_scene.h"
+
+#include "tests/test_macros.h"
+
+class _TestInstancePlaceholderNode : public Node {
+ GDCLASS(_TestInstancePlaceholderNode, Node);
+
+protected:
+ static void _bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_int_property", "int_property"), &_TestInstancePlaceholderNode::set_int_property);
+ ClassDB::bind_method(D_METHOD("get_int_property"), &_TestInstancePlaceholderNode::get_int_property);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "int_property"), "set_int_property", "get_int_property");
+
+ ClassDB::bind_method(D_METHOD("set_reference_property", "reference_property"), &_TestInstancePlaceholderNode::set_reference_property);
+ ClassDB::bind_method(D_METHOD("get_reference_property"), &_TestInstancePlaceholderNode::get_reference_property);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "reference_property", PROPERTY_HINT_NODE_TYPE), "set_reference_property", "get_reference_property");
+
+ ClassDB::bind_method(D_METHOD("set_reference_array_property", "reference_array_property"), &_TestInstancePlaceholderNode::set_reference_array_property);
+ ClassDB::bind_method(D_METHOD("get_reference_array_property"), &_TestInstancePlaceholderNode::get_reference_array_property);
+
+ // The hint string value "24/34:Node" is determined from existing PackedScenes with typed Array properties.
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "reference_array_property", PROPERTY_HINT_TYPE_STRING, "24/34:Node"), "set_reference_array_property", "get_reference_array_property");
+ }
+
+public:
+ int int_property = 0;
+
+ void set_int_property(int p_int) {
+ int_property = p_int;
+ }
+
+ int get_int_property() const {
+ return int_property;
+ }
+
+ Variant reference_property;
+
+ void set_reference_property(const Variant &p_node) {
+ reference_property = p_node;
+ }
+
+ Variant get_reference_property() const {
+ return reference_property;
+ }
+
+ Array reference_array_property;
+
+ void set_reference_array_property(const Array &p_array) {
+ reference_array_property = p_array;
+ }
+
+ Array get_reference_array_property() const {
+ return reference_array_property;
+ }
+
+ _TestInstancePlaceholderNode() {
+ reference_array_property.set_typed(Variant::OBJECT, "Node", Variant());
+ }
+};
+
+namespace TestInstancePlaceholder {
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with no overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ SUBCASE("with non-node values") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ scene->set_int_property(12);
+
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_int_property() == 12);
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node value") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced = memnew(Node);
+ scene->add_child(referenced);
+ referenced->set_owner(scene);
+ scene->set_reference_property(referenced);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 1);
+ CHECK(created->get_reference_property().identity_compare(created->get_child(0, false)));
+ CHECK_FALSE(created->get_reference_property().identity_compare(referenced));
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node-array value") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced1 = memnew(Node);
+ Node *referenced2 = memnew(Node);
+ scene->add_child(referenced1);
+ scene->add_child(referenced2);
+ referenced1->set_owner(scene);
+ referenced2->set_owner(scene);
+ Array node_array;
+ node_array.set_typed(Variant::OBJECT, "Node", Variant());
+ node_array.push_back(referenced1);
+ node_array.push_back(referenced2);
+ scene->set_reference_array_property(node_array);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 2);
+ Array created_array = created->get_reference_array_property();
+ REQUIRE(created_array.size() == node_array.size());
+ REQUIRE(created_array.size() == created->get_child_count());
+
+ // Iterate over all nodes, since the ordering is not guaranteed.
+ for (int i = 0; i < node_array.size(); i++) {
+ bool node_found = false;
+ for (int j = 0; j < created->get_child_count(); j++) {
+ if (created_array[i].identity_compare(created->get_child(j, true))) {
+ node_found = true;
+ }
+ }
+ CHECK(node_found);
+ }
+ root->queue_free();
+ memdelete(scene);
+ }
+}
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ SUBCASE("with non-node values") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ ip->set_name("TestScene");
+ ip->set("int_property", 45);
+ // Create a scene to pack.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ scene->set_int_property(12);
+
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ packed_scene->pack(scene);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_int_property() == 45);
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node values") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ Node *overriding = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ root->add_child(overriding);
+ ip->set("reference_property", overriding);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced = memnew(Node);
+ scene->add_child(referenced);
+ referenced->set_owner(scene);
+ scene->set_reference_property(referenced);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 1);
+ CHECK(created->get_reference_property().identity_compare(overriding));
+ CHECK_FALSE(created->get_reference_property().identity_compare(referenced));
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node-array value") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ Node *override1 = memnew(Node);
+ Node *override2 = memnew(Node);
+ Node *override3 = memnew(Node);
+ root->add_child(ip);
+ root->add_child(override1);
+ root->add_child(override2);
+ root->add_child(override3);
+
+ Array override_node_array;
+ override_node_array.set_typed(Variant::OBJECT, "Node", Variant());
+ override_node_array.push_back(override1);
+ override_node_array.push_back(override2);
+ override_node_array.push_back(override3);
+
+ ip->set("reference_array_property", override_node_array);
+
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced1 = memnew(Node);
+ Node *referenced2 = memnew(Node);
+
+ scene->add_child(referenced1);
+ scene->add_child(referenced2);
+
+ referenced1->set_owner(scene);
+ referenced2->set_owner(scene);
+ Array referenced_array;
+ referenced_array.set_typed(Variant::OBJECT, "Node", Variant());
+ referenced_array.push_back(referenced1);
+ referenced_array.push_back(referenced2);
+
+ scene->set_reference_array_property(referenced_array);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 2);
+ Array created_array = created->get_reference_array_property();
+ REQUIRE_FALSE(created_array.size() == referenced_array.size());
+ REQUIRE(created_array.size() == override_node_array.size());
+ REQUIRE_FALSE(created_array.size() == created->get_child_count());
+
+ // Iterate over all nodes, since the ordering is not guaranteed.
+ for (int i = 0; i < override_node_array.size(); i++) {
+ bool node_found = false;
+ for (int j = 0; j < created_array.size(); j++) {
+ if (override_node_array[i].identity_compare(created_array[j])) {
+ node_found = true;
+ }
+ }
+ CHECK(node_found);
+ }
+ root->queue_free();
+ memdelete(scene);
+ }
+}
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with no overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ // Create the internal scene.
+ _TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);
+ internal->set_name("InternalNode");
+ Node *referenced = memnew(Node);
+ referenced->set_name("OriginalReference");
+ internal->add_child(referenced);
+ referenced->set_owner(internal);
+ internal->set_reference_property(referenced);
+
+ // Pack the internal scene.
+ PackedScene *internal_scene = memnew(PackedScene);
+ Error err = internal_scene->pack(internal);
+ REQUIRE(err == OK);
+
+ const String internal_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_internal.tscn");
+ err = ResourceSaver::save(internal_scene, internal_path);
+ REQUIRE(err == OK);
+
+ Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ // Create the main scene.
+ Node *root = memnew(Node);
+ root->set_name("MainNode");
+ Node *overriding = memnew(Node);
+ overriding->set_name("OverridingReference");
+
+ _TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));
+ internal_created->set_scene_instance_load_placeholder(true);
+ root->add_child(internal_created);
+ internal_created->set_owner(root);
+
+ root->add_child(overriding);
+ overriding->set_owner(root);
+ // Here we introduce an error, we override the property with an internal node to the instance placeholder.
+ // The InstancePlaceholder is now forced to properly resolve the Node.
+ internal_created->set("reference_property", NodePath("OriginalReference"));
+
+ // Pack the main scene.
+ PackedScene *main_scene = memnew(PackedScene);
+ err = main_scene->pack(root);
+ REQUIRE(err == OK);
+
+ const String main_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_main.tscn");
+ err = ResourceSaver::save(main_scene, main_path);
+ REQUIRE(err == OK);
+
+ // // Instantiate the scene.
+ Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ Node *instanced_main_node = main_scene_loaded->instantiate();
+ REQUIRE(instanced_main_node != nullptr);
+ SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);
+ CHECK(instanced_main_node->get_name() == "MainNode");
+ REQUIRE(instanced_main_node->get_child_count() == 2);
+ InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));
+ REQUIRE(instanced_placeholder != nullptr);
+
+ _TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));
+ REQUIRE(final_node != nullptr);
+ REQUIRE(final_node->get_child_count() == 1);
+ REQUIRE(final_node->get_reference_property().identity_compare(final_node->get_child(0, true)));
+
+ instanced_main_node->queue_free();
+ memdelete(overriding);
+ memdelete(root);
+ memdelete(internal);
+ DirAccess::remove_file_or_error(internal_path);
+ DirAccess::remove_file_or_error(main_path);
+}
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ // Create the internal scene.
+ _TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);
+ internal->set_name("InternalNode");
+ Node *referenced = memnew(Node);
+ referenced->set_name("OriginalReference");
+ internal->add_child(referenced);
+ referenced->set_owner(internal);
+ internal->set_reference_property(referenced);
+
+ Node *array_ref1 = memnew(Node);
+ array_ref1->set_name("ArrayRef1");
+ internal->add_child(array_ref1);
+ array_ref1->set_owner(internal);
+ Node *array_ref2 = memnew(Node);
+ array_ref2->set_name("ArrayRef2");
+ internal->add_child(array_ref2);
+ array_ref2->set_owner(internal);
+ Array referenced_array;
+ referenced_array.set_typed(Variant::OBJECT, "Node", Variant());
+ referenced_array.push_back(array_ref1);
+ referenced_array.push_back(array_ref2);
+ internal->set_reference_array_property(referenced_array);
+
+ // Pack the internal scene.
+ PackedScene *internal_scene = memnew(PackedScene);
+ Error err = internal_scene->pack(internal);
+ REQUIRE(err == OK);
+
+ const String internal_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_internal_override.tscn");
+ err = ResourceSaver::save(internal_scene, internal_path);
+ REQUIRE(err == OK);
+
+ Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ // Create the main scene.
+ Node *root = memnew(Node);
+ root->set_name("MainNode");
+ Node *overriding = memnew(Node);
+ overriding->set_name("OverridingReference");
+ Node *array_ext = memnew(Node);
+ array_ext->set_name("ExternalArrayMember");
+
+ _TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));
+ internal_created->set_scene_instance_load_placeholder(true);
+ root->add_child(internal_created);
+ internal_created->set_owner(root);
+
+ root->add_child(overriding);
+ overriding->set_owner(root);
+ root->add_child(array_ext);
+ array_ext->set_owner(root);
+ // Here we introduce an error, we override the property with an internal node to the instance placeholder.
+ // The InstancePlaceholder is now forced to properly resolve the Node.
+ internal_created->set_reference_property(overriding);
+ Array internal_array = internal_created->get_reference_array_property();
+ Array override_array;
+ override_array.set_typed(Variant::OBJECT, "Node", Variant());
+ for (int i = 0; i < internal_array.size(); i++) {
+ override_array.push_back(internal_array[i]);
+ }
+ override_array.push_back(array_ext);
+ internal_created->set_reference_array_property(override_array);
+
+ // Pack the main scene.
+ PackedScene *main_scene = memnew(PackedScene);
+ err = main_scene->pack(root);
+ REQUIRE(err == OK);
+
+ const String main_path = OS::get_singleton()->get_cache_path().path_join("instance_placeholder_test_main_override.tscn");
+ err = ResourceSaver::save(main_scene, main_path);
+ REQUIRE(err == OK);
+
+ // // Instantiate the scene.
+ Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ Node *instanced_main_node = main_scene_loaded->instantiate();
+ REQUIRE(instanced_main_node != nullptr);
+ SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);
+ CHECK(instanced_main_node->get_name() == "MainNode");
+ REQUIRE(instanced_main_node->get_child_count() == 3);
+ InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));
+ REQUIRE(instanced_placeholder != nullptr);
+
+ _TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));
+ REQUIRE(final_node != nullptr);
+ REQUIRE(final_node->get_child_count() == 3);
+ REQUIRE(final_node->get_reference_property().identity_compare(instanced_main_node->get_child(1, true)));
+ Array final_array = final_node->get_reference_array_property();
+ REQUIRE(final_array.size() == 3);
+ Array wanted_node_array;
+ wanted_node_array.push_back(instanced_main_node->get_child(2, true)); // ExternalArrayMember
+ wanted_node_array.push_back(final_node->get_child(1, true)); // ArrayRef1
+ wanted_node_array.push_back(final_node->get_child(2, true)); // ArrayRef2
+
+ // Iterate over all nodes, since the ordering is not guaranteed.
+ for (int i = 0; i < wanted_node_array.size(); i++) {
+ bool node_found = false;
+ for (int j = 0; j < final_array.size(); j++) {
+ if (wanted_node_array[i].identity_compare(final_array[j])) {
+ node_found = true;
+ }
+ }
+ CHECK(node_found);
+ }
+
+ instanced_main_node->queue_free();
+ memdelete(array_ext);
+ memdelete(overriding);
+ memdelete(root);
+ memdelete(internal);
+ DirAccess::remove_file_or_error(internal_path);
+ DirAccess::remove_file_or_error(main_path);
+}
+
+} //namespace TestInstancePlaceholder
+
+#endif // TEST_INSTANCE_PLACEHOLDER_H
diff --git a/tests/scene/test_navigation_region_2d.h b/tests/scene/test_navigation_region_2d.h
index 4574893c8d..fcb5aeacbe 100644
--- a/tests/scene/test_navigation_region_2d.h
+++ b/tests/scene/test_navigation_region_2d.h
@@ -41,7 +41,7 @@ namespace TestNavigationRegion2D {
TEST_SUITE("[Navigation]") {
TEST_CASE("[SceneTree][NavigationRegion2D] New region should have valid RID") {
NavigationRegion2D *region_node = memnew(NavigationRegion2D);
- CHECK(region_node->get_region_rid().is_valid());
+ CHECK(region_node->get_rid().is_valid());
memdelete(region_node);
}
}
diff --git a/tests/scene/test_navigation_region_3d.h b/tests/scene/test_navigation_region_3d.h
index 372f6dc505..f3d7f27361 100644
--- a/tests/scene/test_navigation_region_3d.h
+++ b/tests/scene/test_navigation_region_3d.h
@@ -43,7 +43,7 @@ namespace TestNavigationRegion3D {
TEST_SUITE("[Navigation]") {
TEST_CASE("[SceneTree][NavigationRegion3D] New region should have valid RID") {
NavigationRegion3D *region_node = memnew(NavigationRegion3D);
- CHECK(region_node->get_region_rid().is_valid());
+ CHECK(region_node->get_rid().is_valid());
memdelete(region_node);
}
@@ -65,21 +65,14 @@ TEST_SUITE("[Navigation]") {
CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
SUBCASE("Synchronous bake should have immediate effects") {
+ ERR_PRINT_OFF; // Suppress warning about baking from visual meshes as source geometry.
navigation_region->bake_navigation_mesh(false);
+ ERR_PRINT_ON;
CHECK_FALSE(navigation_region->is_baking());
CHECK_NE(navigation_mesh->get_polygon_count(), 0);
CHECK_NE(navigation_mesh->get_vertices().size(), 0);
}
- // Race condition is present in the below subcase, but baking should take many
- // orders of magnitude longer than basic checks on the main thread, so it's fine.
- SUBCASE("Asynchronous bake should not be immediate") {
- navigation_region->bake_navigation_mesh(true);
- CHECK(navigation_region->is_baking());
- CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
- CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
- }
-
memdelete(mesh_instance);
memdelete(navigation_region);
memdelete(node_3d);
diff --git a/tests/scene/test_sprite_frames.h b/tests/scene/test_sprite_frames.h
index bf127cd42c..55854b90e4 100644
--- a/tests/scene/test_sprite_frames.h
+++ b/tests/scene/test_sprite_frames.h
@@ -74,9 +74,10 @@ TEST_CASE("[SpriteFrames] Animation addition, list getter, renaming, removal, an
sname_list.size() == test_names.size(),
"StringName List getter returned list of expected size");
- for (int i = 0; i < test_names.size(); i++) {
+ int idx = 0;
+ for (List<StringName>::ConstIterator itr = sname_list.begin(); itr != sname_list.end(); ++itr, ++idx) {
CHECK_MESSAGE(
- sname_list[i] == StringName(test_names[i]),
+ *itr == StringName(test_names[idx]),
"StringName List getter returned expected values");
}
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index 8577dd7148..b2d9f5100e 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -36,6 +36,23 @@
#include "tests/test_macros.h"
namespace TestTextEdit {
+static inline Array build_array() {
+ return Array();
+}
+template <typename... Targs>
+static inline Array build_array(Variant item, Targs... Fargs) {
+ Array a = build_array(Fargs...);
+ a.push_front(item);
+ return a;
+}
+static inline Array reverse_nested(Array array) {
+ Array reversed_array = array.duplicate(true);
+ reversed_array.reverse();
+ for (int i = 0; i < reversed_array.size(); i++) {
+ ((Array)reversed_array[i]).reverse();
+ }
+ return reversed_array;
+}
TEST_CASE("[SceneTree][TextEdit] text entry") {
SceneTree::get_singleton()->get_root()->set_physics_object_picking(false);
@@ -52,12 +69,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_WATCH(text_edit, "lines_edited_from");
SIGNAL_WATCH(text_edit, "caret_changed");
- Array args1;
- args1.push_back(0);
- args1.push_back(0);
- Array lines_edited_args;
- lines_edited_args.push_back(args1);
- lines_edited_args.push_back(args1.duplicate());
+ Array lines_edited_args = build_array(build_array(0, 0), build_array(0, 0));
SUBCASE("[TextEdit] clear and set text") {
// "text_changed" should not be emitted on clear / set.
@@ -119,13 +131,10 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_set");
- // Clear.
+ // Can clear even if not editable.
text_edit->set_editable(false);
- Array lines_edited_clear_args;
- Array new_args = args1.duplicate();
- new_args[0] = 1;
- lines_edited_clear_args.push_back(new_args);
+ Array lines_edited_clear_args = build_array(build_array(1, 0));
text_edit->clear();
MessageQueue::get_singleton()->flush();
@@ -210,6 +219,321 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("text_changed");
}
+ SUBCASE("[TextEdit] insert text") {
+ // insert_text is 0 indexed.
+ ERR_PRINT_OFF;
+ text_edit->insert_text("test", 1, 0);
+ ERR_PRINT_ON;
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Insert text when there is no text.
+ lines_edited_args = build_array(build_array(0, 0));
+
+ text_edit->insert_text("tes", 0, 0);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "tes");
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 3);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Insert multiple lines.
+ lines_edited_args = build_array(build_array(0, 1));
+
+ text_edit->insert_text("t\ninserting text", 0, 3);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\ninserting text");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 14);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Can insert even if not editable.
+ lines_edited_args = build_array(build_array(1, 1));
+
+ text_edit->set_editable(false);
+ text_edit->insert_text("mid", 1, 2);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\ninmidserting text");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 17);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->set_editable(true);
+
+ // Undo insert.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\ninserting text");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 14);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Redo insert.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\ninmidserting text");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 17);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Insert offsets carets after the edit.
+ text_edit->add_caret(1, 1);
+ text_edit->add_caret(1, 4);
+ text_edit->select(1, 4, 1, 6, 2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 2));
+
+ text_edit->insert_text("\n ", 1, 2);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\nin\n midserting text");
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 2);
+ CHECK(text_edit->get_caret_column(0) == 16);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 1);
+ CHECK(text_edit->has_selection(2));
+ CHECK(text_edit->get_caret_line(2) == 2);
+ CHECK(text_edit->get_caret_column(2) == 5);
+ CHECK(text_edit->get_selection_origin_line(2) == 2);
+ CHECK(text_edit->get_selection_origin_column(2) == 3);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+
+ // Insert text outside of selections.
+ text_edit->set_text("test text");
+ text_edit->add_caret(0, 8);
+ text_edit->select(0, 1, 0, 4, 0);
+ text_edit->select(0, 4, 0, 8, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0));
+
+ text_edit->insert_text("a", 0, 4, true, false);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "testa text");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 1);
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 9);
+ CHECK(text_edit->get_selection_origin_line(1) == 0);
+ CHECK(text_edit->get_selection_origin_column(1) == 5);
+
+ // Insert text to beginning of selections.
+ text_edit->set_text("test text");
+ text_edit->add_caret(0, 8);
+ text_edit->select(0, 1, 0, 4, 0);
+ text_edit->select(0, 4, 0, 8, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0));
+
+ text_edit->insert_text("a", 0, 4, false, false);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "testa text");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 1);
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 9);
+ CHECK(text_edit->get_selection_origin_line(1) == 0);
+ CHECK(text_edit->get_selection_origin_column(1) == 4);
+
+ // Insert text to end of selections.
+ text_edit->set_text("test text");
+ text_edit->add_caret(0, 8);
+ text_edit->select(0, 1, 0, 4, 0);
+ text_edit->select(0, 4, 0, 8, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0));
+
+ text_edit->insert_text("a", 0, 4, true, true);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "testa text");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 1);
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 9);
+ CHECK(text_edit->get_selection_origin_line(1) == 0);
+ CHECK(text_edit->get_selection_origin_column(1) == 5);
+
+ // Insert text inside of selections.
+ text_edit->set_text("test text");
+ text_edit->add_caret(0, 8);
+ text_edit->select(0, 1, 0, 4, 0);
+ text_edit->select(0, 4, 0, 8, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0));
+
+ text_edit->insert_text("a", 0, 4, false, true);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "testa text");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 9);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 1);
+ }
+
+ SUBCASE("[TextEdit] remove text") {
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 2));
+
+ text_edit->set_text("test\nremoveing text\nthird line");
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_CHECK("text_set", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_changed");
+
+ // remove_text is 0 indexed.
+ ERR_PRINT_OFF;
+ text_edit->remove_text(3, 0, 3, 4);
+ ERR_PRINT_ON;
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\nremoveing text\nthird line");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Remove multiple lines.
+ text_edit->set_caret_line(2);
+ text_edit->set_caret_column(10);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(2, 1));
+
+ text_edit->remove_text(1, 9, 2, 2);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\nremoveingird line");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 17);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Can remove even if not editable.
+ lines_edited_args = build_array(build_array(1, 1));
+
+ text_edit->set_editable(false);
+ text_edit->remove_text(1, 5, 1, 6);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\nremovingird line");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 16);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->set_editable(true);
+
+ // Undo remove.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\nremoveingird line");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 17);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Redo remove.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\nremovingird line");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 16);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Remove collapses carets and offsets carets after the edit.
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(9);
+ text_edit->add_caret(1, 10);
+ text_edit->select(1, 10, 1, 13, 1);
+ text_edit->add_caret(1, 14);
+ text_edit->add_caret(1, 2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+
+ text_edit->remove_text(1, 8, 1, 11);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test\nremoving line");
+ // Caret 0 was merged into the selection.
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 10);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 8);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 11);
+ CHECK(text_edit->get_caret_line(2) == 1);
+ CHECK(text_edit->get_caret_column(2) == 2);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->remove_secondary_carets();
+ }
+
SUBCASE("[TextEdit] set and get line") {
// Set / Get line is 0 indexed.
text_edit->set_line(1, "test");
@@ -225,6 +549,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_text() == "test");
CHECK(text_edit->get_line(0) == "test");
CHECK(text_edit->get_line(1) == "");
+ CHECK(text_edit->get_line_count() == 1);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
@@ -233,14 +558,15 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
// Setting to a longer line, caret and selections should be preserved.
text_edit->select_all();
MessageQueue::get_singleton()->flush();
- CHECK(text_edit->has_selection());
- SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_DISCARD("caret_changed");
text_edit->set_line(0, "test text");
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_line(0) == "test text");
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "test");
+ CHECK(text_edit->get_selection_origin_column() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("caret_changed");
@@ -299,12 +625,84 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("text_set");
-
ERR_PRINT_ON;
+
+ // Both ends of selection are adjusted and deselects.
+ text_edit->set_text("test text");
+ text_edit->select(0, 8, 0, 6);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->set_line(0, "test");
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_line(0) == "test");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_column() == 4);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Multiple carets adjust to keep visual position.
+ text_edit->set_text("test text");
+ text_edit->set_caret_column(2);
+ text_edit->add_caret(0, 0);
+ text_edit->add_caret(0, 1);
+ text_edit->add_caret(0, 6);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->set_line(0, "\tset line");
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_line(0) == "\tset line");
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK_FALSE(text_edit->has_selection());
+ // In the default font, these are the same positions.
+ CHECK(text_edit->get_caret_column(0) == 1);
+ CHECK(text_edit->get_caret_column(1) == 0);
+ // The previous caret at index 2 was merged.
+ CHECK(text_edit->get_caret_column(2) == 4);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->remove_secondary_carets();
+
+ // Insert multiple lines.
+ text_edit->set_text("test text\nsecond line");
+ text_edit->set_caret_column(5);
+ text_edit->add_caret(1, 6);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 1));
+
+ text_edit->set_line(0, "multiple\nlines");
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "multiple\nlines\nsecond line");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 3); // In the default font, this is the same position.
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 6);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->remove_secondary_carets();
}
SUBCASE("[TextEdit] swap lines") {
- ((Array)lines_edited_args[1])[1] = 1;
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 1));
text_edit->set_text("testing\nswap");
MessageQueue::get_singleton()->flush();
@@ -317,15 +715,10 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_caret_column(text_edit->get_line(0).length());
MessageQueue::get_singleton()->flush();
SIGNAL_CHECK("caret_changed", empty_signal_args);
+ // Emitted twice for each line.
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 0), build_array(1, 1), build_array(1, 1));
- ((Array)lines_edited_args[1])[1] = 0;
- Array swap_args;
- swap_args.push_back(1);
- swap_args.push_back(1);
- lines_edited_args.push_back(swap_args);
- lines_edited_args.push_back(swap_args);
-
- // Order does not matter. Should also work if not editable.
+ // Order does not matter. Works when not editable.
text_edit->set_editable(false);
text_edit->swap_lines(1, 0);
MessageQueue::get_singleton()->flush();
@@ -336,19 +729,15 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("text_set");
text_edit->set_editable(true);
- lines_edited_args.reverse();
-
- // Single undo/redo action
+ // Single undo/redo action.
text_edit->undo();
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "testing\nswap");
- SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
- lines_edited_args.reverse();
-
text_edit->redo();
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "swap\ntesting");
@@ -361,36 +750,70 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
ERR_PRINT_OFF;
text_edit->swap_lines(-1, 0);
CHECK(text_edit->get_text() == "swap\ntesting");
- SIGNAL_CHECK_FALSE("lines_edited_from");
- SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK_FALSE("text_changed");
- SIGNAL_CHECK_FALSE("text_set");
-
text_edit->swap_lines(0, -1);
CHECK(text_edit->get_text() == "swap\ntesting");
- SIGNAL_CHECK_FALSE("lines_edited_from");
- SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK_FALSE("text_changed");
- SIGNAL_CHECK_FALSE("text_set");
-
text_edit->swap_lines(2, 0);
CHECK(text_edit->get_text() == "swap\ntesting");
+ text_edit->swap_lines(0, 2);
+ CHECK(text_edit->get_text() == "swap\ntesting");
+ MessageQueue::get_singleton()->flush();
SIGNAL_CHECK_FALSE("lines_edited_from");
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("text_set");
+ ERR_PRINT_ON;
+
+ // Carets are also swapped.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(2);
+ text_edit->select(0, 0, 0, 2);
+ text_edit->add_caret(1, 6);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 1), build_array(1, 1), build_array(0, 0), build_array(0, 0));
+
+ text_edit->swap_lines(0, 1);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "testing\nswap");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 2);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 6);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->remove_secondary_carets();
+
+ // Swap non adjacent lines.
+ text_edit->insert_line_at(1, "new line");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(5);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "testing\nnew line\nswap");
+ SIGNAL_DISCARD("caret_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("text_changed");
+ lines_edited_args = build_array(build_array(2, 2), build_array(2, 2), build_array(0, 0), build_array(0, 0));
text_edit->swap_lines(0, 2);
- CHECK(text_edit->get_text() == "swap\ntesting");
- SIGNAL_CHECK_FALSE("lines_edited_from");
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "swap\nnew line\ntesting");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 5);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
SIGNAL_CHECK_FALSE("caret_changed");
- SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
- ERR_PRINT_ON;
}
SUBCASE("[TextEdit] insert line at") {
- ((Array)lines_edited_args[1])[1] = 1;
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 1));
text_edit->set_text("testing\nswap");
MessageQueue::get_singleton()->flush();
@@ -407,9 +830,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_to_line() == 1);
SIGNAL_CHECK("caret_changed", empty_signal_args);
- // Insert before should move caret and selection, and works when not editable.
+ // Insert line at inserts a line before and moves caret and selection. Works when not editable.
text_edit->set_editable(false);
- lines_edited_args.remove_at(0);
+ lines_edited_args = build_array(build_array(0, 1));
text_edit->insert_line_at(0, "new");
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "new\ntesting\nswap");
@@ -417,7 +840,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_column() == text_edit->get_line(2).size() - 1);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selection_from_line() == 1);
+ CHECK(text_edit->get_selection_from_column() == 0);
CHECK(text_edit->get_selection_to_line() == 2);
+ CHECK(text_edit->get_selection_to_column() == 4);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
@@ -425,19 +850,15 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_editable(true);
// Can undo/redo as single action.
- ((Array)lines_edited_args[0])[0] = 1;
- ((Array)lines_edited_args[0])[1] = 0;
text_edit->undo();
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "testing\nswap");
CHECK(text_edit->has_selection());
- SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
- ((Array)lines_edited_args[0])[0] = 0;
- ((Array)lines_edited_args[0])[1] = 1;
text_edit->redo();
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "new\ntesting\nswap");
@@ -454,9 +875,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_from_line() == 0);
CHECK(text_edit->get_selection_to_line() == 2);
SIGNAL_CHECK_FALSE("caret_changed");
+ lines_edited_args = build_array(build_array(2, 3));
- ((Array)lines_edited_args[0])[0] = 2;
- ((Array)lines_edited_args[0])[1] = 3;
text_edit->insert_line_at(2, "after");
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "new\ntesting\nafter\nswap");
@@ -474,24 +894,222 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
ERR_PRINT_OFF;
text_edit->insert_line_at(-1, "after");
CHECK(text_edit->get_text() == "new\ntesting\nafter\nswap");
+ text_edit->insert_line_at(4, "after");
+ CHECK(text_edit->get_text() == "new\ntesting\nafter\nswap");
+ MessageQueue::get_singleton()->flush();
SIGNAL_CHECK_FALSE("lines_edited_from");
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("text_set");
+ ERR_PRINT_ON;
- text_edit->insert_line_at(4, "after");
- CHECK(text_edit->get_text() == "new\ntesting\nafter\nswap");
+ // Can insert multiple lines.
+ text_edit->select(0, 1, 2, 2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(2, 4));
+
+ text_edit->insert_line_at(2, "multiple\nlines");
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "new\ntesting\nmultiple\nlines\nafter\nswap");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 4);
+ CHECK(text_edit->get_caret_column() == 2);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 1);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ }
+
+ SUBCASE("[TextEdit] remove line at") {
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 5));
+ text_edit->set_text("testing\nremove line at\n\tremove\nlines\n\ntest");
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "testing\nremove line at\n\tremove\nlines\n\ntest");
+ SIGNAL_CHECK("text_set", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_changed");
+
+ // Remove line handles multiple carets.
+ text_edit->set_caret_line(2);
+ text_edit->set_caret_column(0);
+ text_edit->add_caret(2, 7);
+ text_edit->select(2, 1, 2, 7, 1);
+ text_edit->add_caret(3, 1);
+ text_edit->add_caret(4, 5);
+ text_edit->add_caret(1, 5);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(3, 2));
+
+ text_edit->remove_line_at(2, true);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "testing\nremove line at\nlines\n\ntest");
+ CHECK(text_edit->get_caret_count() == 5);
+ CHECK_FALSE(text_edit->has_selection(0)); // Same line.
+ CHECK(text_edit->get_caret_line(0) == 2);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->has_selection(1)); // Same line, clamped.
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 5);
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 3); // In the default font, this is the same position.
+ CHECK_FALSE(text_edit->has_selection(2)); // Moved up.
+ CHECK(text_edit->get_caret_line(2) == 2);
+ CHECK(text_edit->get_caret_column(2) == 1);
+ CHECK_FALSE(text_edit->has_selection(3)); // Moved up.
+ CHECK(text_edit->get_caret_line(3) == 3);
+ CHECK(text_edit->get_caret_column(3) == 0);
+ CHECK_FALSE(text_edit->has_selection(4)); // Didn't move.
+ CHECK(text_edit->get_caret_line(4) == 1);
+ CHECK(text_edit->get_caret_column(4) == 5);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->remove_secondary_carets();
+
+ // Remove first line.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(5);
+ text_edit->add_caret(4, 4);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 0));
+
+ text_edit->remove_line_at(0, false);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "remove line at\nlines\n\ntest");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->remove_secondary_carets();
+
+ // Remove empty line.
+ text_edit->set_caret_line(2);
+ text_edit->set_caret_column(0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(3, 2));
+
+ text_edit->remove_line_at(2, false);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "remove line at\nlines\ntest");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Remove last line.
+ text_edit->set_caret_line(2);
+ text_edit->set_caret_column(2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(2, 1));
+
+ text_edit->remove_line_at(2, true);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "remove line at\nlines");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Out of bounds.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ ERR_PRINT_OFF
+ text_edit->remove_line_at(2, true);
+ ERR_PRINT_ON
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "remove line at\nlines");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 2);
SIGNAL_CHECK_FALSE("lines_edited_from");
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("text_set");
- ERR_PRINT_ON;
+
+ // Remove regular line with move caret up and not editable.
+ text_edit->set_editable(false);
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 0));
+
+ text_edit->remove_line_at(1, false);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "remove line at");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 1); // In the default font, this is the same position.
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+ text_edit->set_editable(true);
+
+ // Undo.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "remove line at\nlines");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 2);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "remove line at");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 1);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
+
+ // Remove only line removes line content.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(10);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0));
+
+ text_edit->remove_line_at(0);
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "");
+ CHECK(text_edit->get_line_count() == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_set");
}
- SUBCASE("[TextEdit] insert line at caret") {
- lines_edited_args.pop_back();
- ((Array)lines_edited_args[0])[1] = 1;
+ SUBCASE("[TextEdit] insert text at caret") {
+ lines_edited_args = build_array(build_array(0, 1));
+ // Insert text at caret can insert multiple lines.
text_edit->insert_text_at_caret("testing\nswap");
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "testing\nswap");
@@ -502,11 +1120,13 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
+ // Text is inserted at caret.
text_edit->set_caret_line(0, false);
text_edit->set_caret_column(2);
+ MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("caret_changed");
- ((Array)lines_edited_args[0])[1] = 0;
+ lines_edited_args = build_array(build_array(0, 0));
text_edit->insert_text_at_caret("mid");
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "temidsting\nswap");
@@ -517,9 +1137,10 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_set");
+ // Selections are deleted then text is inserted. It also works even if not editable.
text_edit->select(0, 0, 0, text_edit->get_line(0).length());
CHECK(text_edit->has_selection());
- lines_edited_args.push_back(args1.duplicate());
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 0));
text_edit->set_editable(false);
text_edit->insert_text_at_caret("new line");
@@ -534,12 +1155,15 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("text_set");
text_edit->set_editable(true);
+ // Undo restores text and selection.
text_edit->undo();
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "temidsting\nswap");
CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 5);
+ CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length());
CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 0);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("caret_changed", empty_signal_args);
@@ -589,24 +1213,19 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_WATCH(text_edit, "lines_edited_from");
SIGNAL_WATCH(text_edit, "caret_changed");
- Array args1;
- args1.push_back(0);
- args1.push_back(0);
- Array lines_edited_args;
- lines_edited_args.push_back(args1);
- lines_edited_args.push_back(args1.duplicate());
+ Array lines_edited_args = build_array(build_array(0, 0), build_array(0, 0));
SUBCASE("[TextEdit] select all") {
+ // Select when there is no text does not select.
text_edit->select_all();
CHECK_FALSE(text_edit->has_selection());
- ERR_PRINT_OFF;
- CHECK(text_edit->get_selection_from_line() == -1);
- CHECK(text_edit->get_selection_from_column() == -1);
- CHECK(text_edit->get_selection_to_line() == -1);
- CHECK(text_edit->get_selection_to_column() == -1);
+ CHECK(text_edit->get_selection_from_line() == 0);
+ CHECK(text_edit->get_selection_from_column() == 0);
+ CHECK(text_edit->get_selection_to_line() == 0);
+ CHECK(text_edit->get_selection_to_column() == 0);
CHECK(text_edit->get_selected_text() == "");
- ERR_PRINT_ON;
+ // Select all selects all text.
text_edit->set_text("test\nselection");
SEND_GUI_ACTION("ui_text_select_all");
CHECK(text_edit->get_viewport()->is_input_handled());
@@ -618,10 +1237,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_to_line() == 1);
CHECK(text_edit->get_selection_to_column() == 9);
CHECK(text_edit->get_selection_mode() == TextEdit::SelectionMode::SELECTION_MODE_SHIFT);
+ CHECK(text_edit->is_caret_after_selection_origin());
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 9);
SIGNAL_CHECK("caret_changed", empty_signal_args);
+ // Cannot select when disabled.
text_edit->set_caret_line(0);
text_edit->set_caret_column(0);
text_edit->set_selecting_enabled(false);
@@ -654,6 +1275,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
+ // Select word under caret with multiple carets.
text_edit->select_word_under_caret();
CHECK(text_edit->has_selection(0));
CHECK(text_edit->get_selected_text(0) == "test");
@@ -675,6 +1297,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_count() == 2);
+ // Select word under caret disables selection if there is already a selection.
text_edit->select_word_under_caret();
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "");
@@ -703,6 +1326,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selected_text() == "test\ntest");
SIGNAL_CHECK("caret_changed", empty_signal_args);
+ // Cannot select when disabled.
text_edit->set_selecting_enabled(false);
text_edit->select_word_under_caret();
CHECK_FALSE(text_edit->has_selection());
@@ -714,10 +1338,10 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("caret_changed");
text_edit->set_selecting_enabled(true);
- text_edit->set_caret_line(1, false, true, 0, 0);
+ // Select word under caret when there is no word does not select.
+ text_edit->set_caret_line(1, false, true, -1, 0);
text_edit->set_caret_column(5, false, 0);
-
- text_edit->set_caret_line(2, false, true, 0, 1);
+ text_edit->set_caret_line(2, false, true, -1, 1);
text_edit->set_caret_column(5, false, 1);
text_edit->select_word_under_caret();
@@ -739,7 +1363,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_caret_column(0);
text_edit->set_caret_line(1);
- // First selection made by the implicit select_word_under_caret call
+ // First selection made by the implicit select_word_under_caret call.
text_edit->add_selection_for_next_occurrence();
CHECK(text_edit->get_caret_count() == 1);
CHECK(text_edit->get_selected_text(0) == "test");
@@ -780,7 +1404,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line(3) == 3);
CHECK(text_edit->get_caret_column(3) == 9);
- // A different word with a new manually added caret
+ // A different word with a new manually added caret.
text_edit->add_caret(2, 1);
text_edit->select(2, 0, 2, 4, 4);
CHECK(text_edit->get_selected_text(4) == "rand");
@@ -795,7 +1419,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line(5) == 3);
CHECK(text_edit->get_caret_column(5) == 22);
- // Make sure the previous selections are still active
+ // Make sure the previous selections are still active.
CHECK(text_edit->get_selected_text(0) == "test");
CHECK(text_edit->get_selected_text(1) == "test");
CHECK(text_edit->get_selected_text(2) == "test");
@@ -987,6 +1611,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::SHIFT)
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "t");
+ CHECK(text_edit->is_caret_after_selection_origin());
#ifdef MACOS_ENABLED
SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::SHIFT | KeyModifierMask::ALT)
@@ -995,10 +1620,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
#endif
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "test");
+ CHECK(text_edit->is_caret_after_selection_origin());
SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::SHIFT)
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "tes");
+ CHECK(text_edit->is_caret_after_selection_origin());
#ifdef MACOS_ENABLED
SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::SHIFT | KeyModifierMask::ALT)
@@ -1019,11 +1646,13 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::SHIFT)
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "t");
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin());
SEND_GUI_KEY_EVENT(Key::LEFT)
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "");
+ // Cannot select when disabled.
text_edit->set_selecting_enabled(false);
SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::SHIFT)
CHECK_FALSE(text_edit->has_selection());
@@ -1032,46 +1661,120 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
}
SUBCASE("[TextEdit] mouse drag select") {
- /* Set size for mouse input. */
+ // Set size for mouse input.
text_edit->set_size(Size2(200, 200));
text_edit->set_text("this is some text\nfor selection");
text_edit->grab_focus();
MessageQueue::get_singleton()->flush();
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
- SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE);
+ // Click and drag to make a selection.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 0).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ // Add (2,0) to bring it past the center point of the grapheme and account for integer division flooring.
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 5).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "for s");
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
- CHECK(text_edit->get_selection_from_line() == 1);
- CHECK(text_edit->get_selection_from_column() == 0);
- CHECK(text_edit->get_selection_to_line() == 1);
- CHECK(text_edit->get_selection_to_column() == 5);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 0);
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 5);
+ CHECK(text_edit->is_caret_after_selection_origin());
+ CHECK(text_edit->is_dragging_cursor());
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ // Releasing finishes.
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(1, 5).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "for s");
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 9).get_center() + Point2i(2, 0), MouseButtonMask::NONE, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "for s");
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 5);
+ CHECK(text_edit->is_caret_after_selection_origin());
+
+ // Clicking clears selection.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 7).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 7);
+ // Cannot select when disabled, but caret still moves.
text_edit->set_selecting_enabled(false);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
- SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 0).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 0);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 5).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 5);
text_edit->set_selecting_enabled(true);
+
+ // Only last caret is moved when adding a selection.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret(0, 15);
+ text_edit->select(0, 11, 0, 15, 1);
+ MessageQueue::get_singleton()->flush();
+
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 5).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::ALT);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 0).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selection_origin_line(1) == 0);
+ CHECK(text_edit->get_selection_origin_column(1) == 11);
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 15);
+
+ CHECK(text_edit->has_selection(2));
+ CHECK(text_edit->get_selected_text(2) == "for s");
+ CHECK(text_edit->get_selection_origin_line(2) == 1);
+ CHECK(text_edit->get_selection_origin_column(2) == 5);
+ CHECK(text_edit->get_caret_line(2) == 1);
+ CHECK(text_edit->get_caret_column(2) == 0);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(2));
+
+ // Overlapping carets and selections merges them.
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 3).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "s is some text\nfor s");
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 5);
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 3);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin());
+
+ // Entering text stops selecting.
+ text_edit->insert_text_at_caret("a");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_text() == "thiaelection");
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 10).get_center() + Point2i(2, 0), MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
}
SUBCASE("[TextEdit] mouse word select") {
- /* Set size for mouse input. */
+ // Set size for mouse input.
text_edit->set_size(Size2(200, 200));
- text_edit->set_text("this is some text\nfor selection");
+ text_edit->set_text("this is some text\nfor selection\n");
MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("caret_changed");
- SEND_GUI_DOUBLE_CLICK(text_edit->get_pos_at_line_column(0, 2), Key::NONE);
+ // Double click to select word.
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(1, 2).get_center() + Point2i(2, 0), Key::NONE);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "for");
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
@@ -1081,9 +1784,11 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_to_column() == 3);
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 3);
+ CHECK(text_edit->is_caret_after_selection_origin());
SIGNAL_CHECK("caret_changed", empty_signal_args);
- SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE);
+ // Moving mouse selects entire words at a time.
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 6).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "for selection");
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
@@ -1093,15 +1798,116 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_selection_to_column() == 13);
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 13);
+ CHECK(text_edit->is_caret_after_selection_origin());
+ CHECK(text_edit->is_dragging_cursor());
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+
+ // Moving to a word before the initial selected word reverses selection direction and keeps the initial word selected.
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 10).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "some text\nfor");
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
+ CHECK(text_edit->get_selection_from_line() == 0);
+ CHECK(text_edit->get_selection_from_column() == 8);
+ CHECK(text_edit->get_selection_to_line() == 1);
+ CHECK(text_edit->get_selection_to_column() == 3);
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 8);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin());
SIGNAL_CHECK("caret_changed", empty_signal_args);
- Point2i line_0 = text_edit->get_pos_at_line_column(0, 0);
- line_0.y /= 2;
- SEND_GUI_MOUSE_BUTTON_EVENT(line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ // Releasing finishes.
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(0, 10).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "some text\nfor");
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 2).get_center(), MouseButtonMask::NONE, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "some text\nfor");
+ text_edit->deselect();
+
+ // Can start word select mode on an empty line.
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(2, 0).get_center() + Point2i(2, 0), Key::NONE);
CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
+ CHECK(text_edit->get_caret_line() == 2);
+ CHECK(text_edit->get_caret_column() == 0);
+
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 9).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "selection\n");
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 4);
+ CHECK(text_edit->get_selection_origin_line() == 2);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+
+ // Clicking clears selection.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 0).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+ // Can start word select mode when not on a word.
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(0, 12).get_center() + Point2i(2, 0), Key::NONE);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 12);
+
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 9).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == " text\nfor selection");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 13);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 12);
+
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 10).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "some");
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 8);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 12);
+
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(0, 15).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+
+ // Add a new selection without affecting the old one.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 5).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::ALT);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 8).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::ALT);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "some");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 8);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 12);
+
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == "ele");
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 8);
+ CHECK(text_edit->get_selection_origin_line(1) == 1);
+ CHECK(text_edit->get_selection_origin_column(1) == 5);
+
+ // Shift + double click to extend selection and start word select mode.
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(1, 8).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ text_edit->remove_secondary_carets();
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(1, 7).get_center() + Point2i(2, 0), Key::NONE | KeyModifierMask::SHIFT);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == " text\nfor selection");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 13);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 12);
+
+ // Cannot select when disabled, but caret still moves to end of word.
text_edit->set_selecting_enabled(false);
- SEND_GUI_DOUBLE_CLICK(text_edit->get_pos_at_line_column(0, 2), Key::NONE);
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(1, 1).get_center() + Point2i(2, 0), Key::NONE);
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 3);
@@ -1109,32 +1915,149 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
}
SUBCASE("[TextEdit] mouse line select") {
- /* Set size for mouse input. */
+ // Set size for mouse input.
text_edit->set_size(Size2(200, 200));
- text_edit->set_text("this is some text\nfor selection");
+ text_edit->set_text("this is some text\nfor selection\nwith 3 lines");
MessageQueue::get_singleton()->flush();
- SEND_GUI_DOUBLE_CLICK(text_edit->get_pos_at_line_column(0, 2), Key::NONE);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ // Triple click to select line.
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(1, 2).get_center(), Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 2).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->has_selection());
- CHECK(text_edit->get_selected_text() == "for selection");
+ CHECK(text_edit->get_selected_text() == "for selection\n");
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE);
CHECK(text_edit->get_selection_from_line() == 1);
CHECK(text_edit->get_selection_from_column() == 0);
+ CHECK(text_edit->get_selection_to_line() == 2);
+ CHECK(text_edit->get_selection_to_column() == 0);
+ CHECK(text_edit->get_caret_line() == 2);
+ CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->is_caret_after_selection_origin());
+
+ // Moving mouse selects entire lines at a time. Selecting above reverses the selection direction.
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 10).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "this is some text\nfor selection");
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE);
+ CHECK(text_edit->get_selection_from_line() == 0);
+ CHECK(text_edit->get_selection_from_column() == 0);
CHECK(text_edit->get_selection_to_line() == 1);
CHECK(text_edit->get_selection_to_column() == 13);
- CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 0);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin());
+ CHECK(text_edit->is_dragging_cursor());
- Point2i line_0 = text_edit->get_pos_at_line_column(0, 0);
- line_0.y /= 2;
- SEND_GUI_MOUSE_BUTTON_EVENT(line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ // Selecting to the last line puts the caret at end of the line.
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(2, 10).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "for selection\nwith 3 lines");
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE);
+ CHECK(text_edit->get_selection_from_line() == 1);
+ CHECK(text_edit->get_selection_from_column() == 0);
+ CHECK(text_edit->get_selection_to_line() == 2);
+ CHECK(text_edit->get_selection_to_column() == 12);
+ CHECK(text_edit->get_caret_line() == 2);
+ CHECK(text_edit->get_caret_column() == 12);
+ CHECK(text_edit->is_caret_after_selection_origin());
+
+ // Releasing finishes.
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(2, 10).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "for selection\nwith 3 lines");
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 2).get_center(), MouseButtonMask::NONE, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "for selection\nwith 3 lines");
+
+ // Clicking clears selection.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 0).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(0, 0).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+
+ // Can start line select mode on an empty line.
+ text_edit->set_text("this is some text\n\nfor selection\nwith 4 lines");
+ MessageQueue::get_singleton()->flush();
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(1, 0).get_center() + Point2i(2, 0), Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 0).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "\n");
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE);
+ CHECK(text_edit->get_caret_line() == 2);
+ CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(2, 9).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "\nfor selection\n");
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE);
+ CHECK(text_edit->get_caret_line() == 3);
+ CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+
+ // Add a new selection without affecting the old one.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 3).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::ALT);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 4).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::ALT);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "\nfor selection\n");
+ CHECK(text_edit->get_caret_line(0) == 3);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
+
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == "is");
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ CHECK(text_edit->get_selection_origin_line(1) == 0);
+ CHECK(text_edit->get_selection_origin_column(1) == 2);
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+
+ // Selecting the last line puts caret at the end.
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(3, 3).get_center(), Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(3, 3).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "with 4 lines");
+ CHECK(text_edit->get_caret_line() == 3);
+ CHECK(text_edit->get_caret_column() == 12);
+ CHECK(text_edit->get_selection_origin_line() == 3);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+
+ // Selecting above reverses direction.
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(2, 10).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "for selection\nwith 4 lines");
+ CHECK(text_edit->get_caret_line() == 2);
+ CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_selection_origin_line() == 3);
+ CHECK(text_edit->get_selection_origin_column() == 12);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(2, 10).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+
+ // Shift + triple click to extend selection and restart line select mode.
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(0, 9).get_center() + Point2i(2, 0), Key::NONE | KeyModifierMask::SHIFT);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 9).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT);
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "this is some text\n\nfor selection\nwith 4 lines");
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_selection_origin_line() == 3);
+ CHECK(text_edit->get_selection_origin_column() == 12);
+
+ // Cannot select when disabled, but caret still moves to the start of the next line.
text_edit->set_selecting_enabled(false);
- SEND_GUI_DOUBLE_CLICK(text_edit->get_pos_at_line_column(0, 2), Key::NONE);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_DOUBLE_CLICK(text_edit->get_rect_at_line_column(0, 2).get_center(), Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 2).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
@@ -1142,30 +2065,47 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
}
SUBCASE("[TextEdit] mouse shift click select") {
- /* Set size for mouse input. */
+ // Set size for mouse input.
text_edit->set_size(Size2(200, 200));
text_edit->set_text("this is some text\nfor selection");
MessageQueue::get_singleton()->flush();
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT);
+ // Shift click to make a selection from the previous caret position.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 1).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 5).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT);
CHECK(text_edit->has_selection());
- CHECK(text_edit->get_selected_text() == "for s");
+ CHECK(text_edit->get_selected_text() == "or s");
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
- CHECK(text_edit->get_selection_from_line() == 1);
- CHECK(text_edit->get_selection_from_column() == 0);
- CHECK(text_edit->get_selection_to_line() == 1);
- CHECK(text_edit->get_selection_to_column() == 5);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 1);
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 5);
+ CHECK(text_edit->is_caret_after_selection_origin());
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ // Shift click above to switch selection direction. Uses original selection position.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 6).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "s some text\nf");
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 1);
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 6);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin());
+
+ // Clicking clears selection.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 7).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 7);
+ // Cannot select when disabled, but caret still moves.
text_edit->set_selecting_enabled(false);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
- SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 0).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 0);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 5).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT);
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 5);
@@ -1175,89 +2115,166 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SUBCASE("[TextEdit] select and deselect") {
text_edit->set_text("this is some text\nfor selection");
MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ // Select clamps input to full text.
text_edit->select(-1, -1, 500, 500);
+ MessageQueue::get_singleton()->flush();
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "this is some text\nfor selection");
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 13);
+ CHECK(text_edit->get_selection_from_line(0) == text_edit->get_selection_origin_line(0));
+ CHECK(text_edit->get_selection_from_column(0) == text_edit->get_selection_origin_column(0));
+ CHECK(text_edit->get_selection_to_line(0) == text_edit->get_caret_line(0));
+ CHECK(text_edit->get_selection_to_column(0) == text_edit->get_caret_column(0));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
text_edit->deselect();
+ MessageQueue::get_singleton()->flush();
CHECK_FALSE(text_edit->has_selection());
+ SIGNAL_CHECK_FALSE("caret_changed");
+ // Select works in the other direction.
text_edit->select(500, 500, -1, -1);
+ MessageQueue::get_singleton()->flush();
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == "this is some text\nfor selection");
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 13);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->get_selection_from_line(0) == text_edit->get_caret_line(0));
+ CHECK(text_edit->get_selection_from_column(0) == text_edit->get_caret_column(0));
+ CHECK(text_edit->get_selection_to_line(0) == text_edit->get_selection_origin_line(0));
+ CHECK(text_edit->get_selection_to_column(0) == text_edit->get_selection_origin_column(0));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
text_edit->deselect();
+ MessageQueue::get_singleton()->flush();
CHECK_FALSE(text_edit->has_selection());
+ SIGNAL_CHECK_FALSE("caret_changed");
+ // Select part of a line.
text_edit->select(0, 4, 0, 8);
+ MessageQueue::get_singleton()->flush();
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == " is ");
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 4);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 8);
+ CHECK(text_edit->get_selection_from_line(0) == text_edit->get_selection_origin_line(0));
+ CHECK(text_edit->get_selection_from_column(0) == text_edit->get_selection_origin_column(0));
+ CHECK(text_edit->get_selection_to_line(0) == text_edit->get_caret_line(0));
+ CHECK(text_edit->get_selection_to_column(0) == text_edit->get_caret_column(0));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
text_edit->deselect();
+ MessageQueue::get_singleton()->flush();
CHECK_FALSE(text_edit->has_selection());
+ SIGNAL_CHECK_FALSE("caret_changed");
+ // Select part of a line in the other direction.
text_edit->select(0, 8, 0, 4);
+ MessageQueue::get_singleton()->flush();
CHECK(text_edit->has_selection());
CHECK(text_edit->get_selected_text() == " is ");
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 8);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_selection_from_line(0) == text_edit->get_caret_line(0));
+ CHECK(text_edit->get_selection_from_column(0) == text_edit->get_caret_column(0));
+ CHECK(text_edit->get_selection_to_line(0) == text_edit->get_selection_origin_line(0));
+ CHECK(text_edit->get_selection_to_column(0) == text_edit->get_selection_origin_column(0));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ // Cannot select when disabled.
text_edit->set_selecting_enabled(false);
CHECK_FALSE(text_edit->has_selection());
text_edit->select(0, 8, 0, 4);
+ MessageQueue::get_singleton()->flush();
CHECK_FALSE(text_edit->has_selection());
+ SIGNAL_CHECK_FALSE("caret_changed");
text_edit->set_selecting_enabled(true);
+ }
- text_edit->select(0, 8, 0, 4);
- CHECK(text_edit->has_selection());
- SEND_GUI_ACTION("ui_text_caret_right");
+ SUBCASE("[TextEdit] delete selection") {
+ text_edit->set_text("this is some text\nfor selection");
+ MessageQueue::get_singleton()->flush();
+
+ // Delete selection does nothing if there is no selection.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(8);
CHECK_FALSE(text_edit->has_selection());
text_edit->delete_selection();
CHECK(text_edit->get_text() == "this is some text\nfor selection");
+ CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 8);
- text_edit->select(0, 8, 0, 4);
+ // Backspace removes selection.
+ text_edit->select(0, 4, 0, 8);
CHECK(text_edit->has_selection());
SEND_GUI_ACTION("ui_text_backspace");
+ CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_text() == "thissome text\nfor selection");
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 4);
+ // Undo restores previous selection.
text_edit->undo();
- CHECK(text_edit->has_selection());
CHECK(text_edit->get_text() == "this is some text\nfor selection");
+ CHECK(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 8);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 4);
+ // Redo restores caret.
text_edit->redo();
- CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_text() == "thissome text\nfor selection");
+ CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 4);
text_edit->undo();
- CHECK(text_edit->has_selection());
CHECK(text_edit->get_text() == "this is some text\nfor selection");
+ CHECK(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 8);
- text_edit->select(0, 8, 0, 4);
+ text_edit->select(0, 4, 0, 8);
CHECK(text_edit->has_selection());
+ // Delete selection removes text, deselects, and moves caret.
text_edit->delete_selection();
- CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_text() == "thissome text\nfor selection");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
+ // Undo delete works.
text_edit->undo();
- CHECK(text_edit->has_selection());
CHECK(text_edit->get_text() == "this is some text\nfor selection");
+ CHECK(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 8);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 4);
+ // Redo delete works.
text_edit->redo();
- CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_text() == "thissome text\nfor selection");
+ CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 4);
@@ -1267,19 +2284,227 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 8);
+ // Can still delete if not editable.
text_edit->set_editable(false);
text_edit->delete_selection();
text_edit->set_editable(false);
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_text() == "thissome text\nfor selection");
+ // Cannot undo since it was not editable.
text_edit->undo();
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_text() == "thissome text\nfor selection");
+
+ // Delete multiple adjacent selections on the same line.
+ text_edit->select(0, 0, 0, 5);
+ text_edit->add_caret(0, 8);
+ text_edit->select(0, 5, 0, 8, 1);
+ CHECK(text_edit->get_caret_count() == 2);
+ text_edit->delete_selection();
+ CHECK(text_edit->get_text() == " text\nfor selection");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+
+ // Delete mulitline selection. Ignore non selections.
+ text_edit->remove_secondary_carets();
+ text_edit->select(1, 3, 0, 2);
+ text_edit->add_caret(1, 7);
+ text_edit->delete_selection();
+ CHECK(text_edit->get_text() == " t selection");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 2);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 6);
}
- // Add readonly test?
SUBCASE("[TextEdit] text drag") {
+ text_edit->set_size(Size2(200, 200));
+ text_edit->set_text("drag test\ndrop here ''");
+ text_edit->grab_click_focus();
+ MessageQueue::get_singleton()->flush();
+
+ // Drag and drop selected text to mouse position.
+ text_edit->select(0, 0, 0, 4);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 2).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 7).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "drag");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 11).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(1, 11).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == " test\ndrop here 'drag'");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 15);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 11);
+
+ // Undo.
+ text_edit->undo();
+ CHECK(text_edit->get_text() == "drag test\ndrop here ''");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+
+ // Redo.
+ text_edit->redo();
+ CHECK(text_edit->get_text() == " test\ndrop here 'drag'");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 15);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 11);
+
+ // Hold control when dropping to not delete selected text.
+ text_edit->select(1, 10, 1, 16);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 12).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 7).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "'drag'");
+ CHECK(text_edit->has_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 0).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_KEY_EVENT(Key::CMD_OR_CTRL);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(0, 0).get_center(), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ SEND_GUI_KEY_UP_EVENT(Key::CMD_OR_CTRL);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "'drag' test\ndrop here 'drag'");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 6);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+
+ // Multiple caret drags entire selection.
+ text_edit->select(0, 11, 0, 7, 0);
+ text_edit->add_caret(1, 2);
+ text_edit->select(1, 2, 1, 4, 1);
+ text_edit->add_caret(1, 12);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 3).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection(true, 1));
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 12).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "test\nop");
+ // Carets aren't removed from dragging, only dropping.
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 7);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 11);
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ CHECK(text_edit->get_selection_origin_line(1) == 1);
+ CHECK(text_edit->get_selection_origin_column(1) == 2);
+ CHECK_FALSE(text_edit->has_selection(2));
+ CHECK(text_edit->get_caret_line(2) == 1);
+ CHECK(text_edit->get_caret_column(2) == 12);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 9).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(1, 9).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "'drag' \ndr heretest\nop 'drag'");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 2);
+ CHECK(text_edit->get_caret_column() == 2);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 7);
+
+ // Drop onto same selection should do effectively nothing.
+ text_edit->select(1, 3, 1, 7);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(1, 6).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 1).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "here");
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(1, 7).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "'drag' \ndr heretest\nop 'drag'");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 7);
+ CHECK(text_edit->get_selection_origin_line() == 1);
+ CHECK(text_edit->get_selection_origin_column() == 3);
+
+ // Cannot drag when drag and drop selection is disabled. It becomes regular drag to select.
+ text_edit->set_drag_and_drop_selection_enabled(false);
+ text_edit->select(0, 1, 0, 5);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 2).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK_FALSE(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 7).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(1, 7).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "'drag' \ndr heretest\nop 'drag'");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 7);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 2);
+ text_edit->set_drag_and_drop_selection_enabled(true);
+
+ // Cancel drag and drop from Escape key.
+ text_edit->select(0, 1, 0, 5);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 3).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 1).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "drag");
+ SEND_GUI_KEY_EVENT(Key::ESCAPE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "'drag' \ndr heretest\nop 'drag'");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 5);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 1);
+
+ // Cancel drag and drop from caret move key input.
+ text_edit->select(0, 1, 0, 5);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 3).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 1).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "drag");
+ SEND_GUI_KEY_EVENT(Key::RIGHT);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "'drag' \ndr heretest\nop 'drag'");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 5);
+
+ // Cancel drag and drop from text key input.
+ text_edit->select(0, 1, 0, 5);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 3).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 1).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "drag");
+ SEND_GUI_KEY_EVENT(Key::A);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "'A' \ndr heretest\nop 'drag'");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 2);
+ }
+
+ SUBCASE("[TextEdit] text drag to another text edit") {
TextEdit *target_text_edit = memnew(TextEdit);
SceneTree::get_singleton()->get_root()->add_child(target_text_edit);
@@ -1292,27 +2517,223 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_text("drag me");
text_edit->select_all();
text_edit->grab_click_focus();
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 7);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 0);
MessageQueue::get_singleton()->flush();
- Point2i line_0 = text_edit->get_pos_at_line_column(0, 0);
- line_0.y /= 2;
- SEND_GUI_MOUSE_BUTTON_EVENT(line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ // Drag text between text edits.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 0).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->is_mouse_over_selection());
- SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 7).get_center(), MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->get_viewport()->gui_is_dragging());
CHECK(text_edit->get_viewport()->gui_get_drag_data() == "drag me");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 7);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 0);
- line_0 = target_text_edit->get_pos_at_line_column(0, 0);
- line_0.y /= 2;
- line_0.x += 401; // As empty add one.
- SEND_GUI_MOUSE_MOTION_EVENT(line_0, MouseButtonMask::LEFT, Key::NONE);
+ Point2i target_line0 = target_text_edit->get_position() + Point2i(1, target_text_edit->get_line_height() / 2);
+ SEND_GUI_MOUSE_MOTION_EVENT(target_line0, MouseButtonMask::LEFT, Key::NONE);
CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(target_text_edit->get_caret_line() == 0);
+ CHECK(target_text_edit->get_caret_column() == 0);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_line0, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+ CHECK(target_text_edit->get_text() == "drag me");
+ CHECK(target_text_edit->has_selection());
+ CHECK(target_text_edit->get_caret_line() == 0);
+ CHECK(target_text_edit->get_caret_column() == 7);
+ CHECK(target_text_edit->get_selection_origin_line() == 0);
+ CHECK(target_text_edit->get_selection_origin_column() == 0);
- SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(line_0, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ // Undo is separate per TextEdit.
+ text_edit->undo();
+ CHECK(text_edit->get_text() == "drag me");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 7);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+ CHECK(target_text_edit->get_text() == "drag me");
+ CHECK(target_text_edit->has_selection());
+ CHECK(target_text_edit->get_caret_line() == 0);
+ CHECK(target_text_edit->get_caret_column() == 7);
+ CHECK(target_text_edit->get_selection_origin_line() == 0);
+ CHECK(target_text_edit->get_selection_origin_column() == 0);
+
+ target_text_edit->undo();
+ CHECK(text_edit->get_text() == "drag me");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 7);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+ CHECK(target_text_edit->get_text() == "");
+ CHECK_FALSE(target_text_edit->has_selection());
+ CHECK(target_text_edit->get_caret_line() == 0);
+ CHECK(target_text_edit->get_caret_column() == 0);
+
+ // Redo is also separate.
+ text_edit->redo();
+ CHECK(text_edit->get_text() == "");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+ CHECK(target_text_edit->get_text() == "");
+ CHECK_FALSE(target_text_edit->has_selection());
+ CHECK(target_text_edit->get_caret_line() == 0);
+ CHECK(target_text_edit->get_caret_column() == 0);
- CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ target_text_edit->redo();
CHECK(text_edit->get_text() == "");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
CHECK(target_text_edit->get_text() == "drag me");
+ CHECK(target_text_edit->has_selection());
+ CHECK(target_text_edit->get_caret_line() == 0);
+ CHECK(target_text_edit->get_caret_column() == 7);
+ CHECK(target_text_edit->get_selection_origin_line() == 0);
+ CHECK(target_text_edit->get_selection_origin_column() == 0);
+
+ // Hold control to not remove selected text.
+ text_edit->set_text("drag test\ndrop test");
+ MessageQueue::get_singleton()->flush();
+ target_text_edit->select(0, 0, 0, 3, 0);
+ target_text_edit->add_caret(0, 5);
+ text_edit->select(0, 5, 0, 7, 0);
+ text_edit->add_caret(0, 1);
+ text_edit->select(0, 1, 0, 0, 1);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 5).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection(true, 0));
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 6).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "d\nte");
+ CHECK(text_edit->has_selection());
+ SEND_GUI_KEY_EVENT(Key::CMD_OR_CTRL);
+ SEND_GUI_MOUSE_MOTION_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 6).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 6).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ SEND_GUI_KEY_UP_EVENT(Key::CMD_OR_CTRL);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "drag test\ndrop test");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 7);
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 0);
+ CHECK(target_text_edit->get_text() == "drag md\ntee");
+ CHECK(target_text_edit->get_caret_count() == 1);
+ CHECK(target_text_edit->has_selection());
+ CHECK(target_text_edit->get_caret_line() == 1);
+ CHECK(target_text_edit->get_caret_column() == 2);
+ CHECK(target_text_edit->get_selection_origin_line() == 0);
+ CHECK(target_text_edit->get_selection_origin_column() == 6);
+
+ // Drop onto selected text deletes the selected text first.
+ text_edit->set_deselect_on_focus_loss_enabled(false);
+ target_text_edit->set_deselect_on_focus_loss_enabled(false);
+ text_edit->remove_secondary_carets();
+ text_edit->select(0, 5, 0, 9);
+ target_text_edit->select(0, 6, 0, 8);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 6).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection(true, 0));
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 7).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "test");
+ CHECK(text_edit->has_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 7).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 7).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "drag \ndrop test");
+ CHECK(target_text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 5);
+ CHECK(target_text_edit->get_text() == "drag mdtest\ntee");
+ CHECK(target_text_edit->has_selection());
+ CHECK(target_text_edit->get_caret_line() == 0);
+ CHECK(target_text_edit->get_caret_column() == 11);
+ CHECK(target_text_edit->get_selection_origin_line() == 0);
+ CHECK(target_text_edit->get_selection_origin_column() == 7);
+ text_edit->set_deselect_on_focus_loss_enabled(true);
+ target_text_edit->set_deselect_on_focus_loss_enabled(true);
+
+ // Can drop even when drag and drop selection is disabled.
+ target_text_edit->set_drag_and_drop_selection_enabled(false);
+ text_edit->select(0, 4, 0, 5);
+ target_text_edit->deselect();
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 4).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 7).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == " ");
+ CHECK(text_edit->has_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 2).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 7).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "drag\ndrop test");
+ CHECK(target_text_edit->get_text() == "drag md test\ntee");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
+ CHECK(target_text_edit->has_selection());
+ CHECK(target_text_edit->get_caret_line() == 0);
+ CHECK(target_text_edit->get_caret_column() == 8);
+ CHECK(target_text_edit->get_selection_origin_line() == 0);
+ CHECK(target_text_edit->get_selection_origin_column() == 7);
+ target_text_edit->set_drag_and_drop_selection_enabled(true);
+
+ // Cannot drop when not editable.
+ target_text_edit->set_editable(false);
+ text_edit->select(0, 1, 0, 4);
+ target_text_edit->deselect();
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 2).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 7).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "rag");
+ CHECK(text_edit->has_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 2).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 2).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "drag\ndrop test");
+ CHECK(target_text_edit->get_text() == "drag md test\ntee");
+ CHECK(text_edit->has_selection());
+ CHECK_FALSE(target_text_edit->has_selection());
+ target_text_edit->set_editable(true);
+
+ // Can drag when not editable, but text will not be removed.
+ text_edit->set_editable(false);
+ text_edit->select(0, 0, 0, 4);
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 2).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->is_mouse_over_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 7).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_viewport()->gui_get_drag_data() == "drag");
+ CHECK(text_edit->has_selection());
+ SEND_GUI_MOUSE_MOTION_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 4).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_text_edit->get_position() + target_text_edit->get_rect_at_line_column(0, 4).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging());
+ CHECK(text_edit->get_text() == "drag\ndrop test");
+ CHECK(target_text_edit->get_text() == "dragdrag md test\ntee");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
+ CHECK(target_text_edit->has_selection());
+ CHECK(target_text_edit->get_caret_line() == 0);
+ CHECK(target_text_edit->get_caret_column() == 8);
+ CHECK(target_text_edit->get_selection_origin_line() == 0);
+ CHECK(target_text_edit->get_selection_origin_column() == 4);
+ text_edit->set_editable(true);
memdelete(target_text_edit);
}
@@ -1324,44 +2745,41 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
}
SUBCASE("[TextEdit] overridable actions") {
+ DisplayServerMock *DS = (DisplayServerMock *)(DisplayServer::get_singleton());
+
SIGNAL_WATCH(text_edit, "text_set");
SIGNAL_WATCH(text_edit, "text_changed");
SIGNAL_WATCH(text_edit, "lines_edited_from");
SIGNAL_WATCH(text_edit, "caret_changed");
- Array args1;
- args1.push_back(0);
- args1.push_back(0);
- Array lines_edited_args;
- lines_edited_args.push_back(args1);
+ Array lines_edited_args = build_array(build_array(0, 0));
SUBCASE("[TextEdit] backspace") {
text_edit->set_text("this is\nsome\n");
text_edit->set_caret_line(0);
text_edit->set_caret_column(0);
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
+ // Cannot backspace at start of text.
text_edit->backspace();
MessageQueue::get_singleton()->flush();
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
+ // Backspace at start of line removes the line.
text_edit->set_caret_line(2);
text_edit->set_caret_column(0);
MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(2, 1));
- ((Array)lines_edited_args[0])[0] = 2;
- ((Array)lines_edited_args[0])[1] = 1;
text_edit->backspace();
MessageQueue::get_singleton()->flush();
-
CHECK(text_edit->get_text() == "this is\nsome");
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 4);
@@ -1369,10 +2787,10 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- ((Array)lines_edited_args[0])[0] = 1;
+ // Backspace removes a character.
+ lines_edited_args = build_array(build_array(1, 1));
text_edit->backspace();
MessageQueue::get_singleton()->flush();
-
CHECK(text_edit->get_text() == "this is\nsom");
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 3);
@@ -1380,11 +2798,11 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Backspace when text is selected removes the selection.
text_edit->end_complex_operation();
text_edit->select(1, 0, 1, 3);
text_edit->backspace();
MessageQueue::get_singleton()->flush();
-
CHECK(text_edit->get_text() == "this is\n");
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
@@ -1392,11 +2810,11 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Cannot backspace if not editable.
text_edit->set_editable(false);
text_edit->backspace();
text_edit->set_editable(true);
MessageQueue::get_singleton()->flush();
-
CHECK(text_edit->get_text() == "this is\n");
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
@@ -1404,6 +2822,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
+ // Undo restores text to the previous end of complex operation.
text_edit->undo();
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_text() == "this is\nsom");
@@ -1412,98 +2831,736 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is\n");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // See ui_text_backspace for more backspace tests.
}
SUBCASE("[TextEdit] cut") {
+ // Cut without a selection removes the entire line.
text_edit->set_text("this is\nsome\n");
text_edit->set_caret_line(0);
text_edit->set_caret_column(6);
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 0));
- ERR_PRINT_OFF;
text_edit->cut();
MessageQueue::get_singleton()->flush();
- ERR_PRINT_ON; // Can't check display server content.
-
- ((Array)lines_edited_args[0])[0] = 1;
+ CHECK(DS->clipboard_get() == "this is\n");
CHECK(text_edit->get_text() == "some\n");
CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 4);
+ CHECK(text_edit->get_caret_column() == 3); // In the default font, this is the same position.
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- ((Array)lines_edited_args[0])[0] = 0;
- ((Array)lines_edited_args[0])[1] = 1;
+ // Undo restores the cut text.
text_edit->undo();
MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "this is\n");
CHECK(text_edit->get_text() == "this is\nsome\n");
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 6);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
- SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
- ((Array)lines_edited_args[0])[0] = 1;
- ((Array)lines_edited_args[0])[1] = 0;
+ // Redo.
text_edit->redo();
MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "this is\n");
CHECK(text_edit->get_text() == "some\n");
CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 4);
+ CHECK(text_edit->get_caret_column() == 3);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Cut with a selection removes just the selection.
text_edit->set_text("this is\nsome\n");
+ text_edit->select(0, 5, 0, 7);
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0));
- ((Array)lines_edited_args[0])[0] = 0;
- text_edit->select(0, 5, 0, 7);
- ERR_PRINT_OFF;
SEND_GUI_ACTION("ui_cut");
CHECK(text_edit->get_viewport()->is_input_handled());
MessageQueue::get_singleton()->flush();
- ERR_PRINT_ON; // Can't check display server content.
+ CHECK(DS->clipboard_get() == "is");
CHECK(text_edit->get_text() == "this \nsome\n");
+ CHECK_FALSE(text_edit->get_caret_line());
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 5);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Cut does not change the text if not editable. Text is still added to clipboard.
+ text_edit->set_text("this is\nsome\n");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(5);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+
text_edit->set_editable(false);
text_edit->cut();
MessageQueue::get_singleton()->flush();
text_edit->set_editable(true);
- CHECK(text_edit->get_text() == "this \nsome\n");
+ CHECK(DS->clipboard_get() == "this is\n");
+ CHECK(text_edit->get_text() == "this is\nsome\n");
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 5);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Cut line with multiple carets.
+ text_edit->set_text("this is\nsome\n");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(3);
+ text_edit->add_caret(0, 2);
+ text_edit->add_caret(0, 4);
+ text_edit->add_caret(2, 0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 0), build_array(1, 0));
+
+ text_edit->cut();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "this is\n\n");
+ CHECK(text_edit->get_text() == "some");
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 2); // In the default font, this is the same position.
+ // The previous caret at index 1 was merged.
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 3); // In the default font, this is the same position.
+ CHECK_FALSE(text_edit->has_selection(2));
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 4);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ text_edit->remove_secondary_carets();
+
+ // Cut on the only line removes the contents.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0));
+
+ text_edit->cut();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "some\n");
+ CHECK(text_edit->get_text() == "");
+ CHECK(text_edit->get_line_count() == 1);
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Cut empty line.
+ text_edit->cut();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "\n");
+ CHECK(text_edit->get_text() == "");
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ // These signals are emitted even if there is no change.
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Cut multiple lines, in order.
+ text_edit->set_text("this is\nsome\ntext to\nbe\n\ncut");
+ text_edit->set_caret_line(2);
+ text_edit->set_caret_column(7);
+ text_edit->add_caret(3, 0);
+ text_edit->add_caret(0, 2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 0), build_array(3, 2), build_array(2, 1));
+
+ text_edit->cut();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "this is\ntext to\nbe\n");
+ CHECK(text_edit->get_text() == "some\n\ncut");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 2);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ text_edit->remove_secondary_carets();
+
+ // Cut multiple selections, in order. Ignores regular carets.
+ text_edit->set_text("this is\nsome\ntext to\nbe\n\ncut");
+ text_edit->add_caret(3, 0);
+ text_edit->add_caret(0, 2);
+ text_edit->add_caret(2, 0);
+ text_edit->select(1, 0, 1, 2, 0);
+ text_edit->select(3, 0, 4, 0, 1);
+ text_edit->select(0, 5, 0, 3, 2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 1), build_array(4, 3), build_array(0, 0));
+
+ text_edit->cut();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "s \nso\nbe\n");
+ CHECK(text_edit->get_text() == "thiis\nme\ntext to\n\ncut");
+ CHECK(text_edit->get_caret_count() == 4);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 0);
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 3);
+ CHECK(text_edit->get_caret_line(3) == 2);
+ CHECK(text_edit->get_caret_column(3) == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
}
SUBCASE("[TextEdit] copy") {
- // TODO: Cannot test need display server support.
+ text_edit->set_text("this is\nsome\ntest\n\ntext");
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+
+ // Copy selected text.
+ text_edit->select(0, 0, 1, 2, 0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ DS->clipboard_set_primary("");
+
+ text_edit->copy();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "this is\nso");
+ CHECK(DS->clipboard_get_primary() == "");
+ CHECK(text_edit->get_text() == "this is\nsome\ntest\n\ntext");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 0);
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 2);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Copy with GUI action.
+ text_edit->select(0, 0, 0, 2, 0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ SEND_GUI_ACTION("ui_copy");
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "th");
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Can copy even if not editable.
+ text_edit->select(2, 4, 1, 2, 0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->set_editable(false);
+ text_edit->copy();
+ text_edit->set_editable(true);
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "me\ntest");
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+ text_edit->deselect();
+
+ // Copy full line when there is no selection.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->copy();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "this is\n");
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Copy empty line.
+ text_edit->set_caret_line(3);
+ text_edit->set_caret_column(0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->copy();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "\n");
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+ text_edit->deselect();
+
+ // Copy full line with multiple carets on that line only copies once.
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(2);
+ text_edit->add_caret(1, 0);
+ text_edit->add_caret(1, 4);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->copy();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "some\n");
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+ text_edit->remove_secondary_carets();
+
+ // Copy selected text from all selections with `\n` in between, in order. Ignore regular carets.
+ text_edit->set_caret_line(2);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret(4, 0);
+ text_edit->add_caret(0, 4);
+ text_edit->add_caret(1, 0);
+ text_edit->select(1, 3, 2, 4, 0);
+ text_edit->select(4, 4, 4, 0, 1);
+ text_edit->select(0, 5, 0, 4, 2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->copy();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == " \ne\ntest\ntext");
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+
+ // Copy multiple lines with multiple carets, in order.
+ text_edit->set_caret_line(3);
+ text_edit->set_caret_column(0);
+ text_edit->add_caret(4, 2);
+ text_edit->add_caret(0, 4);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->copy();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "this is\n\ntext\n");
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
}
SUBCASE("[TextEdit] paste") {
- // TODO: Cannot test need display server support.
+ // Paste text from clipboard at caret.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 1));
+ DS->clipboard_set("paste");
+
+ text_edit->paste();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "paste");
+ CHECK(text_edit->get_text() == "this is\nsopasteme\n\ntext");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 7);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Undo.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "paste");
+ CHECK(text_edit->get_text() == "this is\nsome\n\ntext");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 2);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "paste");
+ CHECK(text_edit->get_text() == "this is\nsopasteme\n\ntext");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 7);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Paste on empty line. Use GUI action.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(2);
+ text_edit->set_caret_column(0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(2, 2));
+ DS->clipboard_set("paste2");
+
+ SEND_GUI_ACTION("ui_paste");
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "paste2");
+ CHECK(text_edit->get_text() == "this is\nsome\npaste2\ntext");
+ CHECK(text_edit->get_caret_line() == 2);
+ CHECK(text_edit->get_caret_column() == 6);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Paste removes selection before pasting.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->select(0, 5, 1, 3);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 0), build_array(0, 0));
+ DS->clipboard_set("paste");
+
+ text_edit->paste();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "paste");
+ CHECK(text_edit->get_text() == "this pastee\n\ntext");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 10);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Paste multiple lines.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 3));
+ DS->clipboard_set("multi\n\nline\npaste");
+
+ text_edit->paste();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "multi\n\nline\npaste");
+ CHECK(text_edit->get_text() == "tmulti\n\nline\npastehis is\nsome\n\ntext");
+ CHECK(text_edit->get_caret_line() == 3);
+ CHECK(text_edit->get_caret_column() == 5);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Paste full line after copying it.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 2));
+ DS->clipboard_set("");
+ text_edit->copy();
+ text_edit->set_caret_column(3);
+ CHECK(DS->clipboard_get() == "some\n");
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->paste();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "some\n");
+ CHECK(text_edit->get_text() == "this is\nsome\nsome\n\ntext");
+ CHECK(text_edit->get_caret_line() == 2);
+ CHECK(text_edit->get_caret_column() == 3);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Do not paste as line since it wasn't copied.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 1));
+ DS->clipboard_set("paste\n");
+
+ text_edit->paste();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "paste\n");
+ CHECK(text_edit->get_text() == "thispaste\n is\nsome\n\ntext");
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Paste text at each caret.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(2);
+ text_edit->add_caret(3, 4);
+ text_edit->add_caret(0, 4);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 1), build_array(2, 3), build_array(5, 6));
+ DS->clipboard_set("paste\ntest");
+
+ text_edit->paste();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "paste\ntest");
+ CHECK(text_edit->get_text() == "thispaste\ntest is\nsopaste\ntestme\n\ntextpaste\ntest");
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->get_caret_line(0) == 3);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_caret_line(1) == 6);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ CHECK(text_edit->get_caret_line(2) == 1);
+ CHECK(text_edit->get_caret_column(2) == 4);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ text_edit->remove_secondary_carets();
+
+ // Paste line per caret when the amount of lines is equal to the number of carets.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(2);
+ text_edit->add_caret(3, 4);
+ text_edit->add_caret(0, 4);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0), build_array(1, 1), build_array(3, 3));
+ DS->clipboard_set("paste\ntest\n1");
+
+ text_edit->paste();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "paste\ntest\n1");
+ CHECK(text_edit->get_text() == "thispaste is\nsotestme\n\ntext1");
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 6);
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 5);
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 9);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ text_edit->remove_secondary_carets();
+
+ // Cannot paste when not editable.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ DS->clipboard_set("no paste");
+
+ text_edit->set_editable(false);
+ text_edit->paste();
+ text_edit->set_editable(true);
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "no paste");
+ CHECK(text_edit->get_text() == "this is\nsome\n\ntext");
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
}
SUBCASE("[TextEdit] paste primary") {
- // TODO: Cannot test need display server support.
+ // Set size for mouse input.
+ text_edit->set_size(Size2(200, 200));
+
+ text_edit->grab_focus();
+ DS->clipboard_set("");
+ DS->clipboard_set_primary("");
+ CHECK(DS->clipboard_get_primary() == "");
+
+ // Select text with mouse to put into primary clipboard.
+ text_edit->set_text("this is\nsome\n\ntext");
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 2).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 3).get_center() + Point2i(2, 0), MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(text_edit->get_rect_at_line_column(1, 3).get_center() + Point2i(2, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(DS->clipboard_get() == "");
+ CHECK(DS->clipboard_get_primary() == "is is\nsom");
+ CHECK(text_edit->get_text() == "this is\nsome\n\ntext");
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "is is\nsom");
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 2);
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 3);
+ SIGNAL_CHECK_FALSE("text_set");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Middle click to paste at mouse.
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(3, 4));
+
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(3, 2).get_center() + Point2i(2, 0), MouseButton::MIDDLE, MouseButtonMask::MIDDLE, Key::NONE);
+ CHECK(DS->clipboard_get_primary() == "is is\nsom");
+ CHECK(text_edit->get_text() == "this is\nsome\n\nteis is\nsomxt");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 4);
+ CHECK(text_edit->get_caret_column() == 3);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Paste at mouse position if there is only one caret.
+ text_edit->set_text("this is\nsome\n\ntext");
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 1).get_center() + Point2i(2, 0), MouseButtonMask::NONE, Key::NONE);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ DS->clipboard_set_primary("paste");
+ lines_edited_args = build_array(build_array(0, 0));
+
+ text_edit->paste_primary_clipboard();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get_primary() == "paste");
+ CHECK(text_edit->get_text() == "tpastehis is\nsome\n\ntext");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 6);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Paste at all carets if there are multiple carets.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(0);
+ text_edit->add_caret(2, 0);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 1).get_center() + Point2i(2, 0), MouseButtonMask::NONE, Key::NONE);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ DS->clipboard_set_primary("paste");
+ lines_edited_args = build_array(build_array(1, 1), build_array(2, 2));
+
+ text_edit->paste_primary_clipboard();
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get_primary() == "paste");
+ CHECK(text_edit->get_text() == "this is\npastesome\npaste\ntext");
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 5);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Cannot paste if not editable.
+ text_edit->set_text("this is\nsome\n\ntext");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(1, 3).get_center() + Point2i(2, 0), MouseButtonMask::NONE, Key::NONE);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ DS->clipboard_set("no paste");
+
+ text_edit->set_editable(false);
+ text_edit->paste_primary_clipboard();
+ text_edit->set_editable(true);
+ MessageQueue::get_singleton()->flush();
+ CHECK(DS->clipboard_get() == "no paste");
+ CHECK(text_edit->get_text() == "this is\nsome\n\ntext");
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 4);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
}
SIGNAL_UNWATCH(text_edit, "text_set");
@@ -1512,60 +3569,77 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_UNWATCH(text_edit, "caret_changed");
}
- // Add undo / redo tests?
SUBCASE("[TextEdit] input") {
SIGNAL_WATCH(text_edit, "text_set");
SIGNAL_WATCH(text_edit, "text_changed");
SIGNAL_WATCH(text_edit, "lines_edited_from");
SIGNAL_WATCH(text_edit, "caret_changed");
- Array args1;
- args1.push_back(0);
- args1.push_back(0);
- Array lines_edited_args;
- lines_edited_args.push_back(args1);
+ Array lines_edited_args = build_array(build_array(0, 0));
SUBCASE("[TextEdit] ui_text_newline_above") {
text_edit->set_text("this is some test text.\nthis is some test text.");
- text_edit->select(0, 0, 0, 4);
- text_edit->set_caret_column(4);
-
- text_edit->add_caret(1, 4);
- text_edit->select(1, 0, 1, 4, 1);
- CHECK(text_edit->get_caret_count() == 2);
-
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // For the second caret.
- Array args2;
- args2.push_back(0);
- args2.push_back(1);
- lines_edited_args.push_front(args2);
+ // Insert new line above.
+ text_edit->select(0, 0, 0, 4);
+ text_edit->add_caret(1, 4);
+ CHECK(text_edit->get_caret_count() == 2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 1), build_array(2, 3));
- ((Array)lines_edited_args[1])[1] = 1;
SEND_GUI_ACTION("ui_text_newline_above");
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "\nthis is some test text.\n\nthis is some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 2);
CHECK(text_edit->get_caret_column(1) == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Undo.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is some test text.\nthis is some test text.");
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "\nthis is some test text.\n\nthis is some test text.");
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 0);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Does not work if not editable.
text_edit->set_caret_line(1);
text_edit->set_caret_column(4);
-
- text_edit->set_caret_line(3, false, true, 0, 1);
+ text_edit->set_caret_line(3, false, true, -1, 1);
text_edit->set_caret_column(4, false, 1);
MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("caret_changed");
@@ -1574,31 +3648,57 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SEND_GUI_ACTION("ui_text_newline_above");
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "\nthis is some test text.\n\nthis is some test text.");
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 4);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 3);
CHECK(text_edit->get_caret_column(1) == 4);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_editable(true);
- ((Array)lines_edited_args[0])[0] = 2;
- ((Array)lines_edited_args[0])[1] = 3;
+ // Works on first line, empty lines, and only happens at caret for selections.
+ text_edit->select(1, 10, 0, 0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 1), build_array(4, 5));
SEND_GUI_ACTION("ui_text_newline_above");
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "\n\nthis is some test text.\n\n\nthis is some test text.");
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 4);
CHECK(text_edit->get_caret_column(1) == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Insert multiple new lines above from one line.
+ text_edit->set_text("test");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(1);
+ text_edit->add_caret(0, 3);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 1), build_array(1, 2));
+
+ SEND_GUI_ACTION("ui_text_newline_above");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == "\n\ntest");
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 0);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
@@ -1606,34 +3706,55 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SUBCASE("[TextEdit] ui_text_newline_blank") {
text_edit->set_text("this is some test text.\nthis is some test text.");
- text_edit->select(0, 0, 0, 4);
- text_edit->set_caret_column(4);
-
- text_edit->add_caret(1, 4);
- text_edit->select(1, 0, 1, 4, 1);
- CHECK(text_edit->get_caret_count() == 2);
-
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
- SIGNAL_DISCARD("caret_changed");
- // For the second caret.
- Array args2;
- args2.push_back(1);
- args2.push_back(2);
- lines_edited_args.push_front(args2);
+ // Insert new line below.
+ text_edit->select(0, 0, 0, 4);
+ text_edit->add_caret(1, 4);
+ CHECK(text_edit->get_caret_count() == 2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 1), build_array(2, 3));
- ((Array)lines_edited_args[1])[1] = 1;
SEND_GUI_ACTION("ui_text_newline_blank");
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "this is some test text.\n\nthis is some test text.\n");
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Undo.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is some test text.\nthis is some test text.");
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is some test text.\n\nthis is some test text.\n");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(0));
CHECK(text_edit->get_caret_line(1) == 3);
CHECK(text_edit->get_caret_column(1) == 0);
CHECK_FALSE(text_edit->has_selection(1));
@@ -1641,75 +3762,119 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Does not work if not editable.
text_edit->set_editable(false);
SEND_GUI_ACTION("ui_text_newline_blank");
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "this is some test text.\n\nthis is some test text.\n");
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 3);
CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_editable(true);
+
+ // Insert multiple new lines below from one line.
+ text_edit->set_text("test");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(1);
+ text_edit->add_caret(0, 3);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 1), build_array(0, 1));
+
+ SEND_GUI_ACTION("ui_text_newline_blank");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == "test\n\n");
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 2);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
}
SUBCASE("[TextEdit] ui_text_newline") {
text_edit->set_text("this is some test text.\nthis is some test text.");
- text_edit->select(0, 0, 0, 4);
- text_edit->set_caret_column(4);
-
- text_edit->add_caret(1, 4);
- text_edit->select(1, 0, 1, 4, 1);
- CHECK(text_edit->get_caret_count() == 2);
-
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // For the second caret.
- Array args2;
- args2.push_back(1);
- args2.push_back(1);
- lines_edited_args.push_front(args2);
- lines_edited_args.push_front(args2.duplicate());
- ((Array)lines_edited_args[1])[1] = 2;
-
- lines_edited_args.push_back(lines_edited_args[2].duplicate());
- ((Array)lines_edited_args[3])[1] = 1;
+ // Insert new line at caret.
+ text_edit->select(0, 0, 0, 4);
+ text_edit->add_caret(1, 4);
+ CHECK(text_edit->get_caret_count() == 2);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ // Lines edited: deletion, insert line, insert line.
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 1), build_array(2, 3));
SEND_GUI_ACTION("ui_text_newline");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text.");
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "\n is some test text.\nthis\n is some test text.");
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 3);
CHECK(text_edit->get_caret_column(1) == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Undo.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is some test text.\nthis is some test text.");
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "\n is some test text.\nthis\n is some test text.");
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 0);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Does not work if not editable.
text_edit->set_editable(false);
SEND_GUI_ACTION("ui_text_newline");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text.");
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "\n is some test text.\nthis\n is some test text.");
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 3);
CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -1717,255 +3882,399 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
}
SUBCASE("[TextEdit] ui_text_backspace_all_to_left") {
- text_edit->set_text("\nthis is some test text.\n\nthis is some test text.");
- text_edit->select(1, 0, 1, 4);
- text_edit->set_caret_line(1);
- text_edit->set_caret_column(4);
-
- text_edit->add_caret(3, 4);
- text_edit->select(3, 0, 3, 4, 1);
- CHECK(text_edit->get_caret_count() == 2);
-
- MessageQueue::get_singleton()->flush();
-
Ref<InputEvent> tmpevent = InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL);
InputMap::get_singleton()->action_add_event("ui_text_backspace_all_to_left", tmpevent);
+ text_edit->set_text("\nthis is some test text.\n\nthis is some test text.");
+ MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // For the second caret.
- Array args2;
- args2.push_back(3);
- args2.push_back(3);
- lines_edited_args.push_front(args2);
-
- // With selection should be a normal backspace.
- ((Array)lines_edited_args[1])[0] = 1;
- ((Array)lines_edited_args[1])[1] = 1;
+ // Remove all text to the left.
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(5);
+ text_edit->add_caret(1, 2);
+ text_edit->add_caret(1, 8);
+ lines_edited_args = build_array(build_array(1, 1));
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
SEND_GUI_ACTION("ui_text_backspace_all_to_left");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text.");
+ CHECK(text_edit->get_text() == "\nsome test text.\n\nthis is some test text.");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 1);
CHECK(text_edit->get_caret_column() == 0);
- CHECK_FALSE(text_edit->has_selection(0));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- CHECK(text_edit->get_caret_line(1) == 3);
- CHECK(text_edit->get_caret_column(1) == 0);
+ // Undo.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "\nthis is some test text.\n\nthis is some test text.");
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 5);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 2);
+ CHECK_FALSE(text_edit->has_selection(2));
+ CHECK(text_edit->get_caret_line(2) == 1);
+ CHECK(text_edit->get_caret_column(2) == 8);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- ((Array)lines_edited_args[0])[1] = 2;
- ((Array)lines_edited_args[1])[1] = 0;
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "\nsome test text.\n\nthis is some test text.");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 1);
+ CHECK(text_edit->get_caret_column() == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Acts as a normal backspace with selections.
+ text_edit->select(1, 5, 1, 9, 0);
+ text_edit->add_caret(3, 4);
+ text_edit->select(3, 7, 3, 4, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(3, 3), build_array(1, 1));
- // Start of line should also be a normal backspace.
SEND_GUI_ACTION("ui_text_backspace_all_to_left");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text.\n is some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "\nsome text.\n\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Acts as a normal backspace when at the start of a line.
+ text_edit->set_caret_column(0);
+ text_edit->set_caret_column(0, false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(3, 2), build_array(1, 0));
+ SEND_GUI_ACTION("ui_text_backspace_all_to_left");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == "some text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Does not work if not editable.
text_edit->set_caret_column(text_edit->get_line(0).length());
text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1);
MessageQueue::get_singleton()->flush();
-
- SIGNAL_DISCARD("text_set");
- SIGNAL_DISCARD("text_changed");
- SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
text_edit->set_editable(false);
SEND_GUI_ACTION("ui_text_backspace_all_to_left");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text.\n is some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length());
+ CHECK(text_edit->get_text() == "some text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == text_edit->get_line(0).length());
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == text_edit->get_line(1).length());
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_editable(true);
- ((Array)lines_edited_args[0])[0] = 1;
- ((Array)lines_edited_args[0])[1] = 1;
- ((Array)lines_edited_args[1])[0] = 0;
+ // Remove entire line content when at the end of the line.
+ lines_edited_args = build_array(build_array(1, 1), build_array(0, 0));
SEND_GUI_ACTION("ui_text_backspace_all_to_left");
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "\n");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ text_edit->remove_secondary_carets();
+
+ // Removing newline effectively happens after removing text.
+ text_edit->set_text("test\nlines");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(0);
+ text_edit->add_caret(1, 4);
+
+ SEND_GUI_ACTION("ui_text_backspace_all_to_left");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_text() == "tests");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ text_edit->remove_secondary_carets();
+
+ // Removing newline effectively happens after removing text, reverse caret order.
+ text_edit->set_text("test\nlines");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret(1, 0);
+
+ SEND_GUI_ACTION("ui_text_backspace_all_to_left");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_text() == "tests");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ text_edit->remove_secondary_carets();
InputMap::get_singleton()->action_erase_event("ui_text_backspace_all_to_left", tmpevent);
}
SUBCASE("[TextEdit] ui_text_backspace_word") {
text_edit->set_text("\nthis is some test text.\n\nthis is some test text.");
- text_edit->select(1, 0, 1, 4);
- text_edit->set_caret_line(1);
- text_edit->set_caret_column(4);
-
- text_edit->add_caret(3, 4);
- text_edit->select(3, 0, 3, 4, 1);
- CHECK(text_edit->get_caret_count() == 2);
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
- SIGNAL_DISCARD("caret_changed");
-
- // For the second caret.
- Array args2;
- args2.push_back(3);
- args2.push_back(3);
- lines_edited_args.push_front(args2);
- // With selection should be a normal backspace.
- ((Array)lines_edited_args[1])[0] = 1;
- ((Array)lines_edited_args[1])[1] = 1;
+ // Acts as a normal backspace with selections.
+ text_edit->select(1, 8, 1, 15);
+ text_edit->add_caret(3, 6);
+ text_edit->select(3, 10, 3, 6, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(3, 3), build_array(1, 1));
SEND_GUI_ACTION("ui_text_backspace_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text.");
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "\nthis is st text.\n\nthis ime test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
- CHECK(text_edit->get_caret_line(1) == 3);
- CHECK(text_edit->get_caret_column(1) == 0);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 8);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 6);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
text_edit->end_complex_operation();
- ((Array)lines_edited_args[0])[1] = 2;
- ((Array)lines_edited_args[1])[1] = 0;
+ lines_edited_args = build_array(build_array(3, 2), build_array(1, 0));
// Start of line should also be a normal backspace.
+ text_edit->set_caret_column(0);
+ text_edit->set_caret_column(0, false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
SEND_GUI_ACTION("ui_text_backspace_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text.\n is some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "this is st text.\nthis ime test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Does not work if not editable.
text_edit->set_editable(false);
SEND_GUI_ACTION("ui_text_backspace_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text.\n is some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "this is st text.\nthis ime test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_editable(true);
+ // FIXME: Remove after GH-77101 is fixed.
+ text_edit->start_action(TextEdit::ACTION_NONE);
+
+ // Remove text to the start of the word to the left of the caret.
text_edit->set_caret_column(text_edit->get_line(0).length());
- text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1);
+ text_edit->set_caret_column(12, false, 1);
MessageQueue::get_singleton()->flush();
-
- SIGNAL_DISCARD("text_set");
- SIGNAL_DISCARD("text_changed");
- SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
-
- ((Array)lines_edited_args[0])[0] = 1;
- ((Array)lines_edited_args[0])[1] = 1;
- ((Array)lines_edited_args[1])[0] = 0;
+ lines_edited_args = build_array(build_array(1, 1), build_array(0, 0));
SEND_GUI_ACTION("ui_text_backspace_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test \n is some test ");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 14);
+ CHECK(text_edit->get_text() == "this is st \nthis ime t text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 11);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 9);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Undo.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is st text.\nthis ime test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 16);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
- CHECK(text_edit->get_caret_column(1) == 14);
+ CHECK(text_edit->get_caret_column(1) == 12);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is st \nthis ime t text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 11);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 9);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- }
- SUBCASE("[TextEdit] ui_text_backspace_word same line") {
- text_edit->set_text("test test test");
- text_edit->set_caret_column(4);
- text_edit->add_caret(0, 9);
- text_edit->add_caret(0, 15);
+ // Removing newline effectively happens after removing text.
+ text_edit->set_text("test\nlines");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(0);
+ text_edit->add_caret(1, 4);
- // For the second caret.
- Array args2;
- args2.push_back(0);
- lines_edited_args.push_front(args2);
+ SEND_GUI_ACTION("ui_text_backspace_word");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_text() == "tests");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ text_edit->remove_secondary_carets();
- // For the third caret.
- Array args3;
- args2.push_back(0);
- lines_edited_args.push_front(args2);
+ // Removing newline effectively happens after removing text, reverse caret order.
+ text_edit->set_text("test\nlines");
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret(1, 0);
- CHECK(text_edit->get_caret_count() == 3);
- MessageQueue::get_singleton()->flush();
+ SEND_GUI_ACTION("ui_text_backspace_word");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_text() == "tests");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ text_edit->remove_secondary_carets();
+ }
+ SUBCASE("[TextEdit] ui_text_backspace_word same line") {
+ text_edit->set_text("test longwordtest test");
+ MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
+ // Multiple carets on the same line is handled.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret(0, 11);
+ text_edit->add_caret(0, 15);
+ text_edit->add_caret(0, 9);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 0));
+
SEND_GUI_ACTION("ui_text_backspace_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " ");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == " st test");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 0);
CHECK(text_edit->get_caret_column(1) == 1);
- CHECK_FALSE(text_edit->has_selection(1));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- CHECK(text_edit->get_caret_line(2) == 0);
- CHECK(text_edit->get_caret_column(2) == 2);
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test longwordtest test");
+ CHECK(text_edit->get_caret_count() == 4);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 11);
+ CHECK_FALSE(text_edit->has_selection(2));
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 15);
+ CHECK_FALSE(text_edit->has_selection(3));
+ CHECK(text_edit->get_caret_line(3) == 0);
+ CHECK(text_edit->get_caret_column(3) == 9);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " st test");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 1);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
@@ -1973,130 +4282,267 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SUBCASE("[TextEdit] ui_text_backspace") {
text_edit->set_text("\nthis is some test text.\n\nthis is some test text.");
- text_edit->select(1, 0, 1, 4);
- text_edit->set_caret_line(1);
- text_edit->set_caret_column(4);
-
- text_edit->add_caret(3, 4);
- text_edit->select(3, 0, 3, 4, 1);
- CHECK(text_edit->get_caret_count() == 2);
-
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
- SIGNAL_DISCARD("caret_changed");
- // For the second caret.
- Array args2;
- args2.push_back(3);
- args2.push_back(3);
- lines_edited_args.push_front(args2);
-
- // With selection should be a normal backspace.
- ((Array)lines_edited_args[1])[0] = 1;
- ((Array)lines_edited_args[1])[1] = 1;
+ // Remove selected text when there are selections.
+ text_edit->select(1, 0, 1, 4);
+ text_edit->add_caret(3, 4);
+ text_edit->select(3, 5, 3, 2, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(3, 3), build_array(1, 1));
SEND_GUI_ACTION("ui_text_backspace");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text.");
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "\n is some test text.\n\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 2);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Undo remove selection.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_text() == "\nthis is some test text.\n\nthis is some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
+ CHECK(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 3);
- CHECK(text_edit->get_caret_column(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 2);
+ CHECK(text_edit->get_selection_origin_line(1) == 3);
+ CHECK(text_edit->get_selection_origin_column(1) == 5);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+
+ // Redo remove selection.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "\n is some test text.\n\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 2);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- ((Array)lines_edited_args[0])[1] = 2;
- ((Array)lines_edited_args[1])[1] = 0;
+ // Remove the newline when at start of line.
+ text_edit->set_caret_column(0, false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(3, 2), build_array(1, 0));
- // Start of line should also be a normal backspace.
SEND_GUI_ACTION("ui_text_backspace");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text.\n is some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == " is some test text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Undo remove newline.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "\n is some test text.\n\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 3);
+ CHECK(text_edit->get_caret_column(1) == 0);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+
+ // Redo remove newline.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " is some test text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 0);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Does not work if not editable.
text_edit->set_caret_column(text_edit->get_line(0).length());
- text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1);
+ text_edit->set_caret_column(15, false, 1);
MessageQueue::get_singleton()->flush();
-
- SIGNAL_DISCARD("text_set");
- SIGNAL_DISCARD("text_changed");
- SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
text_edit->set_editable(false);
SEND_GUI_ACTION("ui_text_backspace");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text.\n is some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length());
+ CHECK(text_edit->get_text() == " is some test text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
- CHECK(text_edit->get_caret_line(1) == 1);
- CHECK(text_edit->get_caret_column(1) == text_edit->get_line(1).length());
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == text_edit->get_line(0).length());
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 15);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_editable(true);
- ((Array)lines_edited_args[0])[0] = 1;
- ((Array)lines_edited_args[0])[1] = 1;
- ((Array)lines_edited_args[1])[0] = 0;
+ // FIXME: Remove after GH-77101 is fixed.
+ text_edit->start_action(TextEdit::ACTION_NONE);
+
+ // Backspace removes character to the left.
+ lines_edited_args = build_array(build_array(1, 1), build_array(0, 0));
SEND_GUI_ACTION("ui_text_backspace");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text\n is some test text");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 18);
+ CHECK(text_edit->get_text() == " is some test text\nthis some testtext.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 18);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 14);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Backspace another character without changing caret.
+ SEND_GUI_ACTION("ui_text_backspace");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == " is some test tex\nthis some testext.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 17);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
- CHECK(text_edit->get_caret_column(1) == 18);
+ CHECK(text_edit->get_caret_column(1) == 13);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Undo both backspaces.
+ lines_edited_args = build_array(build_array(1, 1), build_array(0, 0), build_array(1, 1), build_array(0, 0));
+
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " is some test text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 19);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 15);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+
+ // Redo both backspaces.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " is some test tex\nthis some testext.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 17);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 13);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- // Select the entire text, from right to left
- text_edit->select(0, 18, 0, 0);
+ // Backspace with multiple carets that will overlap.
+ text_edit->remove_secondary_carets();
text_edit->set_caret_line(0);
- text_edit->set_caret_column(0);
-
- text_edit->select(1, 18, 1, 0, 1);
- text_edit->set_caret_line(1, false, true, 0, 1);
- text_edit->set_caret_column(0, false, 1);
+ text_edit->set_caret_column(8);
+ text_edit->add_caret(0, 7);
+ text_edit->add_caret(0, 9);
MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 0), build_array(0, 0));
- SIGNAL_DISCARD("text_set");
- SIGNAL_DISCARD("text_changed");
- SIGNAL_DISCARD("lines_edited_from");
+ SEND_GUI_ACTION("ui_text_backspace");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == " is sotest tex\nthis some testext.");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 6);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Select each line of text, from right to left. Remove selection to column 0.
+ text_edit->select(0, text_edit->get_line(0).length(), 0, 0);
+ text_edit->add_caret(1, 0);
+ text_edit->select(1, text_edit->get_line(1).length(), 1, 0, 1);
+ MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 1), build_array(0, 0));
SEND_GUI_ACTION("ui_text_backspace");
CHECK(text_edit->get_text() == "\n");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 0);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Backspace at start of first line does nothing.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ SEND_GUI_ACTION("ui_text_backspace");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == "\n");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 0);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
}
SUBCASE("[TextEdit] ui_text_delete_all_to_right") {
@@ -2104,101 +4550,138 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
InputMap::get_singleton()->action_add_event("ui_text_delete_all_to_right", tmpevent);
text_edit->set_text("this is some test text.\nthis is some test text.\n");
- text_edit->select(0, 0, 0, 4);
- text_edit->set_caret_line(0);
- text_edit->set_caret_column(4);
-
- text_edit->add_caret(1, 4);
- text_edit->select(1, 0, 1, 4, 1);
- CHECK(text_edit->get_caret_count() == 2);
-
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
- SIGNAL_DISCARD("caret_changed");
- // For the second caret.
- Array args2;
- args2.push_back(1);
- args2.push_back(1);
- lines_edited_args.push_front(args2);
+ // Remove all text to right of caret.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(18);
+ text_edit->add_caret(0, 16);
+ text_edit->add_caret(0, 20);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0));
- // With selection should be a normal delete.
SEND_GUI_ACTION("ui_text_delete_all_to_right");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text.\n is some test text.\n");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "this is some tes\nthis is some test text.\n");
+ CHECK(text_edit->get_caret_count() == 1);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 16);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- CHECK(text_edit->get_caret_line(1) == 1);
- CHECK(text_edit->get_caret_column(1) == 0);
+ // Undo.
+ lines_edited_args = build_array(build_array(0, 0));
+
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is some test text.\nthis is some test text.\n");
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 18);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 16);
+ CHECK_FALSE(text_edit->has_selection(2));
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 20);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- // End of line should not do anything.
- text_edit->set_caret_column(text_edit->get_line(0).length());
- text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1);
+ // Redo.
+ text_edit->redo();
MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is some tes\nthis is some test text.\n");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 16);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_DISCARD("text_set");
- SIGNAL_DISCARD("text_changed");
- SIGNAL_DISCARD("lines_edited_from");
+ // Acts as a normal delete with selections.
+ text_edit->select(0, 0, 0, 4);
+ text_edit->add_caret(1, 4);
+ text_edit->select(1, 8, 1, 4, 1);
+ MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0), build_array(1, 1));
SEND_GUI_ACTION("ui_text_delete_all_to_right");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text.\n is some test text.\n");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length());
+ CHECK(text_edit->get_text() == " is some tes\nthissome test text.\n");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Does nothing when caret is at end of line.
+ text_edit->set_caret_column(text_edit->get_line(0).length());
+ text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ SEND_GUI_ACTION("ui_text_delete_all_to_right");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == " is some tes\nthissome test text.\n");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == text_edit->get_line(0).length());
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == text_edit->get_line(1).length());
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
+ // Does not work if not editable.
text_edit->set_caret_column(0);
text_edit->set_caret_column(0, false, 1);
MessageQueue::get_singleton()->flush();
-
- SIGNAL_DISCARD("text_set");
- SIGNAL_DISCARD("text_changed");
- SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
text_edit->set_editable(false);
SEND_GUI_ACTION("ui_text_delete_all_to_right");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " is some test text.\n is some test text.\n");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == " is some tes\nthissome test text.\n");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_editable(true);
+ // Delete entire line.
SEND_GUI_ACTION("ui_text_delete_all_to_right");
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_text() == "\n\n");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
@@ -2210,302 +4693,589 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_caret_mid_grapheme_enabled(true);
CHECK(text_edit->is_caret_mid_grapheme_enabled());
- text_edit->set_text("this ffi some test text.\n\nthis ffi some test text.\n");
- text_edit->select(0, 0, 0, 4);
- text_edit->set_caret_line(0);
- text_edit->set_caret_column(4);
-
- text_edit->add_caret(2, 4);
- text_edit->select(2, 0, 2, 4, 1);
- CHECK(text_edit->get_caret_count() == 2);
-
+ text_edit->set_text("this is some test text.\n\nthis is some test text.\n");
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // For the second caret.
- Array args2;
- args2.push_back(2);
- args2.push_back(2);
- lines_edited_args.push_front(args2);
+ // Acts as a normal delete with selections.
+ text_edit->select(0, 8, 0, 15);
+ text_edit->add_caret(2, 6);
+ text_edit->select(2, 10, 2, 6, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0), build_array(2, 2));
- // With selection should be a normal delete.
SEND_GUI_ACTION("ui_text_delete_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " ffi some test text.\n\n ffi some test text.\n");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "this is st text.\n\nthis ime test text.\n");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
- CHECK(text_edit->get_caret_line(1) == 2);
- CHECK(text_edit->get_caret_column(1) == 0);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 8);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 6);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- // With selection should be a normal delete.
- ((Array)lines_edited_args[0])[0] = 3;
- ((Array)lines_edited_args[1])[0] = 1;
+ // Removes newlines when at end of line.
text_edit->set_caret_column(text_edit->get_line(0).length());
text_edit->set_caret_column(text_edit->get_line(2).length(), false, 1);
MessageQueue::get_singleton()->flush();
-
- SIGNAL_DISCARD("text_set");
- SIGNAL_DISCARD("text_changed");
- SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 0), build_array(2, 1));
SEND_GUI_ACTION("ui_text_delete_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " ffi some test text.\n ffi some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length());
- CHECK_FALSE(text_edit->has_selection());
-
+ CHECK(text_edit->get_text() == "this is st text.\nthis ime test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == text_edit->get_line(0).length());
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == text_edit->get_line(1).length());
- CHECK_FALSE(text_edit->has_selection(0));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- ((Array)lines_edited_args[1])[0] = 0;
- ((Array)lines_edited_args[0])[0] = 1;
- ((Array)lines_edited_args[0])[1] = 1;
+ // Does not work if not editable.
text_edit->set_caret_column(0);
- text_edit->set_caret_column(0, false, 1);
+ text_edit->set_caret_column(10, false, 1);
MessageQueue::get_singleton()->flush();
-
- SIGNAL_DISCARD("text_set");
- SIGNAL_DISCARD("text_changed");
- SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
text_edit->set_editable(false);
SEND_GUI_ACTION("ui_text_delete_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " ffi some test text.\n ffi some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "this is st text.\nthis ime test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
- CHECK(text_edit->get_caret_line(1) == 1);
- CHECK(text_edit->get_caret_column(1) == 0);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 10);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_editable(true);
+ // FIXME: Remove after GH-77101 is fixed.
+ text_edit->start_action(TextEdit::ACTION_NONE);
+
+ // Delete to the end of the word right of the caret.
+ lines_edited_args = build_array(build_array(0, 0), build_array(1, 1));
+
SEND_GUI_ACTION("ui_text_delete_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " some test text.\n some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == " is st text.\nthis ime t text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 10);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Undo.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "this is st text.\nthis ime test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
- CHECK(text_edit->get_caret_column(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 10);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " is st text.\nthis ime t text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 10);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- }
- SUBCASE("[TextEdit] ui_text_delete") {
- text_edit->set_caret_mid_grapheme_enabled(true);
- CHECK(text_edit->is_caret_mid_grapheme_enabled());
+ // Delete one word with multiple carets.
+ text_edit->remove_secondary_carets();
+ text_edit->set_text("onelongword test");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(6);
+ text_edit->add_caret(0, 9);
+ text_edit->add_caret(0, 3);
+ lines_edited_args = build_array(build_array(0, 0));
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("text_set");
+ SIGNAL_DISCARD("text_changed");
+ SIGNAL_DISCARD("lines_edited_from");
+ SIGNAL_DISCARD("caret_changed");
- text_edit->set_text("this ffi some test text.\nthis ffi some test text.");
- text_edit->select(0, 0, 0, 4);
+ SEND_GUI_ACTION("ui_text_delete_word");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == "one test");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 3);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Removing newline effectively happens after removing text.
+ text_edit->set_text("test\nlines");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(2);
+ text_edit->add_caret(0, 4);
+
+ SEND_GUI_ACTION("ui_text_delete_word");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_text() == "telines");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 2);
+ text_edit->remove_secondary_carets();
+
+ // Removing newline effectively happens after removing text, reverse caret order.
+ text_edit->set_text("test\nlines");
text_edit->set_caret_line(0);
text_edit->set_caret_column(4);
+ text_edit->add_caret(0, 2);
- text_edit->add_caret(1, 4);
- text_edit->select(1, 0, 1, 4, 1);
- CHECK(text_edit->get_caret_count() == 2);
+ SEND_GUI_ACTION("ui_text_delete_word");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_text() == "telines");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 2);
+ text_edit->remove_secondary_carets();
+ }
+ SUBCASE("[TextEdit] ui_text_delete_word same line") {
+ text_edit->set_text("test longwordtest test");
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // For the second caret.
- Array args2;
- args2.push_back(1);
- args2.push_back(1);
- lines_edited_args.push_front(args2);
+ // Multiple carets on the same line is handled.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(0);
+ text_edit->add_caret(0, 11);
+ text_edit->add_caret(0, 15);
+ text_edit->add_caret(0, 9);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
- // With selection should be a normal delete.
- SEND_GUI_ACTION("ui_text_delete");
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 0));
+
+ SEND_GUI_ACTION("ui_text_delete_word");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " ffi some test text.\n ffi some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == " long test");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
- CHECK(text_edit->get_caret_line(1) == 1);
- CHECK(text_edit->get_caret_column(1) == 0);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 5);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- // With selection should be a normal delete.
- lines_edited_args.remove_at(0);
- ((Array)lines_edited_args[0])[0] = 1;
- text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1);
- text_edit->set_caret_column(text_edit->get_line(0).length());
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 0));
+
+ text_edit->undo();
MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "test longwordtest test");
+ CHECK(text_edit->get_caret_count() == 4);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 11);
+ CHECK_FALSE(text_edit->has_selection(2));
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 15);
+ CHECK_FALSE(text_edit->has_selection(3));
+ CHECK(text_edit->get_caret_line(3) == 0);
+ CHECK(text_edit->get_caret_column(3) == 9);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " long test");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 5);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ }
+
+ SUBCASE("[TextEdit] ui_text_delete") {
+ text_edit->set_caret_mid_grapheme_enabled(true);
+ CHECK(text_edit->is_caret_mid_grapheme_enabled());
+
+ text_edit->set_text("this is some test text.\n\nthis is some test text.\n");
+ MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
+ // Remove selected text when there are selections.
+ text_edit->select(0, 0, 0, 4);
+ text_edit->add_caret(2, 2);
+ text_edit->select(2, 5, 2, 2, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(0, 0), build_array(2, 2));
+
SEND_GUI_ACTION("ui_text_delete");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " ffi some test text. ffi some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 20);
+ CHECK(text_edit->get_text() == " is some test text.\n\nthis some test text.\n");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 2);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- // Caret should be removed due to column preservation.
- CHECK(text_edit->get_caret_count() == 1);
-
- // Lets add it back.
- text_edit->set_caret_column(0);
- text_edit->add_caret(0, 20);
+ // Undo remove selection.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_text() == "this is some test text.\n\nthis is some test text.\n");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 2);
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 5);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
- ((Array)lines_edited_args[0])[0] = 0;
- lines_edited_args.push_back(args2);
- ((Array)lines_edited_args[1])[0] = 0;
- ((Array)lines_edited_args[1])[1] = 0;
+ // Redo remove selection.
+ text_edit->redo();
MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " is some test text.\n\nthis some test text.\n");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 2);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- SIGNAL_DISCARD("text_set");
- SIGNAL_DISCARD("text_changed");
- SIGNAL_DISCARD("lines_edited_from");
+ // Remove newline when at end of line.
+ text_edit->set_caret_column(text_edit->get_line(0).length());
+ text_edit->set_caret_column(text_edit->get_line(2).length(), false, 1);
+ MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("caret_changed");
+ lines_edited_args = build_array(build_array(1, 0), build_array(2, 1));
- text_edit->set_editable(false);
SEND_GUI_ACTION("ui_text_delete");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == " ffi some test text. ffi some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == " is some test text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 19);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 20);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- CHECK(text_edit->get_caret_line(1) == 0);
+ // Undo remove newline.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " is some test text.\n\nthis some test text.\n");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 19);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 2);
CHECK(text_edit->get_caret_column(1) == 20);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+
+ // Redo remove newline.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " is some test text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 19);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 20);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Does not work if not editable.
+ text_edit->set_caret_column(0);
+ text_edit->set_caret_column(15, false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ text_edit->set_editable(false);
+ SEND_GUI_ACTION("ui_text_delete");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == " is some test text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 15);
SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_editable(true);
+ // FIXME: Remove after GH-77101 is fixed.
text_edit->start_action(TextEdit::EditAction::ACTION_NONE);
+ // Delete removes character to the right.
+ lines_edited_args = build_array(build_array(0, 0), build_array(1, 1));
+
SEND_GUI_ACTION("ui_text_delete");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == "ffi some test text.ffi some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_text() == "is some test text.\nthis some test ext.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 15);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- CHECK(text_edit->get_caret_line(1) == 0);
- CHECK(text_edit->get_caret_column(1) == 19);
+ // Delete another character without changing caret.
+ SEND_GUI_ACTION("ui_text_delete");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == "s some test text.\nthis some test xt.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
- SIGNAL_CHECK("caret_changed", empty_signal_args);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 15);
+ SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
- text_edit->start_action(TextEdit::EditAction::ACTION_NONE);
+ // Undo both deletes.
+ lines_edited_args = build_array(build_array(0, 0), build_array(1, 1), build_array(0, 0), build_array(1, 1));
- SEND_GUI_ACTION("ui_text_delete");
- CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_text() == "fi some test text.fi some test text.");
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == " is some test text.\nthis some test text.");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 15);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
- CHECK(text_edit->get_caret_line(1) == 0);
- CHECK(text_edit->get_caret_column(1) == 18);
+ // Redo both deletes.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "s some test text.\nthis some test xt.");
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
- SIGNAL_CHECK("caret_changed", empty_signal_args);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 15);
+ SIGNAL_CHECK_FALSE("caret_changed");
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Delete at end of last line does nothing.
+ text_edit->remove_secondary_carets();
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(18);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ SEND_GUI_ACTION("ui_text_delete");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_text() == "s some test text.\nthis some test xt.");
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 18);
+ SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
}
SUBCASE("[TextEdit] ui_text_caret_word_left") {
text_edit->set_text("\nthis is some test text.\nthis is some test text.");
text_edit->set_caret_line(1);
- text_edit->set_caret_column(7);
-
- text_edit->add_caret(2, 7);
- CHECK(text_edit->get_caret_count() == 2);
+ text_edit->set_caret_column(15);
+ text_edit->add_caret(2, 10);
+ text_edit->select(1, 10, 1, 15);
+ text_edit->select(2, 15, 2, 10, 1);
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // Shift should select.
+ // Deselect to start of previous word when selection is right to left.
+ // Select to start of next word when selection is left to right.
#ifdef MACOS_ENABLED
SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::ALT | KeyModifierMask::SHIFT);
#else
SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT);
#endif
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 5);
- CHECK(text_edit->get_selected_text(0) == "is");
+ CHECK(text_edit->get_caret_count() == 2);
+
CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "me ");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 13);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 10);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
- CHECK(text_edit->get_caret_line(1) == 2);
- CHECK(text_edit->get_caret_column(1) == 5);
- CHECK(text_edit->get_selected_text(1) == "is");
CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == "some te");
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 8);
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 15);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // Should still move caret with selection.
- SEND_GUI_ACTION("ui_text_caret_word_left");
+ // Select to start of word with shift.
+ text_edit->deselect();
+ text_edit->set_caret_column(7);
+ text_edit->set_caret_column(16, false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+#ifdef MACOS_ENABLED
+ SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::ALT | KeyModifierMask::SHIFT);
+#else
+ SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT);
+#endif
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
- CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "is");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 7);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(0));
+
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == "tes");
CHECK(text_edit->get_caret_line(1) == 2);
- CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_column(1) == 13);
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 16);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // Normal word left.
+ // Deselect and move caret to start of next word without shift.
SEND_GUI_ACTION("ui_text_caret_word_left");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 8);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Moves to end of previous line when at start of line. Does nothing at start of text.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(0);
+ text_edit->set_caret_column(0, false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ SEND_GUI_ACTION("ui_text_caret_word_left");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 23);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2515,249 +5285,417 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_text("\nthis is some test text.\nthis is some test text.");
text_edit->set_caret_line(1);
text_edit->set_caret_column(7);
- text_edit->select(1, 2, 1, 7);
-
- text_edit->add_caret(2, 7);
- text_edit->select(2, 2, 2, 7, 1);
- CHECK(text_edit->get_caret_count() == 2);
-
+ text_edit->select(1, 3, 1, 7);
+ text_edit->add_caret(2, 3);
+ text_edit->select(2, 7, 2, 3, 1);
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // Normal left should deselect and place at selection start.
- SEND_GUI_ACTION("ui_text_caret_left");
+ // Remove one character from selection when selection is left to right.
+ // Add one character to selection when selection is right to left.
+ SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::SHIFT);
CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "s i");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 6);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 3);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 2);
- CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == "is is");
CHECK(text_edit->get_caret_line(1) == 2);
CHECK(text_edit->get_caret_column(1) == 2);
- CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 7);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // With shift should select.
- SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::SHIFT);
+ // Deselect and put caret at selection start without shift.
+ SEND_GUI_ACTION("ui_text_caret_left");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 1);
- CHECK(text_edit->get_selected_text(0) == "h");
- CHECK(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 3);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 2);
- CHECK(text_edit->get_caret_column(1) == 1);
- CHECK(text_edit->get_selected_text(1) == "h");
- CHECK(text_edit->has_selection(1));
-
+ CHECK(text_edit->get_caret_column(1) == 2);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // All ready at select left, should only deselect.
+ // Move caret one character to the left.
SEND_GUI_ACTION("ui_text_caret_left");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 1);
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 2);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 2);
CHECK(text_edit->get_caret_column(1) == 1);
- CHECK_FALSE(text_edit->has_selection(1));
-
- SIGNAL_CHECK_FALSE("caret_changed");
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // Normal left.
- SEND_GUI_ACTION("ui_text_caret_left");
+ // Select one character to the left with shift and no existing selection.
+ SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::SHIFT);
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
- CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "h");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 1);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 2);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(0));
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == "t");
CHECK(text_edit->get_caret_line(1) == 2);
CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 1);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(1));
+
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // Left at col 0 should go up a line.
+ // Moves to end of previous line when at start of line. Does nothing at start of text.
+ text_edit->deselect();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(0);
+ text_edit->set_caret_column(0, false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
SEND_GUI_ACTION("ui_text_caret_left");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 1);
CHECK(text_edit->get_caret_column(1) == 23);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- }
- SUBCASE("[TextEdit] ui_text_caret_word_right") {
- text_edit->set_text("this is some test text\n\nthis is some test text\n");
- text_edit->set_caret_line(0);
- text_edit->set_caret_column(13);
+ // Selects to end of previous line when at start of line.
+ text_edit->remove_secondary_carets();
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(0);
+ text_edit->select(1, 1, 1, 0);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
- text_edit->add_caret(2, 13);
- CHECK(text_edit->get_caret_count() == 2);
+ SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::SHIFT);
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "\nt");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 1);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(0));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+ // Merge selections when they overlap.
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(4);
+ text_edit->select(1, 6, 1, 4);
+ text_edit->add_caret(1, 8);
+ text_edit->select(1, 8, 1, 6, 1);
MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ CHECK(text_edit->get_caret_count() == 2);
+
+ SEND_GUI_KEY_EVENT(Key::LEFT | KeyModifierMask::SHIFT);
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "s is ");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 3);
+ CHECK(text_edit->get_selection_origin_line(0) == 1);
+ CHECK(text_edit->get_selection_origin_column(0) == 8);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(0));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+ }
+ SUBCASE("[TextEdit] ui_text_caret_word_right") {
+ text_edit->set_text("this is some test text\n\nthis is some test text");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(15);
+ text_edit->add_caret(2, 10);
+ text_edit->select(0, 10, 0, 15);
+ text_edit->select(2, 15, 2, 10, 1);
+ MessageQueue::get_singleton()->flush();
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // Shift should select.
+ // Select to end of next word when selection is right to left.
+ // Deselect to end of previous word when selection is left to right.
#ifdef MACOS_ENABLED
SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::ALT | KeyModifierMask::SHIFT);
#else
SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT);
#endif
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 17);
- CHECK(text_edit->get_selected_text(0) == "test");
+ CHECK(text_edit->get_caret_count() == 2);
CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "me test");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 17);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 10);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == " te");
CHECK(text_edit->get_caret_line(1) == 2);
- CHECK(text_edit->get_caret_column(1) == 17);
- CHECK(text_edit->get_selected_text(1) == "test");
+ CHECK(text_edit->get_caret_column(1) == 12);
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 15);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(1));
+
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Select to end of word with shift.
+ text_edit->deselect();
+ text_edit->set_caret_column(13);
+ text_edit->set_caret_column(15, false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+#ifdef MACOS_ENABLED
+ SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::ALT | KeyModifierMask::SHIFT);
+#else
+ SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT);
+#endif
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "test");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 17);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 13);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+
CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == "st");
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 17);
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 15);
+ CHECK(text_edit->is_caret_after_selection_origin(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // Should still move caret with selection.
+ // Deselect and move caret to end of next word without shift.
SEND_GUI_ACTION("ui_text_caret_word_right");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 22);
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 22);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 2);
CHECK(text_edit->get_caret_column(1) == 22);
- CHECK_FALSE(text_edit->has_selection(1));
-
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // Normal word right.
+ // Moves to start of next line when at end of line. Does nothing at end of text.
SEND_GUI_ACTION("ui_text_caret_word_right");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
- CHECK(text_edit->get_caret_line(1) == 3);
- CHECK(text_edit->get_caret_column(1) == 0);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 22);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
}
SUBCASE("[TextEdit] ui_text_caret_right") {
- text_edit->set_text("this is some test text\n\nthis is some test text\n");
+ text_edit->set_text("this is some test text\n\nthis is some test text");
text_edit->set_caret_line(0);
- text_edit->set_caret_column(16);
- text_edit->select(0, 16, 0, 20);
-
- text_edit->add_caret(2, 16);
- text_edit->select(2, 16, 2, 20, 1);
- CHECK(text_edit->get_caret_count() == 2);
-
+ text_edit->set_caret_column(19);
+ text_edit->select(0, 15, 0, 19);
+ text_edit->add_caret(2, 15);
+ text_edit->select(2, 19, 2, 15, 1);
MessageQueue::get_singleton()->flush();
-
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // Normal right should deselect and place at selection start.
- SEND_GUI_ACTION("ui_text_caret_right");
+ // Remove one character from selection when selection is right to left.
+ // Add one character to selection when selection is left to right.
+ SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::SHIFT);
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 20);
- CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "st te");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 20);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 15);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == "t t");
CHECK(text_edit->get_caret_line(1) == 2);
- CHECK(text_edit->get_caret_column(1) == 20);
- CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_column(1) == 16);
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 19);
+ CHECK_FALSE(text_edit->is_caret_after_selection_origin(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // With shift should select.
- SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::SHIFT);
+ // Deselect and put caret at selection end without shift.
+ SEND_GUI_ACTION("ui_text_caret_right");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 21);
- CHECK(text_edit->get_selected_text(0) == "x");
- CHECK(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 20);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 2);
- CHECK(text_edit->get_caret_column(1) == 21);
- CHECK(text_edit->get_selected_text(1) == "x");
- CHECK(text_edit->has_selection(1));
-
+ CHECK(text_edit->get_caret_column(1) == 19);
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // All ready at select right, should only deselect.
+ // Move caret one character to the right.
SEND_GUI_ACTION("ui_text_caret_right");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 21);
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 21);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 20);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
+ // Select one character to the right with shift and no existing selection.
+ SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::SHIFT);
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "t");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 22);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 21);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selected_text(1) == "x");
CHECK(text_edit->get_caret_line(1) == 2);
CHECK(text_edit->get_caret_column(1) == 21);
- CHECK_FALSE(text_edit->has_selection(1));
- SIGNAL_CHECK_FALSE("caret_changed");
+ CHECK(text_edit->get_selection_origin_line(1) == 2);
+ CHECK(text_edit->get_selection_origin_column(1) == 20);
+ CHECK(text_edit->is_caret_after_selection_origin(1));
+
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // Normal right.
+ // Moves to start of next line when at end of line. Does nothing at end of text.
+ text_edit->deselect();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(22);
+ text_edit->set_caret_column(22, false, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
SEND_GUI_ACTION("ui_text_caret_right");
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 0);
- CHECK(text_edit->get_caret_column() == 22);
+ CHECK(text_edit->get_caret_count() == 2);
CHECK_FALSE(text_edit->has_selection(0));
-
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK_FALSE(text_edit->has_selection(1));
CHECK(text_edit->get_caret_line(1) == 2);
CHECK(text_edit->get_caret_column(1) == 22);
- CHECK_FALSE(text_edit->has_selection(1));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- // Right at end col should go down a line.
- SEND_GUI_ACTION("ui_text_caret_right");
+ // Selects to start of next line when at end of line.
+ text_edit->remove_secondary_carets();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(22);
+ text_edit->select(0, 21, 0, 22);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+
+ SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::SHIFT);
CHECK(text_edit->get_viewport()->is_input_handled());
- CHECK(text_edit->get_caret_line() == 1);
- CHECK(text_edit->get_caret_column() == 0);
- CHECK_FALSE(text_edit->has_selection(0));
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == "t\n");
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 21);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK_FALSE("text_changed");
+ SIGNAL_CHECK_FALSE("lines_edited_from");
- CHECK(text_edit->get_caret_line(1) == 3);
- CHECK(text_edit->get_caret_column(1) == 0);
- CHECK_FALSE(text_edit->has_selection(1));
+ // Merge selections when they overlap.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ text_edit->select(0, 4, 0, 6);
+ text_edit->add_caret(0, 8);
+ text_edit->select(0, 6, 0, 8, 1);
+ MessageQueue::get_singleton()->flush();
+ SIGNAL_DISCARD("caret_changed");
+ CHECK(text_edit->get_caret_count() == 2);
+
+ SEND_GUI_KEY_EVENT(Key::RIGHT | KeyModifierMask::SHIFT);
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selected_text(0) == " is s");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 9);
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 4);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
@@ -2775,7 +5713,6 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_count() == 2);
MessageQueue::get_singleton()->flush();
-
CHECK(text_edit->is_line_wrapped(0));
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
@@ -3156,11 +6093,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_DISCARD("lines_edited_from");
SIGNAL_DISCARD("caret_changed");
- // For the second caret.
- Array args2;
- args2.push_back(1);
- args2.push_back(1);
- lines_edited_args.push_front(args2);
+ lines_edited_args = build_array(build_array(0, 0), build_array(1, 1));
SEND_GUI_KEY_EVENT(Key::A);
CHECK(text_edit->get_viewport()->is_input_handled());
@@ -3171,6 +6104,27 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("text_changed", empty_signal_args);
SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+ // Undo reverts both carets.
+ text_edit->undo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "a\na");
+ CHECK(text_edit->get_caret_column() == 1);
+ CHECK(text_edit->get_caret_column(1) == 1);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", reverse_nested(lines_edited_args));
+
+ // Redo.
+ text_edit->redo();
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_text() == "aA\naA");
+ CHECK(text_edit->get_caret_column() == 2);
+ CHECK(text_edit->get_caret_column(1) == 2);
+ SIGNAL_CHECK("caret_changed", empty_signal_args);
+ SIGNAL_CHECK("text_changed", empty_signal_args);
+ SIGNAL_CHECK("lines_edited_from", lines_edited_args);
+
+ // Does not work if not editable.
text_edit->set_editable(false);
SEND_GUI_KEY_EVENT(Key::A);
CHECK_FALSE(text_edit->get_viewport()->is_input_handled()); // Should this be handled?
@@ -3182,8 +6136,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK_FALSE("lines_edited_from");
text_edit->set_editable(true);
- lines_edited_args.push_back(lines_edited_args[1].duplicate());
- lines_edited_args.push_front(args2.duplicate());
+ lines_edited_args = build_array(build_array(0, 0), build_array(0, 0), build_array(1, 1), build_array(1, 1));
text_edit->select(0, 0, 0, 1);
text_edit->select(1, 0, 1, 1, 1);
@@ -3220,8 +6173,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
text_edit->set_overtype_mode_enabled(false);
CHECK_FALSE(text_edit->is_overtype_mode_enabled());
- lines_edited_args.remove_at(0);
- lines_edited_args.remove_at(1);
+ lines_edited_args = build_array(build_array(0, 0), build_array(1, 1));
SEND_GUI_KEY_EVENT(Key::TAB);
CHECK(text_edit->get_viewport()->is_input_handled());
@@ -3576,6 +6528,11 @@ TEST_CASE("[SceneTree][TextEdit] caret") {
text_edit->set_caret_column(4);
CHECK(text_edit->get_word_under_caret() == "Lorem");
+ text_edit->set_caret_column(1);
+ text_edit->add_caret(0, 15);
+ CHECK(text_edit->get_word_under_caret() == "Lorem\ndolor");
+ text_edit->remove_secondary_carets();
+
// Should this work?
text_edit->set_caret_column(5);
CHECK(text_edit->get_word_under_caret() == "");
@@ -3616,18 +6573,20 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") {
SIGNAL_DISCARD("caret_changed");
SUBCASE("[TextEdit] add remove caret") {
- // Overlapping
+ // Overlapping.
CHECK(text_edit->add_caret(0, 0) == -1);
MessageQueue::get_singleton()->flush();
SIGNAL_CHECK_FALSE("caret_changed");
- // Selection
- text_edit->select(0, 0, 2, 4);
+ // Select.
+ text_edit->select(2, 4, 0, 0);
+
+ // Cannot add in selection.
CHECK(text_edit->add_caret(0, 0) == -1);
CHECK(text_edit->add_caret(2, 4) == -1);
CHECK(text_edit->add_caret(1, 2) == -1);
- // Out of bounds
+ // Cannot add when out of bounds.
CHECK(text_edit->add_caret(-1, 0) == -1);
CHECK(text_edit->add_caret(5, 0) == -1);
CHECK(text_edit->add_caret(0, 100) == -1);
@@ -3670,23 +6629,276 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") {
ERR_PRINT_ON;
}
- SUBCASE("[TextEdit] caret index edit order") {
- Vector<int> caret_index_get_order;
- caret_index_get_order.push_back(1);
- caret_index_get_order.push_back(0);
+ SUBCASE("[TextEdit] sort carets") {
+ Vector<int> sorted_carets = { 0, 1, 2 };
- CHECK(text_edit->add_caret(1, 0));
- CHECK(text_edit->get_caret_count() == 2);
- CHECK(text_edit->get_caret_index_edit_order() == caret_index_get_order);
+ // Ascending order.
+ text_edit->remove_secondary_carets();
+ text_edit->add_caret(0, 1);
+ text_edit->add_caret(1, 0);
+ CHECK(text_edit->get_sorted_carets() == sorted_carets);
+ // Descending order.
+ sorted_carets = { 2, 1, 0 };
text_edit->remove_secondary_carets();
text_edit->set_caret_line(1);
- CHECK(text_edit->add_caret(0, 0));
+ text_edit->add_caret(0, 1);
+ text_edit->add_caret(0, 0);
+ CHECK(text_edit->get_sorted_carets() == sorted_carets);
+
+ // Mixed order.
+ sorted_carets = { 0, 2, 1, 3 };
+ text_edit->remove_secondary_carets();
+ text_edit->set_caret_line(0);
+ text_edit->add_caret(1, 0);
+ text_edit->add_caret(0, 1);
+ text_edit->add_caret(1, 1);
+ CHECK(text_edit->get_sorted_carets() == sorted_carets);
+
+ // Overlapping carets.
+ sorted_carets = { 0, 1, 3, 2 };
+ text_edit->remove_secondary_carets();
+ text_edit->add_caret(0, 1);
+ text_edit->add_caret(1, 2);
+ text_edit->add_caret(0, 2);
+ text_edit->set_caret_column(1, false, 3);
+ CHECK(text_edit->get_sorted_carets() == sorted_carets);
+
+ // Sorted by selection start.
+ sorted_carets = { 1, 0 };
+ text_edit->remove_secondary_carets();
+ text_edit->select(1, 3, 1, 5);
+ text_edit->add_caret(2, 0);
+ text_edit->select(1, 0, 2, 0, 1);
+ CHECK(text_edit->get_sorted_carets() == sorted_carets);
+ }
+
+ SUBCASE("[TextEdit] merge carets") {
+ text_edit->set_text("this is some text\nfor selection");
+ MessageQueue::get_singleton()->flush();
+
+ // Don't merge carets that are not overlapping.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret(0, 6);
+ text_edit->add_caret(1, 6);
+ text_edit->merge_overlapping_carets();
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 6);
+ CHECK(text_edit->get_caret_line(2) == 1);
+ CHECK(text_edit->get_caret_column(2) == 6);
+ text_edit->remove_secondary_carets();
+
+ // Don't merge when in a multicaret edit.
+ text_edit->begin_multicaret_edit();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret(0, 4);
+ text_edit->merge_overlapping_carets();
+ CHECK(text_edit->is_in_mulitcaret_edit());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 4);
+
+ // Merge overlapping carets. Merge at the end of the multicaret edit.
+ text_edit->end_multicaret_edit();
+ CHECK_FALSE(text_edit->is_in_mulitcaret_edit());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+
+ // Don't merge selections that are not overlapping.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret(0, 2);
+ text_edit->add_caret(1, 4);
+ text_edit->select(0, 4, 1, 2, 0);
+ text_edit->select(0, 2, 0, 3, 1);
+ text_edit->select(1, 4, 1, 8, 2);
+ text_edit->merge_overlapping_carets();
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->has_selection(2));
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+
+ // Don't merge selections that are only touching.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret(1, 2);
+ text_edit->select(0, 4, 1, 2, 0);
+ text_edit->select(1, 2, 1, 5, 1);
+ text_edit->merge_overlapping_carets();
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->has_selection(1));
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+
+ // Merge carets into selection.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(3);
+ text_edit->add_caret(0, 2);
+ text_edit->add_caret(1, 4);
+ text_edit->add_caret(1, 8);
+ text_edit->add_caret(1, 10);
+ text_edit->select(0, 2, 1, 8, 0);
+ text_edit->merge_overlapping_carets();
CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_from_line(0) == 0);
+ CHECK(text_edit->get_selection_from_column(0) == 2);
+ CHECK(text_edit->get_selection_to_line(0) == 1);
+ CHECK(text_edit->get_selection_to_column(0) == 8);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 10);
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
- caret_index_get_order.write[0] = 0;
- caret_index_get_order.write[1] = 1;
- CHECK(text_edit->get_caret_index_edit_order() == caret_index_get_order);
+ // Merge partially overlapping selections.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(1);
+ text_edit->add_caret(0, 2);
+ text_edit->add_caret(0, 3);
+ text_edit->select(0, 2, 0, 6, 0);
+ text_edit->select(0, 4, 1, 3, 1);
+ text_edit->select(1, 0, 1, 5, 2);
+ text_edit->merge_overlapping_carets();
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_from_line(0) == 0);
+ CHECK(text_edit->get_selection_from_column(0) == 2);
+ CHECK(text_edit->get_selection_to_line(0) == 1);
+ CHECK(text_edit->get_selection_to_column(0) == 5);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+
+ // Merge smaller overlapping selection into a bigger one.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(1);
+ text_edit->add_caret(0, 2);
+ text_edit->add_caret(0, 3);
+ text_edit->select(0, 2, 0, 6, 0);
+ text_edit->select(0, 8, 1, 3, 1);
+ text_edit->select(0, 2, 1, 5, 2);
+ text_edit->merge_overlapping_carets();
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_from_line(0) == 0);
+ CHECK(text_edit->get_selection_from_column(0) == 2);
+ CHECK(text_edit->get_selection_to_line(0) == 1);
+ CHECK(text_edit->get_selection_to_column(0) == 5);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+
+ // Merge equal overlapping selections.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(1);
+ text_edit->add_caret(0, 2);
+ text_edit->select(0, 2, 1, 6, 0);
+ text_edit->select(0, 2, 1, 6, 1);
+ text_edit->merge_overlapping_carets();
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_from_line(0) == 0);
+ CHECK(text_edit->get_selection_from_column(0) == 2);
+ CHECK(text_edit->get_selection_to_line(0) == 1);
+ CHECK(text_edit->get_selection_to_column(0) == 6);
+ CHECK(text_edit->is_caret_after_selection_origin(0));
+ }
+
+ SUBCASE("[TextEdit] collapse carets") {
+ text_edit->set_text("this is some text\nfor selection");
+
+ // Collapse carets in range, dont affect other carets.
+ text_edit->add_caret(0, 9);
+ text_edit->add_caret(1, 0);
+ text_edit->add_caret(1, 2);
+ text_edit->add_caret(1, 6);
+ text_edit->begin_multicaret_edit();
+
+ text_edit->collapse_carets(0, 8, 1, 2);
+ CHECK(text_edit->get_caret_count() == 5);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 8);
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 8);
+ CHECK(text_edit->get_caret_line(3) == 1);
+ CHECK(text_edit->get_caret_column(3) == 2);
+ CHECK(text_edit->get_caret_line(4) == 1);
+ CHECK(text_edit->get_caret_column(4) == 6);
+ CHECK_FALSE(text_edit->multicaret_edit_ignore_caret(0));
+ CHECK(text_edit->multicaret_edit_ignore_caret(1));
+ CHECK(text_edit->multicaret_edit_ignore_caret(2));
+ CHECK_FALSE(text_edit->multicaret_edit_ignore_caret(3));
+ CHECK_FALSE(text_edit->multicaret_edit_ignore_caret(4));
+
+ // Collapsed carets get merged at the end of the edit.
+ text_edit->end_multicaret_edit();
+ CHECK(text_edit->get_caret_count() == 4);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 8);
+ CHECK(text_edit->get_caret_line(2) == 1);
+ CHECK(text_edit->get_caret_column(2) == 2);
+ CHECK(text_edit->get_caret_line(3) == 1);
+ CHECK(text_edit->get_caret_column(3) == 6);
+ text_edit->remove_secondary_carets();
+
+ // Collapse inclusive.
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(3);
+ text_edit->add_caret(1, 2);
+ text_edit->collapse_carets(0, 3, 1, 2, true);
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 3);
+ text_edit->remove_secondary_carets();
+
+ // Deselect if selection was encompassed.
+ text_edit->select(0, 5, 0, 7);
+ text_edit->collapse_carets(0, 3, 1, 2);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 3);
+
+ // Clamp only caret end of selection.
+ text_edit->select(0, 1, 0, 7);
+ text_edit->collapse_carets(0, 3, 1, 2);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 3);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 1);
+ text_edit->deselect();
+
+ // Clamp only selection origin end of selection.
+ text_edit->select(0, 7, 0, 1);
+ text_edit->collapse_carets(0, 3, 1, 2);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 1);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 3);
+ text_edit->deselect();
}
SUBCASE("[TextEdit] add caret at carets") {
@@ -3694,36 +6906,325 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") {
text_edit->set_caret_line(1);
text_edit->set_caret_column(9);
+ // Add caret below. Column will clamp.
text_edit->add_caret_at_carets(true);
CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 9);
CHECK(text_edit->get_caret_line(1) == 2);
CHECK(text_edit->get_caret_column(1) == 4);
+ // Cannot add below when at last line.
text_edit->add_caret_at_carets(true);
CHECK(text_edit->get_caret_count() == 2);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 9);
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ // Add caret above. Column will clamp.
text_edit->add_caret_at_carets(false);
CHECK(text_edit->get_caret_count() == 3);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 9);
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 4);
CHECK(text_edit->get_caret_line(2) == 0);
CHECK(text_edit->get_caret_column(2) == 7);
+ // Cannot add above when at first line.
+ text_edit->add_caret_at_carets(false);
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 9);
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 7);
+
+ // Cannot add below when at the last line for selection.
+ text_edit->remove_secondary_carets();
+ text_edit->select(2, 1, 2, 4);
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 2);
+ CHECK(text_edit->get_selection_origin_column(0) == 1);
+ CHECK(text_edit->get_caret_line(0) == 2);
+ CHECK(text_edit->get_caret_column(0) == 4);
+
+ // Cannot add above when at the first line for selection.
+ text_edit->select(0, 1, 0, 4);
+ text_edit->add_caret_at_carets(false);
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+
+ // Add selection below.
+ text_edit->select(0, 0, 0, 4);
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selection_origin_line(1) == 1);
+ CHECK(text_edit->get_selection_origin_column(1) == 0);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 3); // In the default font, this is the same position.
+
+ // Add selection below again.
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 0);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selection_origin_line(1) == 1);
+ CHECK(text_edit->get_selection_origin_column(1) == 0);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 3);
+ CHECK(text_edit->has_selection(2));
+ CHECK(text_edit->get_selection_origin_line(2) == 2);
+ CHECK(text_edit->get_selection_origin_column(2) == 0);
+ CHECK(text_edit->get_caret_line(2) == 2);
+ CHECK(text_edit->get_caret_column(2) == 4);
+
+ text_edit->set_text("\tthis is\nsome\n\ttest text");
+ MessageQueue::get_singleton()->flush();
+
+ // Last fit x is preserved when adding below.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(6);
+ text_edit->add_caret_at_carets(true);
+ text_edit->add_caret_at_carets(true);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 6);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ CHECK(text_edit->get_caret_line(2) == 2);
+ CHECK(text_edit->get_caret_column(2) == 6);
+
+ // Last fit x is preserved when adding above.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->set_caret_line(2);
+ text_edit->set_caret_column(9);
+ text_edit->add_caret_at_carets(false);
+ text_edit->add_caret_at_carets(false);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->get_caret_line(0) == 2);
+ CHECK(text_edit->get_caret_column(0) == 9);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 8);
+
+ // Last fit x is preserved when selection adding below.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->select(0, 8, 0, 5);
+ text_edit->add_caret_at_carets(true);
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 8);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ CHECK(text_edit->has_selection(2));
+ CHECK(text_edit->get_selection_origin_line(2) == 2);
+ CHECK(text_edit->get_selection_origin_column(2) == 7);
+ CHECK(text_edit->get_caret_line(2) == 2);
+ CHECK(text_edit->get_caret_column(2) == 5);
+
+ // Last fit x is preserved when selection adding above.
text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->select(2, 9, 2, 5);
+ text_edit->add_caret_at_carets(false);
+ text_edit->add_caret_at_carets(false);
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 2);
+ CHECK(text_edit->get_selection_origin_column(0) == 9);
+ CHECK(text_edit->get_caret_line(0) == 2);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ CHECK_FALSE(text_edit->has_selection(1));
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ CHECK(text_edit->has_selection(2));
+ CHECK(text_edit->get_selection_origin_line(2) == 0);
+ CHECK(text_edit->get_selection_origin_column(2) == 8);
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 5);
+
+ // Selections are merged when they overlap.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->select(0, 1, 0, 5);
+ text_edit->add_caret(1, 0);
+ text_edit->select(1, 1, 1, 3, 1);
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selection_origin_line(1) == 1);
+ CHECK(text_edit->get_selection_origin_column(1) == 1);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 4);
+ CHECK(text_edit->has_selection(2));
+ CHECK(text_edit->get_selection_origin_line(2) == 2);
+ CHECK(text_edit->get_selection_origin_column(2) == 0);
+ CHECK(text_edit->get_caret_line(2) == 2);
+ CHECK(text_edit->get_caret_column(2) == 3);
+
+ // Multiline selection.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(1);
+ text_edit->select(0, 3, 1, 1);
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->has_selection(0));
+ CHECK(text_edit->get_selection_origin_line(0) == 0);
+ CHECK(text_edit->get_selection_origin_column(0) == 3);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 1);
+ CHECK(text_edit->has_selection(1));
+ CHECK(text_edit->get_selection_origin_line(1) == 1);
+ CHECK(text_edit->get_selection_origin_column(1) == 3);
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 0);
+
+ text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
+ text_edit->set_size(Size2(50, 100));
+ // Line wraps: `\t,this, is\nso,me\n\t,test, ,text`.
+ CHECK(text_edit->is_line_wrapped(0));
+ MessageQueue::get_singleton()->flush();
+
+ // Add caret below on next line wrap.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
text_edit->set_caret_line(0);
text_edit->set_caret_column(4);
- text_edit->select(0, 0, 0, 4);
text_edit->add_caret_at_carets(true);
+ CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_count() == 2);
- CHECK(text_edit->get_selection_from_line(1) == 1);
- CHECK(text_edit->get_selection_to_line(1) == 1);
- CHECK(text_edit->get_selection_from_column(1) == 0);
- CHECK(text_edit->get_selection_to_column(1) == 3);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 8);
+ // Add caret below from end of line wrap.
text_edit->add_caret_at_carets(true);
+ CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_count() == 3);
- CHECK(text_edit->get_selection_from_line(2) == 2);
- CHECK(text_edit->get_selection_to_line(2) == 2);
- CHECK(text_edit->get_selection_from_column(2) == 0);
- CHECK(text_edit->get_selection_to_column(2) == 4);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_caret_line(1) == 0);
+ CHECK(text_edit->get_caret_column(1) == 8);
+ CHECK(text_edit->get_caret_line(2) == 1);
+ CHECK(text_edit->get_caret_column(2) == 1);
+
+ // Add caret below from last line and not last line wrap.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->set_caret_line(2);
+ text_edit->set_caret_column(5);
+ text_edit->add_caret_at_carets(true);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_caret_line(0) == 2);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 10);
+
+ // Cannot add caret below from last line last line wrap.
+ text_edit->add_caret_at_carets(true);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_caret_line(0) == 2);
+ CHECK(text_edit->get_caret_column(0) == 5);
+ CHECK(text_edit->get_caret_line(1) == 2);
+ CHECK(text_edit->get_caret_column(1) == 10);
+
+ // Add caret above from not first line wrap.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->set_caret_line(1);
+ text_edit->set_caret_column(4);
+ text_edit->add_caret_at_carets(false);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 2);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 1);
+
+ // Add caret above from first line wrap.
+ text_edit->add_caret_at_carets(false);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 3);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 1);
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 8);
+
+ // Add caret above from first line and not first line wrap.
+ text_edit->add_caret_at_carets(false);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 4);
+ CHECK(text_edit->get_caret_line(0) == 1);
+ CHECK(text_edit->get_caret_column(0) == 4);
+ CHECK(text_edit->get_caret_line(1) == 1);
+ CHECK(text_edit->get_caret_column(1) == 1);
+ CHECK(text_edit->get_caret_line(2) == 0);
+ CHECK(text_edit->get_caret_column(2) == 8);
+ CHECK(text_edit->get_caret_line(3) == 0);
+ CHECK(text_edit->get_caret_column(3) == 4);
+
+ // Cannot add caret above from first line first line wrap.
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(0);
+ text_edit->add_caret_at_carets(false);
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
+
+ // Does nothing if multiple carets are disabled.
+ text_edit->set_multiple_carets_enabled(false);
+ text_edit->add_caret_at_carets(true);
+ CHECK(text_edit->get_caret_count() == 1);
}
memdelete(text_edit);
@@ -3992,7 +7493,7 @@ TEST_CASE("[SceneTree][TextEdit] viewport") {
CHECK(text_edit->get_last_full_visible_line() == visible_lines - 1);
CHECK(text_edit->get_last_full_visible_line_wrap_index() == 0);
- // Wrap
+ // Wrap.
text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
MessageQueue::get_singleton()->flush();
CHECK(text_edit->get_total_visible_line_count() > total_visible_lines);
@@ -4242,7 +7743,7 @@ TEST_CASE("[SceneTree][TextEdit] viewport") {
CHECK(text_edit->get_last_full_visible_line_wrap_index() == 0);
CHECK(text_edit->get_caret_wrap_index() == 0);
- // Typing and undo / redo should adjust viewport
+ // Typing and undo / redo should adjust viewport.
text_edit->set_caret_line(0);
text_edit->set_caret_column(0);
text_edit->set_line_as_first_visible(5);
diff --git a/tests/scene/test_timer.h b/tests/scene/test_timer.h
new file mode 100644
index 0000000000..913ed92de5
--- /dev/null
+++ b/tests/scene/test_timer.h
@@ -0,0 +1,217 @@
+/**************************************************************************/
+/* test_timer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_TIMER_H
+#define TEST_TIMER_H
+
+#include "scene/main/timer.h"
+
+#include "tests/test_macros.h"
+
+namespace TestTimer {
+
+TEST_CASE("[SceneTree][Timer] Check Timer Setters and Getters") {
+ Timer *test_timer = memnew(Timer);
+
+ SUBCASE("[Timer] Timer set and get wait time") {
+ // check default
+ CHECK(Math::is_equal_approx(test_timer->get_wait_time(), 1.0));
+
+ test_timer->set_wait_time(50.0);
+ CHECK(Math::is_equal_approx(test_timer->get_wait_time(), 50.0));
+
+ test_timer->set_wait_time(42.0);
+ CHECK(Math::is_equal_approx(test_timer->get_wait_time(), 42.0));
+
+ // wait time remains unchanged if we attempt to set it negative or zero
+ ERR_PRINT_OFF;
+ test_timer->set_wait_time(-22.0);
+ ERR_PRINT_ON;
+ CHECK(Math::is_equal_approx(test_timer->get_wait_time(), 42.0));
+
+ ERR_PRINT_OFF;
+ test_timer->set_wait_time(0.0);
+ ERR_PRINT_ON;
+ CHECK(Math::is_equal_approx(test_timer->get_wait_time(), 42.0));
+ }
+
+ SUBCASE("[Timer] Timer set and get one shot") {
+ // check default
+ CHECK(test_timer->is_one_shot() == false);
+
+ test_timer->set_one_shot(true);
+ CHECK(test_timer->is_one_shot() == true);
+
+ test_timer->set_one_shot(false);
+ CHECK(test_timer->is_one_shot() == false);
+ }
+
+ SUBCASE("[Timer] Timer set and get autostart") {
+ // check default
+ CHECK(test_timer->has_autostart() == false);
+
+ test_timer->set_autostart(true);
+ CHECK(test_timer->has_autostart() == true);
+
+ test_timer->set_autostart(false);
+ CHECK(test_timer->has_autostart() == false);
+ }
+
+ SUBCASE("[Timer] Timer start and stop") {
+ test_timer->set_autostart(false);
+ }
+
+ SUBCASE("[Timer] Timer set and get paused") {
+ // check default
+ CHECK(test_timer->is_paused() == false);
+
+ test_timer->set_paused(true);
+ CHECK(test_timer->is_paused() == true);
+
+ test_timer->set_paused(false);
+ CHECK(test_timer->is_paused() == false);
+ }
+
+ memdelete(test_timer);
+}
+
+TEST_CASE("[SceneTree][Timer] Check Timer Start and Stop") {
+ Timer *test_timer = memnew(Timer);
+
+ SUBCASE("[Timer] Timer start and stop") {
+ SceneTree::get_singleton()->get_root()->add_child(test_timer);
+
+ test_timer->start(5.0);
+
+ CHECK(Math::is_equal_approx(test_timer->get_wait_time(), 5.0));
+ CHECK(Math::is_equal_approx(test_timer->get_time_left(), 5.0));
+
+ test_timer->start(-2.0);
+
+ // the wait time and time left remains unchanged when started with a negative start time
+ CHECK(Math::is_equal_approx(test_timer->get_wait_time(), 5.0));
+ CHECK(Math::is_equal_approx(test_timer->get_time_left(), 5.0));
+
+ test_timer->stop();
+ CHECK(test_timer->is_processing() == false);
+ CHECK(test_timer->has_autostart() == false);
+ }
+
+ memdelete(test_timer);
+}
+
+TEST_CASE("[SceneTree][Timer] Check Timer process callback") {
+ Timer *test_timer = memnew(Timer);
+
+ SUBCASE("[Timer] Timer process callback") {
+ // check default
+ CHECK(test_timer->get_timer_process_callback() == Timer::TimerProcessCallback::TIMER_PROCESS_IDLE);
+
+ test_timer->set_timer_process_callback(Timer::TimerProcessCallback::TIMER_PROCESS_PHYSICS);
+ CHECK(test_timer->get_timer_process_callback() == Timer::TimerProcessCallback::TIMER_PROCESS_PHYSICS);
+
+ test_timer->set_timer_process_callback(Timer::TimerProcessCallback::TIMER_PROCESS_IDLE);
+ CHECK(test_timer->get_timer_process_callback() == Timer::TimerProcessCallback::TIMER_PROCESS_IDLE);
+ }
+
+ memdelete(test_timer);
+}
+
+TEST_CASE("[SceneTree][Timer] Check Timer timeout signal") {
+ Timer *test_timer = memnew(Timer);
+ SceneTree::get_singleton()->get_root()->add_child(test_timer);
+
+ test_timer->set_process(true);
+ test_timer->set_physics_process(true);
+
+ SUBCASE("[Timer] Timer process timeout signal must be emitted") {
+ SIGNAL_WATCH(test_timer, SNAME("timeout"));
+ test_timer->start(0.1);
+
+ SceneTree::get_singleton()->process(0.2);
+
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ SIGNAL_CHECK(SNAME("timeout"), signal_args);
+
+ SIGNAL_UNWATCH(test_timer, SNAME("timeout"));
+ }
+
+ SUBCASE("[Timer] Timer process timeout signal must not be emitted") {
+ SIGNAL_WATCH(test_timer, SNAME("timeout"));
+ test_timer->start(0.1);
+
+ SceneTree::get_singleton()->process(0.05);
+
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ SIGNAL_CHECK_FALSE(SNAME("timeout"));
+
+ SIGNAL_UNWATCH(test_timer, SNAME("timeout"));
+ }
+
+ test_timer->set_timer_process_callback(Timer::TimerProcessCallback::TIMER_PROCESS_PHYSICS);
+
+ SUBCASE("[Timer] Timer physics process timeout signal must be emitted") {
+ SIGNAL_WATCH(test_timer, SNAME("timeout"));
+ test_timer->start(0.1);
+
+ SceneTree::get_singleton()->physics_process(0.2);
+
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ SIGNAL_CHECK(SNAME("timeout"), signal_args);
+
+ SIGNAL_UNWATCH(test_timer, SNAME("timeout"));
+ }
+
+ SUBCASE("[Timer] Timer physics process timeout signal must not be emitted") {
+ SIGNAL_WATCH(test_timer, SNAME("timeout"));
+ test_timer->start(0.1);
+
+ SceneTree::get_singleton()->physics_process(0.05);
+
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ SIGNAL_CHECK_FALSE(SNAME("timeout"));
+
+ SIGNAL_UNWATCH(test_timer, SNAME("timeout"));
+ }
+
+ memdelete(test_timer);
+}
+
+} // namespace TestTimer
+
+#endif // TEST_TIMER_H
diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h
index 66b0f438cc..1341cc0332 100644
--- a/tests/scene/test_viewport.h
+++ b/tests/scene/test_viewport.h
@@ -548,8 +548,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification propagation when moving into child.") {
- SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -568,8 +568,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Move to child Control node_j. node_i should not receive any new Mouse Enter signals.
SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
@@ -577,8 +577,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Move to parent Control node_i. node_i should not receive any new Mouse Enter signals.
SEND_GUI_MOUSE_MOTION_EVENT(on_i, MouseButtonMask::NONE, Key::NONE);
@@ -586,8 +586,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Move to background.
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
@@ -595,16 +595,16 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), signal_args);
CHECK_FALSE(node_i->invalid_order);
CHECK_FALSE(node_j->invalid_order);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification propagation with top level.") {
@@ -756,8 +756,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when changing top level.") {
- SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -794,8 +794,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to top level. node_h should receive Mouse Exit. node_i should not receive any new signals.
node_i->set_as_top_level(true);
@@ -805,8 +805,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to not top level. node_h should receive Mouse Enter. node_i should not receive any new signals.
node_i->set_as_top_level(false);
@@ -816,8 +816,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_b->invalid_order);
CHECK_FALSE(node_d->invalid_order);
@@ -830,13 +830,13 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when changing the mouse filter to stop.") {
- SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -851,8 +851,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to MOUSE_FILTER_STOP. node_h should receive Mouse Exit. node_i should not receive any new signals.
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
@@ -862,8 +862,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to MOUSE_FILTER_PASS. node_h should receive Mouse Enter. node_i should not receive any new signals.
node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
@@ -873,8 +873,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_h->invalid_order);
CHECK_FALSE(node_i->invalid_order);
@@ -883,13 +883,13 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when changing the mouse filter to ignore.") {
- SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -904,8 +904,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to MOUSE_FILTER_IGNORE. node_i should receive Mouse Exit.
node_i->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
@@ -915,8 +915,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), signal_args);
// Change node_i to MOUSE_FILTER_PASS. node_i should receive Mouse Enter.
node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
@@ -926,8 +926,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_j to MOUSE_FILTER_IGNORE. After updating the mouse motion, node_i should now have mouse_over_self.
node_j->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
@@ -938,8 +938,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_j to MOUSE_FILTER_PASS. After updating the mouse motion, node_j should now have mouse_over_self.
node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
@@ -950,8 +950,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_h->invalid_order);
CHECK_FALSE(node_i->invalid_order);
@@ -960,13 +960,13 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when removing the hovered Control.") {
- SIGNAL_WATCH(node_h, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_h, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_h, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_h, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -981,8 +981,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Remove node_i from the tree. node_i and node_j should receive Mouse Exit. node_h should not receive any new signals.
node_h->remove_child(node_i);
@@ -992,8 +992,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Add node_i to the tree and update the mouse. node_i and node_j should receive Mouse Enter. node_h should not receive any new signals.
node_h->add_child(node_i);
@@ -1004,8 +1004,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_h->invalid_order);
CHECK_FALSE(node_i->invalid_order);
@@ -1014,13 +1014,13 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_h, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_h, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_h, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_h, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when hiding the hovered Control.") {
- SIGNAL_WATCH(node_h, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_h, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_h, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_h, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -1035,8 +1035,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Hide node_i. node_i and node_j should receive Mouse Exit. node_h should not receive any new signals.
node_i->hide();
@@ -1046,8 +1046,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Show node_i and update the mouse. node_i and node_j should receive Mouse Enter. node_h should not receive any new signals.
node_i->show();
@@ -1058,8 +1058,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_h->invalid_order);
CHECK_FALSE(node_i->invalid_order);
@@ -1068,26 +1068,26 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_h, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_h, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_h, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_h, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Window Mouse Enter/Exit signals.") {
- SIGNAL_WATCH(root, SNAME("mouse_entered"));
- SIGNAL_WATCH(root, SNAME("mouse_exited"));
+ SIGNAL_WATCH(root, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(root, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
SEND_GUI_MOUSE_MOTION_EVENT(on_outside, MouseButtonMask::NONE, Key::NONE);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), signal_args);
SEND_GUI_MOUSE_MOTION_EVENT(on_a, MouseButtonMask::NONE, Key::NONE);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
- SIGNAL_UNWATCH(root, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(root, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(root, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(root, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Process-Mode affects, if GUI Mouse Motion Events are processed.") {
@@ -1415,9 +1415,9 @@ public:
Ref<InputEvent> last_input_event;
void init_signals() {
- connect(SNAME("mouse_entered"), callable_mp(this, &TestArea2D::_on_mouse_entered));
- connect(SNAME("mouse_exited"), callable_mp(this, &TestArea2D::_on_mouse_exited));
- connect(SNAME("input_event"), callable_mp(this, &TestArea2D::_on_input_event));
+ connect(SceneStringName(mouse_entered), callable_mp(this, &TestArea2D::_on_mouse_entered));
+ connect(SceneStringName(mouse_exited), callable_mp(this, &TestArea2D::_on_mouse_exited));
+ connect(SceneStringName(input_event), callable_mp(this, &TestArea2D::_on_input_event));
}
void test_reset() {
@@ -1459,8 +1459,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
pc.a->set_name("A" + itos(i));
pc.c->set_name("C" + itos(i));
v.push_back(pc);
- SIGNAL_WATCH(pc.a, SNAME("mouse_entered"));
- SIGNAL_WATCH(pc.a, SNAME("mouse_exited"));
+ SIGNAL_WATCH(pc.a, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(pc.a, SceneStringName(mouse_exited));
}
Node2D *node_a = memnew(Node2D);
@@ -1499,8 +1499,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
SUBCASE("[Viewport][Picking2D] Mouse Motion") {
SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
tree->physics_process(1);
- SIGNAL_CHECK(SNAME("mouse_entered"), empty_signal_args_4);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), empty_signal_args_4);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
for (PickingCollider E : v) {
CHECK(E.a->enter_id);
CHECK_FALSE(E.a->exit_id);
@@ -1509,8 +1509,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
SEND_GUI_MOUSE_MOTION_EVENT(on_01, MouseButtonMask::NONE, Key::NONE);
tree->physics_process(1);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), empty_signal_args_2);
for (int i = 0; i < v.size(); i++) {
CHECK_FALSE(v[i].a->enter_id);
@@ -1524,8 +1524,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
SEND_GUI_MOUSE_MOTION_EVENT(on_outside, MouseButtonMask::NONE, Key::NONE);
tree->physics_process(1);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), empty_signal_args_2);
for (int i = 0; i < v.size(); i++) {
CHECK_FALSE(v[i].a->enter_id);
if (i < 2) {
@@ -1788,8 +1788,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
}
for (PickingCollider E : v) {
- SIGNAL_UNWATCH(E.a, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(E.a, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(E.a, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(E.a, SceneStringName(mouse_exited));
memdelete(E.c);
memdelete(E.a);
}
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index 827a1bed17..8778ea86a6 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -764,6 +764,8 @@ TEST_SUITE("[Navigation]") {
navigation_server->process(0.0); // Give server some cycles to commit.
}
+ // FIXME: The race condition mentioned below is actually a problem and fails on CI (GH-90613).
+ /*
TEST_CASE("[NavigationServer3D] Server should be able to bake asynchronously") {
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
@@ -781,6 +783,7 @@ TEST_SUITE("[Navigation]") {
CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
}
+ */
}
} //namespace TestNavigationServer3D
diff --git a/tests/servers/test_text_server.h b/tests/servers/test_text_server.h
index 334c642d26..d982102a03 100644
--- a/tests/servers/test_text_server.h
+++ b/tests/servers/test_text_server.h
@@ -637,6 +637,97 @@ TEST_SUITE("[TextServer]") {
}
}
+ SUBCASE("[TextServer] Unicode letters") {
+ for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
+ Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
+ CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
+
+ struct ul_testcase {
+ int fail_index = -1; // Expecting failure at given index.
+ char32_t text[10]; // Using 0 as the terminator.
+ };
+ ul_testcase cases[14] = {
+ {
+ 0,
+ { 0x2D, 0x33, 0x30, 0, 0, 0, 0, 0, 0, 0 }, // "-30"
+ },
+ {
+ 1,
+ { 0x61, 0x2E, 0x31, 0, 0, 0, 0, 0, 0, 0 }, // "a.1"
+ },
+ {
+ 1,
+ { 0x61, 0x2C, 0x31, 0, 0, 0, 0, 0, 0, 0 }, // "a,1"
+ },
+ {
+ 0,
+ { 0x31, 0x65, 0x2D, 0x32, 0, 0, 0, 0, 0, 0 }, // "1e-2"
+ },
+ {
+ 0,
+ { 0xAB, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // "Left-Pointing Double Angle Quotation Mark"
+ },
+ {
+ -1,
+ { 0x41, 0x42, 0, 0, 0, 0, 0, 0, 0, 0 }, // "AB"
+ },
+ {
+ 4,
+ { 0x54, 0x65, 0x73, 0x74, 0x31, 0, 0, 0, 0, 0 }, // "Test1"
+ },
+ {
+ 2,
+ { 0x54, 0x65, 0x2A, 0x73, 0x74, 0, 0, 0, 0, 0 }, // "Te*st"
+ },
+ {
+ 4,
+ { 0x74, 0x65, 0x73, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x65 }, // "test_teste"
+ },
+ {
+ 4,
+ { 0x74, 0x65, 0x73, 0x74, 0x20, 0x74, 0x65, 0x73, 0x74, 0 }, // "test test"
+ },
+ {
+ -1,
+ { 0x643, 0x402, 0x716, 0xB05, 0, 0, 0, 0, 0, 0 }, // "كЂܖଅ" (arabic letters),
+ },
+ {
+ -1,
+ { 0x643, 0x402, 0x716, 0xB05, 0x54, 0x65, 0x73, 0x74, 0x30AA, 0x4E21 }, // 0-3 arabic letters, 4-7 latin letters, 8-9 CJK letters
+ },
+ {
+ -1,
+ { 0x4D2, 0x4D6, 0x4DA, 0x4DC, 0, 0, 0, 0, 0, 0 }, // "ӒӖӚӜ" cyrillic letters
+ },
+ {
+ -1,
+ { 0xC2, 0xC3, 0xC4, 0xC5, 0x100, 0x102, 0x104, 0xC7, 0x106, 0x108 }, // "ÂÃÄÅĀĂĄÇĆĈ" rarer latin letters
+ },
+ };
+
+ for (int j = 0; j < 14; j++) {
+ ul_testcase test = cases[j];
+ int failed_on_index = -1;
+ for (int k = 0; k < 10; k++) {
+ char32_t character = test.text[k];
+ if (character == 0) {
+ break;
+ }
+ if (!ts->is_valid_letter(character)) {
+ failed_on_index = k;
+ break;
+ }
+ }
+
+ if (test.fail_index == -1) {
+ CHECK_MESSAGE(test.fail_index == failed_on_index, "In interface ", ts->get_name() + ": In test case ", j, ", the character at index ", failed_on_index, " should have been a letter.");
+ } else {
+ CHECK_MESSAGE(test.fail_index == failed_on_index, "In interface ", ts->get_name() + ": In test case ", j, ", expected first non-letter at index ", test.fail_index, ", but found at index ", failed_on_index);
+ }
+ }
+ }
+ }
+
SUBCASE("[TextServer] Strip Diacritics") {
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
diff --git a/tests/test_macros.h b/tests/test_macros.h
index a173b37a2d..10f4c59a90 100644
--- a/tests/test_macros.h
+++ b/tests/test_macros.h
@@ -119,6 +119,7 @@ DOCTEST_STRINGIFY_VARIANT(PackedStringArray);
DOCTEST_STRINGIFY_VARIANT(PackedVector2Array);
DOCTEST_STRINGIFY_VARIANT(PackedVector3Array);
DOCTEST_STRINGIFY_VARIANT(PackedColorArray);
+DOCTEST_STRINGIFY_VARIANT(PackedVector4Array);
// Register test commands to be launched from the command-line.
// For instance: REGISTER_TEST_COMMAND("gdscript-parser" &test_parser_func).
@@ -136,6 +137,7 @@ int register_test_command(String p_command, TestFunc p_function);
// Requires Message Queue and InputMap to be setup.
// SEND_GUI_ACTION - takes an input map key. e.g SEND_GUI_ACTION("ui_text_newline").
// SEND_GUI_KEY_EVENT - takes a keycode set. e.g SEND_GUI_KEY_EVENT(Key::A | KeyModifierMask::META).
+// SEND_GUI_KEY_UP_EVENT - takes a keycode set. e.g SEND_GUI_KEY_UP_EVENT(Key::A | KeyModifierMask::META).
// SEND_GUI_MOUSE_BUTTON_EVENT - takes a position, mouse button, mouse mask and modifiers e.g SEND_GUI_MOUSE_BUTTON_EVENT(Vector2(50, 50), MOUSE_BUTTON_NONE, MOUSE_BUTTON_NONE, Key::None);
// SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT - takes a position, mouse button, mouse mask and modifiers e.g SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(Vector2(50, 50), MOUSE_BUTTON_NONE, MOUSE_BUTTON_NONE, Key::None);
// SEND_GUI_MOUSE_MOTION_EVENT - takes a position, mouse mask and modifiers e.g SEND_GUI_MOUSE_MOTION_EVENT(Vector2(50, 50), MouseButtonMask::LEFT, KeyModifierMask::META);
@@ -161,6 +163,14 @@ int register_test_command(String p_command, TestFunc p_function);
MessageQueue::get_singleton()->flush(); \
}
+#define SEND_GUI_KEY_UP_EVENT(m_input) \
+ { \
+ Ref<InputEventKey> event = InputEventKey::create_reference(m_input); \
+ event->set_pressed(false); \
+ _SEND_DISPLAYSERVER_EVENT(event); \
+ MessageQueue::get_singleton()->flush(); \
+ }
+
#define _UPDATE_EVENT_MODIFERS(m_event, m_modifers) \
m_event->set_shift_pressed(((m_modifers) & KeyModifierMask::SHIFT) != Key::NONE); \
m_event->set_alt_pressed(((m_modifers) & KeyModifierMask::ALT) != Key::NONE); \
@@ -396,4 +406,71 @@ public:
#define SIGNAL_CHECK_FALSE(m_signal) CHECK(SignalWatcher::get_singleton()->check_false(m_signal));
#define SIGNAL_DISCARD(m_signal) SignalWatcher::get_singleton()->discard_signal(m_signal);
+#define MULTICHECK_STRING_EQ(m_obj, m_func, m_param1, m_eq) \
+ CHECK(m_obj.m_func(m_param1) == m_eq); \
+ CHECK(m_obj.m_func(U##m_param1) == m_eq); \
+ CHECK(m_obj.m_func(L##m_param1) == m_eq); \
+ CHECK(m_obj.m_func(String(m_param1)) == m_eq);
+
+#define MULTICHECK_STRING_INT_EQ(m_obj, m_func, m_param1, m_param2, m_eq) \
+ CHECK(m_obj.m_func(m_param1, m_param2) == m_eq); \
+ CHECK(m_obj.m_func(U##m_param1, m_param2) == m_eq); \
+ CHECK(m_obj.m_func(L##m_param1, m_param2) == m_eq); \
+ CHECK(m_obj.m_func(String(m_param1), m_param2) == m_eq);
+
+#define MULTICHECK_STRING_INT_INT_EQ(m_obj, m_func, m_param1, m_param2, m_param3, m_eq) \
+ CHECK(m_obj.m_func(m_param1, m_param2, m_param3) == m_eq); \
+ CHECK(m_obj.m_func(U##m_param1, m_param2, m_param3) == m_eq); \
+ CHECK(m_obj.m_func(L##m_param1, m_param2, m_param3) == m_eq); \
+ CHECK(m_obj.m_func(String(m_param1), m_param2, m_param3) == m_eq);
+
+#define MULTICHECK_STRING_STRING_EQ(m_obj, m_func, m_param1, m_param2, m_eq) \
+ CHECK(m_obj.m_func(m_param1, m_param2) == m_eq); \
+ CHECK(m_obj.m_func(U##m_param1, U##m_param2) == m_eq); \
+ CHECK(m_obj.m_func(L##m_param1, L##m_param2) == m_eq); \
+ CHECK(m_obj.m_func(String(m_param1), String(m_param2)) == m_eq);
+
+#define MULTICHECK_GET_SLICE(m_obj, m_param1, m_slices) \
+ for (int i = 0; i < m_obj.get_slice_count(m_param1); ++i) { \
+ CHECK(m_obj.get_slice(m_param1, i) == m_slices[i]); \
+ } \
+ for (int i = 0; i < m_obj.get_slice_count(U##m_param1); ++i) { \
+ CHECK(m_obj.get_slice(U##m_param1, i) == m_slices[i]); \
+ } \
+ for (int i = 0; i < m_obj.get_slice_count(L##m_param1); ++i) { \
+ CHECK(m_obj.get_slice(L##m_param1, i) == m_slices[i]); \
+ } \
+ for (int i = 0; i < m_obj.get_slice_count(String(m_param1)); ++i) { \
+ CHECK(m_obj.get_slice(String(m_param1), i) == m_slices[i]); \
+ }
+
+#define MULTICHECK_SPLIT(m_obj, m_func, m_param1, m_param2, m_param3, m_slices, m_expected_size) \
+ do { \
+ Vector<String> string_list; \
+ \
+ string_list = m_obj.m_func(m_param1, m_param2, m_param3); \
+ CHECK(m_expected_size == string_list.size()); \
+ for (int i = 0; i < string_list.size(); ++i) { \
+ CHECK(string_list[i] == m_slices[i]); \
+ } \
+ \
+ string_list = m_obj.m_func(U##m_param1, m_param2, m_param3); \
+ CHECK(m_expected_size == string_list.size()); \
+ for (int i = 0; i < string_list.size(); ++i) { \
+ CHECK(string_list[i] == m_slices[i]); \
+ } \
+ \
+ string_list = m_obj.m_func(L##m_param1, m_param2, m_param3); \
+ CHECK(m_expected_size == string_list.size()); \
+ for (int i = 0; i < string_list.size(); ++i) { \
+ CHECK(string_list[i] == m_slices[i]); \
+ } \
+ \
+ string_list = m_obj.m_func(String(m_param1), m_param2, m_param3); \
+ CHECK(m_expected_size == string_list.size()); \
+ for (int i = 0; i < string_list.size(); ++i) { \
+ CHECK(string_list[i] == m_slices[i]); \
+ } \
+ } while (0)
+
#endif // TEST_MACROS_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index bb6837c965..041231888b 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -100,7 +100,6 @@
#include "tests/core/variant/test_variant.h"
#include "tests/core/variant/test_variant_utility.h"
#include "tests/scene/test_animation.h"
-#include "tests/scene/test_arraymesh.h"
#include "tests/scene/test_audio_stream_wav.h"
#include "tests/scene/test_bit_map.h"
#include "tests/scene/test_camera_2d.h"
@@ -112,6 +111,7 @@
#include "tests/scene/test_curve_3d.h"
#include "tests/scene/test_gradient.h"
#include "tests/scene/test_image_texture.h"
+#include "tests/scene/test_instance_placeholder.h"
#include "tests/scene/test_node.h"
#include "tests/scene/test_node_2d.h"
#include "tests/scene/test_packed_scene.h"
@@ -119,6 +119,7 @@
#include "tests/scene/test_sprite_frames.h"
#include "tests/scene/test_text_edit.h"
#include "tests/scene/test_theme.h"
+#include "tests/scene/test_timer.h"
#include "tests/scene/test_viewport.h"
#include "tests/scene/test_visual_shader.h"
#include "tests/scene/test_window.h"
@@ -127,6 +128,7 @@
#include "tests/test_validate_testing.h"
#ifndef _3D_DISABLED
+#include "tests/scene/test_arraymesh.h"
#include "tests/scene/test_camera_3d.h"
#include "tests/scene/test_navigation_agent_2d.h"
#include "tests/scene/test_navigation_agent_3d.h"
@@ -187,7 +189,7 @@ int test_main(int argc, char *argv[]) {
}
// Doctest runner.
doctest::Context test_context;
- List<String> test_args;
+ LocalVector<String> test_args;
// Clean arguments of "--test" from the args.
for (int x = 0; x < argc; x++) {
@@ -200,7 +202,7 @@ int test_main(int argc, char *argv[]) {
if (test_args.size() > 0) {
// Convert Godot command line arguments back to standard arguments.
char **doctest_args = new char *[test_args.size()];
- for (int x = 0; x < test_args.size(); x++) {
+ for (uint32_t x = 0; x < test_args.size(); x++) {
// Operation to convert Godot string to non wchar string.
CharString cs = test_args[x].utf8();
const char *str = cs.get_data();
@@ -212,7 +214,7 @@ int test_main(int argc, char *argv[]) {
test_context.applyCommandLine(test_args.size(), doctest_args);
- for (int x = 0; x < test_args.size(); x++) {
+ for (uint32_t x = 0; x < test_args.size(); x++) {
delete[] doctest_args[x];
}
delete[] doctest_args;
@@ -241,7 +243,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
String name = String(p_in.m_name);
String suite_name = String(p_in.m_test_suite);
- if (name.find("[SceneTree]") != -1 || name.find("[Editor]") != -1) {
+ if (name.contains("[SceneTree]") || name.contains("[Editor]")) {
memnew(MessageQueue);
memnew(Input);
@@ -251,7 +253,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
OS::get_singleton()->set_has_server_feature_callback(nullptr);
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
if (String("mock") == DisplayServer::get_create_function_name(i)) {
- DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, err);
+ DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, DisplayServer::CONTEXT_EDITOR, err);
break;
}
}
@@ -290,7 +292,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
}
#ifdef TOOLS_ENABLED
- if (name.find("[Editor]") != -1) {
+ if (name.contains("[Editor]")) {
Engine::get_singleton()->set_editor_hint(true);
EditorPaths::create();
EditorSettings::create();
@@ -300,7 +302,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
return;
}
- if (name.find("Audio") != -1) {
+ if (name.contains("Audio")) {
// The last driver index should always be the dummy driver.
int dummy_idx = AudioDriverManager::get_driver_count() - 1;
AudioDriverManager::initialize(dummy_idx);
@@ -310,7 +312,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
}
#ifndef _3D_DISABLED
- if (suite_name.find("[Navigation]") != -1 && navigation_server_2d == nullptr && navigation_server_3d == nullptr) {
+ if (suite_name.contains("[Navigation]") && navigation_server_2d == nullptr && navigation_server_3d == nullptr) {
ERR_PRINT_OFF;
navigation_server_3d = NavigationServer3DManager::new_default_server();
navigation_server_2d = NavigationServer2DManager::new_default_server();
diff --git a/tests/test_validate_testing.h b/tests/test_validate_testing.h
index 4673d42b5a..f2e3bf3bb4 100644
--- a/tests/test_validate_testing.h
+++ b/tests/test_validate_testing.h
@@ -181,6 +181,13 @@ TEST_SUITE("Validate tests") {
color_arr.push_back(Color(2, 2, 2));
INFO(color_arr);
+ PackedVector4Array vec4_arr;
+ vec4_arr.push_back(Vector4(0, 0, 0, 0));
+ vec4_arr.push_back(Vector4(1, 1, 1, 1));
+ vec4_arr.push_back(Vector4(2, 2, 2, 2));
+ vec4_arr.push_back(Vector4(3, 3, 3, 3));
+ INFO(vec4_arr);
+
// doctest string concatenation.
CHECK_MESSAGE(true, var, " ", vec2, " ", rect2, " ", color);
}