From ab1b85559f1f798c5127f93333201e8936924ff5 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Wed, 1 Feb 2023 03:12:39 -0600 Subject: [PATCH] Add a method to construct a NodePath from a StringName --- core/string/node_path.cpp | 22 ++++++++++ core/string/node_path.h | 2 + core/variant/variant_call.cpp | 1 + doc/classes/NodePath.xml | 7 +++ tests/core/string/test_node_path.h | 69 ++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+) diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp index fdc72bc8dcd8..e718dd3d3acc 100644 --- a/core/string/node_path.cpp +++ b/core/string/node_path.cpp @@ -365,6 +365,28 @@ NodePath NodePath::simplified() const { return np; } +NodePath NodePath::from_string_name(const StringName &p_string_name) { + String string = String(p_string_name); + const int size = string.size(); + if (size == 0) { + return NodePath(); + } + // Check if p_string_name contains a slash or colon. If so, we need to parse it. + const char32_t *chars = string.ptr(); + for (int i = 0; i < size; i++) { + if (unlikely(chars[i] == '/' || chars[i] == ':')) { + return NodePath(string); + } + } + // If there is no colon or slash, the desired NodePath has one StringName. + // Therefore we can avoid all the String parsing and StringName re-creation. + Vector path; + path.push_back(p_string_name); + NodePath ret = NodePath(path, false); + ret.data->concatenated_path = p_string_name; + return ret; +} + NodePath::NodePath(const Vector &p_path, bool p_absolute) { if (p_path.size() == 0 && !p_absolute) { return; diff --git a/core/string/node_path.h b/core/string/node_path.h index 56799839d765..af60738644b7 100644 --- a/core/string/node_path.h +++ b/core/string/node_path.h @@ -89,6 +89,8 @@ class NodePath { void simplify(); NodePath simplified() const; + static NodePath from_string_name(const StringName &p_string_name); + NodePath(const Vector &p_path, bool p_absolute); NodePath(const Vector &p_path, const Vector &p_subpath, bool p_absolute); NodePath(const NodePath &p_path); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 83f1f981b3e0..5fc1cdd2a024 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -2085,6 +2085,7 @@ static void _register_variant_builtin_methods_misc() { bind_method(NodePath, slice, sarray("begin", "end"), varray(INT_MAX)); bind_method(NodePath, get_as_property_path, sarray(), varray()); bind_method(NodePath, is_empty, sarray(), varray()); + bind_static_method(NodePath, from_string_name, sarray("string_name"), varray()); /* Callable */ diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml index d0ec81ab4549..952b89e30d14 100644 --- a/doc/classes/NodePath.xml +++ b/doc/classes/NodePath.xml @@ -81,6 +81,13 @@ + + + + + Constructs a NodePath from a [StringName]. This method will be faster than constructing from a String if the StringName does not contain any slashes or colons. + + diff --git a/tests/core/string/test_node_path.h b/tests/core/string/test_node_path.h index bdbc578e8507..124e966c2e7a 100644 --- a/tests/core/string/test_node_path.h +++ b/tests/core/string/test_node_path.h @@ -168,6 +168,75 @@ TEST_CASE("[NodePath] Empty path") { "The node path should be considered empty."); } +TEST_CASE("[NodePath] From StringName") { + const StringName sname = StringName("Path2D"); + + // Test the case where the StringName has no slash or colon and uses the StringName directly. + const NodePath node_path_sname = NodePath::from_string_name(sname); + const NodePath node_path_string = NodePath("Path2D"); + + CHECK_MESSAGE( + !node_path_sname.is_absolute(), + "The StringName version should not be absolute."); + + CHECK_MESSAGE( + !node_path_string.is_absolute(), + "The String version should not be absolute."); + + CHECK_MESSAGE( + node_path_sname.get_name(0) == sname, + "The StringName version should have the expected StringName as the first name."); + + CHECK_MESSAGE( + node_path_string.get_name(0) == sname, + "The String version should have the expected StringName as the first name."); + + CHECK_MESSAGE( + node_path_sname.get_concatenated_names() == sname, + "The StringName version should give the expected StringName."); + + CHECK_MESSAGE( + node_path_string.get_concatenated_names() == sname, + "The String version should give the expected StringName."); + + CHECK_MESSAGE( + node_path_string.get_concatenated_names() == node_path_sname.get_concatenated_names(), + "The String and StringName versions should give the same result."); + + // Test the case where the StringName has a slash or colon and uses String parsing. + const NodePath node_path_abs_sname = NodePath::from_string_name("/Path2D"); + const NodePath node_path_abs_string = NodePath("/Path2D"); + + CHECK_MESSAGE( + node_path_abs_sname.is_absolute(), + "The StringName version should be absolute."); + + CHECK_MESSAGE( + node_path_abs_string.is_absolute(), + "The String version should be absolute."); + + CHECK_MESSAGE( + node_path_abs_sname.get_name(0) == sname, + "The StringName version should have the expected StringName as the first name."); + + CHECK_MESSAGE( + node_path_abs_string.get_name(0) == sname, + "The String version should have the expected StringName as the first name."); + + // Absolute paths do not include a leading slash in the concatenated names. + CHECK_MESSAGE( + node_path_abs_sname.get_concatenated_names() == sname, + "The StringName version should give the expected StringName."); + + CHECK_MESSAGE( + node_path_abs_string.get_concatenated_names() == sname, + "The String version should give the expected StringName."); + + CHECK_MESSAGE( + node_path_abs_string.get_concatenated_names() == node_path_abs_sname.get_concatenated_names(), + "The String and StringName versions should give the same result."); +} + TEST_CASE("[NodePath] Slice") { const NodePath node_path_relative = NodePath("Parent/Child:prop"); const NodePath node_path_absolute = NodePath("/root/Parent/Child:prop");