Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node: Re-add find_node as find_child, improve docs #60511

Merged
merged 1 commit into from
Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 33 additions & 25 deletions doc/classes/Node.xml
Original file line number Diff line number Diff line change
Expand Up @@ -193,28 +193,45 @@
[b]Note:[/b] It will not work properly if the node contains a script with constructor arguments (i.e. needs to supply arguments to [method Object._init] method). In that case, the node will be duplicated without a script.
</description>
</method>
<method name="find_nodes" qualifiers="const">
<method name="find_child" qualifiers="const">
<return type="Node" />
<argument index="0" name="pattern" type="String" />
<argument index="1" name="recursive" type="bool" default="true" />
<argument index="2" name="owned" type="bool" default="true" />
<description>
Finds the first descendant of this node whose name matches [code]pattern[/code] as in [method String.match].
[code]pattern[/code] does not match against the full path, just against individual node names. It is case-sensitive, with [code]"*"[/code] matching zero or more characters and [code]"?"[/code] matching any single character except [code]"."[/code]).
If [code]recursive[/code] is [code]true[/code], all child nodes are included, even if deeply nested. Nodes are checked in tree order, so this node's first direct child is checked first, then its own direct children, etc., before moving to the second direct child, and so on. If [code]recursive[/code] is [code]false[/code], only this node's direct children are matched.
If [code]owned[/code] is [code]true[/code], this method only finds nodes who have an assigned [member Node.owner]. This is especially important for scenes instantiated through a script, because those scenes don't have an owner.
Returns [code]null[/code] if no matching [Node] is found.
[b]Note:[/b] As this method walks through all the descendants of the node, it is the slowest way to get a reference to another node. Whenever possible, consider using [method get_node] with unique names instead (see [member unique_name_in_owner]), or caching the node references into variable.
[b]Note:[/b] To find all descendant nodes matching a pattern or a class type, see [method find_children].
</description>
</method>
<method name="find_children" qualifiers="const">
<return type="Node[]" />
<argument index="0" name="mask" type="String" />
<argument index="0" name="pattern" type="String" />
<argument index="1" name="type" type="String" default="&quot;&quot;" />
<argument index="2" name="recursive" type="bool" default="true" />
<argument index="3" name="owned" type="bool" default="true" />
<description>
Finds descendants of this node whose, name matches [code]mask[/code] as in [method String.match], and/or type matches [code]type[/code] as in [method Object.is_class].
[code]mask[/code] does not match against the full path, just against individual node names. It is case-sensitive, with [code]"*"[/code] matching zero or more characters and [code]"?"[/code] matching any single character except [code]"."[/code]).
[code]type[/code] will check equality or inheritance. It is case-sensitive, [code]"Object"[/code] will match a node whose type is [code]"Node"[/code] but not the other way around.
If [code]owned[/code] is [code]true[/code], this method only finds nodes whose owner is this node. This is especially important for scenes instantiated through a script, because those scenes don't have an owner.
Returns an empty array, if no matching nodes are found.
[b]Note:[/b] As this method walks through all the descendants of the node, it is the slowest way to get references to other nodes. To avoid using [method find_nodes] too often, consider caching the node references into variables.
Finds descendants of this node whose name matches [code]pattern[/code] as in [method String.match], and/or type matches [code]type[/code] as in [method Object.is_class].
[code]pattern[/code] does not match against the full path, just against individual node names. It is case-sensitive, with [code]"*"[/code] matching zero or more characters and [code]"?"[/code] matching any single character except [code]"."[/code]).
[code]type[/code] will check equality or inheritance, and is case-sensitive. [code]"Object"[/code] will match a node whose type is [code]"Node"[/code] but not the other way around.
If [code]recursive[/code] is [code]true[/code], all child nodes are included, even if deeply nested. Nodes are checked in tree order, so this node's first direct child is checked first, then its own direct children, etc., before moving to the second direct child, and so on. If [code]recursive[/code] is [code]false[/code], only this node's direct children are matched.
If [code]owned[/code] is [code]true[/code], this method only finds nodes who have an assigned [member Node.owner]. This is especially important for scenes instantiated through a script, because those scenes don't have an owner.
Returns an empty array if no matching nodes are found.
[b]Note:[/b] As this method walks through all the descendants of the node, it is the slowest way to get references to other nodes. Whenever possible, consider caching the node references into variables.
[b]Note:[/b] If you only want to find the first descendant node that matches a pattern, see [method find_child].
</description>
</method>
<method name="find_parent" qualifiers="const">
<return type="Node" />
<argument index="0" name="mask" type="String" />
<argument index="0" name="pattern" type="String" />
<description>
Finds the first parent of the current node whose name matches [code]mask[/code] as in [method String.match] (i.e. case-sensitive, but [code]"*"[/code] matches zero or more characters and [code]"?"[/code] matches any single character except [code]"."[/code]).
[b]Note:[/b] It does not match against the full path, just against individual node names.
[b]Note:[/b] As this method walks upwards in the scene tree, it can be slow in large, deeply nested scene trees. Whenever possible, consider using [method get_node] instead. To avoid using [method find_parent] too often, consider caching the node reference into a variable.
Finds the first parent of the current node whose name matches [code]pattern[/code] as in [method String.match].
[code]pattern[/code] does not match against the full path, just against individual node names. It is case-sensitive, with [code]"*"[/code] matching zero or more characters and [code]"?"[/code] matching any single character except [code]"."[/code]).
[b]Note:[/b] As this method walks upwards in the scene tree, it can be slow in large, deeply nested scene trees. Whenever possible, consider using [method get_node] with unique names instead (see [member unique_name_in_owner]), or caching the node references into variable.
</description>
</method>
<method name="get_child" qualifiers="const">
Expand Down Expand Up @@ -492,12 +509,6 @@
Returns [code]true[/code] if the node is processing unhandled key input (see [method set_process_unhandled_key_input]).
</description>
</method>
<method name="is_unique_name_in_owner" qualifiers="const">
<return type="bool" />
<description>
Returns whether the node is an unique name for all the other nodes owned by its [member owner].
</description>
</method>
<method name="move_child">
<return type="void" />
<argument index="0" name="child_node" type="Node" />
Expand Down Expand Up @@ -725,13 +736,6 @@
Sets whether this is an instance load placeholder. See [InstancePlaceholder].
</description>
</method>
<method name="set_unique_name_in_owner">
<return type="void" />
<argument index="0" name="enable" type="bool" />
<description>
Sets this node's name as the unique name in its [member owner]. This allows the node to be accessed as [code]%Name[/code] instead of the full path, from any node within that scene.
</description>
</method>
<method name="update_configuration_warnings">
<return type="void" />
<description>
Expand Down Expand Up @@ -767,6 +771,10 @@
<member name="scene_file_path" type="String" setter="set_scene_file_path" getter="get_scene_file_path">
If a scene is instantiated from a file, its topmost node contains the absolute file path from which it was loaded in [member scene_file_path] (e.g. [code]res://levels/1.tscn[/code]). Otherwise, [member scene_file_path] is set to an empty string.
</member>
<member name="unique_name_in_owner" type="bool" setter="set_unique_name_in_owner" getter="is_unique_name_in_owner" default="false">
Sets this node's name as a unique name in its [member owner]. This allows the node to be accessed as [code]%Name[/code] instead of the full path, from any node within that scene.
If another node with the same owner already had that name declared as unique, that other node's name will no longer be set as having a unique name.
</member>
</members>
<signals>
<signal name="child_entered_tree">
Expand Down
2 changes: 1 addition & 1 deletion editor/import/scene_import_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ void SceneImportSettings::_update_view_gizmos() {
continue;
}

TypedArray<Node> descendants = mesh_node->find_nodes("collider_view", "MeshInstance3D");
TypedArray<Node> descendants = mesh_node->find_children("collider_view", "MeshInstance3D");

CRASH_COND_MSG(descendants.is_empty(), "This is unreachable, since the collider view is always created even when the collision is not used! If this is triggered there is a bug on the function `_fill_scene`.");

Expand Down
56 changes: 44 additions & 12 deletions scene/main/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1373,9 +1373,39 @@ bool Node::has_node(const NodePath &p_path) const {
return get_node_or_null(p_path) != nullptr;
}

TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bool p_recursive, bool p_owned) const {
// Finds the first child node (in tree order) whose name matches the given pattern.
// Can be recursive or not, and limited to owned nodes.
Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned) const {
ERR_FAIL_COND_V(p_pattern.is_empty(), nullptr);

Node *const *cptr = data.children.ptr();
int ccount = data.children.size();
for (int i = 0; i < ccount; i++) {
if (p_owned && !cptr[i]->data.owner) {
continue;
}
if (cptr[i]->data.name.operator String().match(p_pattern)) {
return cptr[i];
}

if (!p_recursive) {
continue;
}

Node *ret = cptr[i]->find_child(p_pattern, true, p_owned);
if (ret) {
return ret;
}
}
return nullptr;
}

// Finds child nodes based on their name using pattern matching, or class name,
// or both (either pattern or type can be left empty).
// Can be recursive or not, and limited to owned nodes.
TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_type, bool p_recursive, bool p_owned) const {
TypedArray<Node> ret;
ERR_FAIL_COND_V(p_mask.is_empty() && p_type.is_empty(), ret);
ERR_FAIL_COND_V(p_pattern.is_empty() && p_type.is_empty(), ret);

Node *const *cptr = data.children.ptr();
int ccount = data.children.size();
Expand All @@ -1384,8 +1414,8 @@ TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bo
continue;
}

if (!p_mask.is_empty()) {
if (!cptr[i]->data.name.operator String().match(p_mask)) {
if (!p_pattern.is_empty()) {
if (!cptr[i]->data.name.operator String().match(p_pattern)) {
continue;
} else if (p_type.is_empty()) {
ret.append(cptr[i]);
Expand All @@ -1407,7 +1437,7 @@ TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bo
}

if (p_recursive) {
ret.append_array(cptr[i]->find_nodes(p_mask, p_type, true, p_owned));
ret.append_array(cptr[i]->find_children(p_pattern, p_type, true, p_owned));
}
}

Expand All @@ -1418,10 +1448,10 @@ Node *Node::get_parent() const {
return data.parent;
}

Node *Node::find_parent(const String &p_mask) const {
Node *Node::find_parent(const String &p_pattern) const {
Node *p = data.parent;
while (p) {
if (p->data.name.operator String().match(p_mask)) {
if (p->data.name.operator String().match(p_pattern)) {
return p;
}
p = p->data.parent;
Expand Down Expand Up @@ -1542,7 +1572,9 @@ void Node::_acquire_unique_name_in_owner() {
StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.operator String());
Node **which = data.owner->data.owned_unique_nodes.getptr(key);
if (which != nullptr && *which != this) {
WARN_PRINT(vformat(RTR("Setting node name '%s' to be unique within scene for '%s', but it's already claimed by '%s'. This node is no longer set unique."), get_name(), is_inside_tree() ? get_path() : data.owner->get_path_to(this), is_inside_tree() ? (*which)->get_path() : data.owner->get_path_to(*which)));
String which_path = is_inside_tree() ? (*which)->get_path() : data.owner->get_path_to(*which);
WARN_PRINT(vformat(RTR("Setting node name '%s' to be unique within scene for '%s', but it's already claimed by '%s'.\n'%s' is no longer set as having a unique name."),
get_name(), is_inside_tree() ? get_path() : data.owner->get_path_to(this), which_path, which_path));
data.unique_name_in_owner = false;
return;
}
Expand Down Expand Up @@ -2780,8 +2812,9 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node);
ClassDB::bind_method(D_METHOD("get_node_or_null", "path"), &Node::get_node_or_null);
ClassDB::bind_method(D_METHOD("get_parent"), &Node::get_parent);
ClassDB::bind_method(D_METHOD("find_nodes", "mask", "type", "recursive", "owned"), &Node::find_nodes, DEFVAL(""), DEFVAL(true), DEFVAL(true));
ClassDB::bind_method(D_METHOD("find_parent", "mask"), &Node::find_parent);
ClassDB::bind_method(D_METHOD("find_child", "pattern", "recursive", "owned"), &Node::find_child, DEFVAL(true), DEFVAL(true));
ClassDB::bind_method(D_METHOD("find_children", "pattern", "type", "recursive", "owned"), &Node::find_children, DEFVAL(""), DEFVAL(true), DEFVAL(true));
ClassDB::bind_method(D_METHOD("find_parent", "pattern"), &Node::find_parent);
ClassDB::bind_method(D_METHOD("has_node_and_resource", "path"), &Node::has_node_and_resource);
ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource);

Expand Down Expand Up @@ -2872,8 +2905,6 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_unique_name_in_owner", "enable"), &Node::set_unique_name_in_owner);
ClassDB::bind_method(D_METHOD("is_unique_name_in_owner"), &Node::is_unique_name_in_owner);

ADD_PROPERTY(PropertyInfo(Variant::BOOL, "unique_name_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_unique_name_in_owner", "is_unique_name_in_owner");

#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("_set_property_pinned", "property", "pinned"), &Node::set_property_pinned);
#endif
Expand Down Expand Up @@ -2964,6 +2995,7 @@ void Node::_bind_methods() {
ADD_SIGNAL(MethodInfo("child_exited_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));

ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "unique_name_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_unique_name_in_owner", "is_unique_name_in_owner");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_file_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_file_path", "get_scene_file_path");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_owner", "get_owner");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "", "get_multiplayer");
Expand Down
5 changes: 3 additions & 2 deletions scene/main/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,13 @@ class Node : public Object {
bool has_node(const NodePath &p_path) const;
Node *get_node(const NodePath &p_path) const;
Node *get_node_or_null(const NodePath &p_path) const;
TypedArray<Node> find_nodes(const String &p_mask, const String &p_type = "", bool p_recursive = true, bool p_owned = true) const;
Node *find_child(const String &p_pattern, bool p_recursive = true, bool p_owned = true) const;
TypedArray<Node> find_children(const String &p_pattern, const String &p_type = "", bool p_recursive = true, bool p_owned = true) const;
bool has_node_and_resource(const NodePath &p_path) const;
Node *get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const;

Node *get_parent() const;
Node *find_parent(const String &p_mask) const;
Node *find_parent(const String &p_pattern) const;

_FORCE_INLINE_ SceneTree *get_tree() const {
ERR_FAIL_COND_V(!data.tree, nullptr);
Expand Down