|
@@ -48,6 +48,7 @@
|
|
#include <stdint.h>
|
|
#include <stdint.h>
|
|
|
|
|
|
VARIANT_ENUM_CAST(Node::ProcessMode);
|
|
VARIANT_ENUM_CAST(Node::ProcessMode);
|
|
|
|
+VARIANT_ENUM_CAST(Node::InternalMode);
|
|
|
|
|
|
int Node::orphan_node_count = 0;
|
|
int Node::orphan_node_count = 0;
|
|
|
|
|
|
@@ -291,14 +292,40 @@ void Node::_propagate_exit_tree() {
|
|
|
|
|
|
void Node::move_child(Node *p_child, int p_pos) {
|
|
void Node::move_child(Node *p_child, int p_pos) {
|
|
ERR_FAIL_NULL(p_child);
|
|
ERR_FAIL_NULL(p_child);
|
|
- ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1, vformat("Invalid new child position: %d.", p_pos));
|
|
|
|
ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node.");
|
|
ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node.");
|
|
|
|
+
|
|
|
|
+ // We need to check whether node is internal and move it only in the relevant node range.
|
|
|
|
+ if (p_child->_is_internal_front()) {
|
|
|
|
+ ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos));
|
|
|
|
+ _move_child(p_child, p_pos);
|
|
|
|
+ } else if (p_child->_is_internal_back()) {
|
|
|
|
+ ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos));
|
|
|
|
+ _move_child(p_child, data.children.size() - data.internal_children_back + p_pos);
|
|
|
|
+ } else {
|
|
|
|
+ ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos));
|
|
|
|
+ _move_child(p_child, p_pos + data.internal_children_front);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) {
|
|
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup).");
|
|
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup).");
|
|
|
|
|
|
// Specifying one place beyond the end
|
|
// Specifying one place beyond the end
|
|
// means the same as moving to the last position
|
|
// means the same as moving to the last position
|
|
- if (p_pos == data.children.size()) {
|
|
|
|
- p_pos--;
|
|
|
|
|
|
+ if (!p_ignore_end) { // p_ignore_end is a little hack to make back internal children work properly.
|
|
|
|
+ if (p_child->_is_internal_front()) {
|
|
|
|
+ if (p_pos == data.internal_children_front) {
|
|
|
|
+ p_pos--;
|
|
|
|
+ }
|
|
|
|
+ } else if (p_child->_is_internal_back()) {
|
|
|
|
+ if (p_pos == data.children.size()) {
|
|
|
|
+ p_pos--;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if (p_pos == data.children.size() - data.internal_children_back) {
|
|
|
|
+ p_pos--;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
if (p_child->data.pos == p_pos) {
|
|
if (p_child->data.pos == p_pos) {
|
|
@@ -339,7 +366,14 @@ void Node::raise() {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- data.parent->move_child(this, data.parent->data.children.size() - 1);
|
|
|
|
|
|
+ // Internal children move within a different index range.
|
|
|
|
+ if (_is_internal_front()) {
|
|
|
|
+ data.parent->move_child(this, data.parent->data.internal_children_front - 1);
|
|
|
|
+ } else if (_is_internal_back()) {
|
|
|
|
+ data.parent->move_child(this, data.parent->data.internal_children_back - 1);
|
|
|
|
+ } else {
|
|
|
|
+ data.parent->move_child(this, data.parent->get_child_count(false) - 1);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
void Node::add_child_notify(Node *p_child) {
|
|
void Node::add_child_notify(Node *p_child) {
|
|
@@ -1058,6 +1092,10 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
|
|
p_child->data.pos = data.children.size();
|
|
p_child->data.pos = data.children.size();
|
|
data.children.push_back(p_child);
|
|
data.children.push_back(p_child);
|
|
p_child->data.parent = this;
|
|
p_child->data.parent = this;
|
|
|
|
+
|
|
|
|
+ if (data.internal_children_back > 0) {
|
|
|
|
+ _move_child(p_child, data.children.size() - data.internal_children_back - 1);
|
|
|
|
+ }
|
|
p_child->notification(NOTIFICATION_PARENTED);
|
|
p_child->notification(NOTIFICATION_PARENTED);
|
|
|
|
|
|
if (data.tree) {
|
|
if (data.tree) {
|
|
@@ -1070,7 +1108,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
|
|
add_child_notify(p_child);
|
|
add_child_notify(p_child);
|
|
}
|
|
}
|
|
|
|
|
|
-void Node::add_child(Node *p_child, bool p_legible_unique_name) {
|
|
|
|
|
|
+void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_internal) {
|
|
ERR_FAIL_NULL(p_child);
|
|
ERR_FAIL_NULL(p_child);
|
|
ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself!
|
|
ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself!
|
|
ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent
|
|
ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent
|
|
@@ -1079,19 +1117,35 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) {
|
|
#endif
|
|
#endif
|
|
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead.");
|
|
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead.");
|
|
|
|
|
|
- /* Validate name */
|
|
|
|
_validate_child_name(p_child, p_legible_unique_name);
|
|
_validate_child_name(p_child, p_legible_unique_name);
|
|
-
|
|
|
|
_add_child_nocheck(p_child, p_child->data.name);
|
|
_add_child_nocheck(p_child, p_child->data.name);
|
|
|
|
+
|
|
|
|
+ if (p_internal == INTERNAL_MODE_FRONT) {
|
|
|
|
+ _move_child(p_child, data.internal_children_front);
|
|
|
|
+ data.internal_children_front++;
|
|
|
|
+ } else if (p_internal == INTERNAL_MODE_BACK) {
|
|
|
|
+ if (data.internal_children_back > 0) {
|
|
|
|
+ _move_child(p_child, data.children.size() - 1, true);
|
|
|
|
+ }
|
|
|
|
+ data.internal_children_back++;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) {
|
|
void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) {
|
|
ERR_FAIL_NULL(p_sibling);
|
|
ERR_FAIL_NULL(p_sibling);
|
|
|
|
+ ERR_FAIL_NULL(data.parent);
|
|
ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself!
|
|
ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself!
|
|
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_sibling() failed. Consider using call_deferred(\"add_sibling\", sibling) instead.");
|
|
ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_sibling() failed. Consider using call_deferred(\"add_sibling\", sibling) instead.");
|
|
|
|
|
|
- get_parent()->add_child(p_sibling, p_legible_unique_name);
|
|
|
|
- get_parent()->move_child(p_sibling, this->get_index() + 1);
|
|
|
|
|
|
+ InternalMode internal = INTERNAL_MODE_DISABLED;
|
|
|
|
+ if (_is_internal_front()) { // The sibling will have the same internal status.
|
|
|
|
+ internal = INTERNAL_MODE_FRONT;
|
|
|
|
+ } else if (_is_internal_back()) {
|
|
|
|
+ internal = INTERNAL_MODE_BACK;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ data.parent->add_child(p_sibling, p_legible_unique_name, internal);
|
|
|
|
+ data.parent->_move_child(p_sibling, get_index() + 1);
|
|
}
|
|
}
|
|
|
|
|
|
void Node::_propagate_validate_owner() {
|
|
void Node::_propagate_validate_owner() {
|
|
@@ -1145,7 +1199,12 @@ void Node::remove_child(Node *p_child) {
|
|
ERR_FAIL_COND_MSG(idx == -1, vformat("Cannot remove child node '%s' as it is not a child of this node.", p_child->get_name()));
|
|
ERR_FAIL_COND_MSG(idx == -1, vformat("Cannot remove child node '%s' as it is not a child of this node.", p_child->get_name()));
|
|
//ERR_FAIL_COND( p_child->data.blocked > 0 );
|
|
//ERR_FAIL_COND( p_child->data.blocked > 0 );
|
|
|
|
|
|
- //if (data.scene) { does not matter
|
|
|
|
|
|
+ // If internal child, update the counter.
|
|
|
|
+ if (p_child->_is_internal_front()) {
|
|
|
|
+ data.internal_children_front--;
|
|
|
|
+ } else if (p_child->_is_internal_back()) {
|
|
|
|
+ data.internal_children_back--;
|
|
|
|
+ }
|
|
|
|
|
|
p_child->_set_tree(nullptr);
|
|
p_child->_set_tree(nullptr);
|
|
//}
|
|
//}
|
|
@@ -1175,17 +1234,29 @@ void Node::remove_child(Node *p_child) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-int Node::get_child_count() const {
|
|
|
|
- return data.children.size();
|
|
|
|
|
|
+int Node::get_child_count(bool p_include_internal) const {
|
|
|
|
+ if (p_include_internal) {
|
|
|
|
+ return data.children.size();
|
|
|
|
+ } else {
|
|
|
|
+ return data.children.size() - data.internal_children_front - data.internal_children_back;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
-Node *Node::get_child(int p_index) const {
|
|
|
|
- if (p_index < 0) {
|
|
|
|
- p_index += data.children.size();
|
|
|
|
|
|
+Node *Node::get_child(int p_index, bool p_include_internal) const {
|
|
|
|
+ if (p_include_internal) {
|
|
|
|
+ if (p_index < 0) {
|
|
|
|
+ p_index += data.children.size();
|
|
|
|
+ }
|
|
|
|
+ ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr);
|
|
|
|
+ return data.children[p_index];
|
|
|
|
+ } else {
|
|
|
|
+ if (p_index < 0) {
|
|
|
|
+ p_index += data.children.size() - data.internal_children_front - data.internal_children_back;
|
|
|
|
+ }
|
|
|
|
+ ERR_FAIL_INDEX_V(p_index, data.children.size() - data.internal_children_front - data.internal_children_back, nullptr);
|
|
|
|
+ p_index += data.internal_children_front;
|
|
|
|
+ return data.children[p_index];
|
|
}
|
|
}
|
|
- ERR_FAIL_INDEX_V(p_index, data.children.size(), nullptr);
|
|
|
|
-
|
|
|
|
- return data.children[p_index];
|
|
|
|
}
|
|
}
|
|
|
|
|
|
Node *Node::_get_child_by_name(const StringName &p_name) const {
|
|
Node *Node::_get_child_by_name(const StringName &p_name) const {
|
|
@@ -1717,7 +1788,13 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) {
|
|
data.blocked--;
|
|
data.blocked--;
|
|
}
|
|
}
|
|
|
|
|
|
-int Node::get_index() const {
|
|
|
|
|
|
+int Node::get_index(bool p_include_internal) const {
|
|
|
|
+ // p_include_internal = false doesn't make sense if the node is internal.
|
|
|
|
+ ERR_FAIL_COND_V_MSG(!p_include_internal && (_is_internal_front() || _is_internal_back()), -1, "Node is internal. Can't get index with 'include_internal' being false.");
|
|
|
|
+
|
|
|
|
+ if (data.parent && !p_include_internal) {
|
|
|
|
+ return data.pos - data.parent->data.internal_children_front;
|
|
|
|
+ }
|
|
return data.pos;
|
|
return data.pos;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2429,12 +2506,12 @@ void Node::queue_delete() {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-TypedArray<Node> Node::_get_children() const {
|
|
|
|
|
|
+TypedArray<Node> Node::_get_children(bool p_include_internal) const {
|
|
TypedArray<Node> arr;
|
|
TypedArray<Node> arr;
|
|
- int cc = get_child_count();
|
|
|
|
|
|
+ int cc = get_child_count(p_include_internal);
|
|
arr.resize(cc);
|
|
arr.resize(cc);
|
|
for (int i = 0; i < cc; i++) {
|
|
for (int i = 0; i < cc; i++) {
|
|
- arr[i] = get_child(i);
|
|
|
|
|
|
+ arr[i] = get_child(i, p_include_internal);
|
|
}
|
|
}
|
|
|
|
|
|
return arr;
|
|
return arr;
|
|
@@ -2581,11 +2658,11 @@ void Node::_bind_methods() {
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name);
|
|
ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name);
|
|
ClassDB::bind_method(D_METHOD("get_name"), &Node::get_name);
|
|
ClassDB::bind_method(D_METHOD("get_name"), &Node::get_name);
|
|
- ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name"), &Node::add_child, DEFVAL(false));
|
|
|
|
|
|
+ ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0));
|
|
ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child);
|
|
ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child);
|
|
- ClassDB::bind_method(D_METHOD("get_child_count"), &Node::get_child_count);
|
|
|
|
- ClassDB::bind_method(D_METHOD("get_children"), &Node::_get_children);
|
|
|
|
- ClassDB::bind_method(D_METHOD("get_child", "idx"), &Node::get_child);
|
|
|
|
|
|
+ ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript.
|
|
|
|
+ ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::_get_children, DEFVAL(false));
|
|
|
|
+ ClassDB::bind_method(D_METHOD("get_child", "idx", "include_internal"), &Node::get_child, DEFVAL(false));
|
|
ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node);
|
|
ClassDB::bind_method(D_METHOD("has_node", "path"), &Node::has_node);
|
|
ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node);
|
|
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_node_or_null", "path"), &Node::get_node_or_null);
|
|
@@ -2609,7 +2686,7 @@ void Node::_bind_methods() {
|
|
ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner);
|
|
ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner);
|
|
ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner);
|
|
ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner);
|
|
ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip);
|
|
ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip);
|
|
- ClassDB::bind_method(D_METHOD("get_index"), &Node::get_index);
|
|
|
|
|
|
+ ClassDB::bind_method(D_METHOD("get_index", "include_internal"), &Node::get_index, DEFVAL(false));
|
|
ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree);
|
|
ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree);
|
|
ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
|
|
ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
|
|
ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename);
|
|
ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename);
|
|
@@ -2747,6 +2824,10 @@ void Node::_bind_methods() {
|
|
BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS);
|
|
BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS);
|
|
BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING);
|
|
BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING);
|
|
|
|
|
|
|
|
+ BIND_ENUM_CONSTANT(INTERNAL_MODE_DISABLED);
|
|
|
|
+ BIND_ENUM_CONSTANT(INTERNAL_MODE_FRONT);
|
|
|
|
+ BIND_ENUM_CONSTANT(INTERNAL_MODE_BACK);
|
|
|
|
+
|
|
ADD_SIGNAL(MethodInfo("ready"));
|
|
ADD_SIGNAL(MethodInfo("ready"));
|
|
ADD_SIGNAL(MethodInfo("renamed"));
|
|
ADD_SIGNAL(MethodInfo("renamed"));
|
|
ADD_SIGNAL(MethodInfo("tree_entered"));
|
|
ADD_SIGNAL(MethodInfo("tree_entered"));
|