Add support for internal nodes

This commit is contained in:
kobewi 2021-08-25 15:49:30 +02:00
parent d04aa9a114
commit a913ae8d56
21 changed files with 196 additions and 88 deletions

View file

@ -48,6 +48,7 @@
#include <stdint.h>
VARIANT_ENUM_CAST(Node::ProcessMode);
VARIANT_ENUM_CAST(Node::InternalMode);
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) {
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.");
// 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).");
// Specifying one place beyond the end
// 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) {
@ -339,7 +366,14 @@ void Node::raise() {
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) {
@ -1058,6 +1092,10 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
p_child->data.pos = data.children.size();
data.children.push_back(p_child);
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);
if (data.tree) {
@ -1070,7 +1108,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) {
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_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
@ -1079,19 +1117,35 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) {
#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.");
/* Validate name */
_validate_child_name(p_child, p_legible_unique_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) {
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(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() {
@ -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( 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);
//}
@ -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 {
@ -1717,7 +1788,13 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) {
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;
}
@ -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;
int cc = get_child_count();
int cc = get_child_count(p_include_internal);
arr.resize(cc);
for (int i = 0; i < cc; i++) {
arr[i] = get_child(i);
arr[i] = get_child(i, p_include_internal);
}
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("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("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("get_node", "path"), &Node::get_node);
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("get_owner"), &Node::get_owner);
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_pretty"), &Node::print_tree_pretty);
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_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("renamed"));
ADD_SIGNAL(MethodInfo("tree_entered"));