Skip to content

Commit

Permalink
Add a method to construct a NodePath from a StringName
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronfranke committed Aug 30, 2024
1 parent a5830f6 commit ab1b855
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 0 deletions.
22 changes: 22 additions & 0 deletions core/string/node_path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<StringName> 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<StringName> &p_path, bool p_absolute) {
if (p_path.size() == 0 && !p_absolute) {
return;
Expand Down
2 changes: 2 additions & 0 deletions core/string/node_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class NodePath {
void simplify();
NodePath simplified() const;

static NodePath from_string_name(const StringName &p_string_name);

NodePath(const Vector<StringName> &p_path, bool p_absolute);
NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute);
NodePath(const NodePath &p_path);
Expand Down
1 change: 1 addition & 0 deletions core/variant/variant_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
7 changes: 7 additions & 0 deletions doc/classes/NodePath.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@
</constructor>
</constructors>
<methods>
<method name="from_string_name" qualifiers="static">
<return type="NodePath" />
<param index="0" name="string_name" type="StringName" />
<description>
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.
</description>
</method>
<method name="get_as_property_path" qualifiers="const">
<return type="NodePath" />
<description>
Expand Down
69 changes: 69 additions & 0 deletions tests/core/string/test_node_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down

0 comments on commit ab1b855

Please sign in to comment.