From 3ed7bdc26f30747961a0b189c20747d117f33f63 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Wed, 31 May 2023 11:31:43 +0200 Subject: [PATCH] Implement `TreeItem.add_child` --- doc/classes/TreeItem.xml | 10 +++- scene/gui/tree.cpp | 117 +++++++++++++++++++++++++-------------- scene/gui/tree.h | 7 +-- 3 files changed, 86 insertions(+), 48 deletions(-) diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index 2bd8f57bd29..33b69929bd9 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -22,6 +22,13 @@ Adds a button with [Texture2D] [param button] at column [param column]. The [param id] is used to identify the button in the according [signal Tree.button_clicked] signal and can be different from the buttons index. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately before this method. Optionally, the button can be [param disabled] and have a [param tooltip_text]. + + + + + Adds a previously unparented [TreeItem] as a direct child of this one. The [param child] item must not be a part of any [Tree] or parented to any [TreeItem]. See also [method remove_child]. + + @@ -430,7 +437,8 @@ - Removes the given child [TreeItem] and all its children from the [Tree]. Note that it doesn't free the item from memory, so it can be reused later. To completely remove a [TreeItem] use [method Object.free]. + Removes the given child [TreeItem] and all its children from the [Tree]. Note that it doesn't free the item from memory, so it can be reused later (see [method add_child]). To completely remove a [TreeItem] use [method Object.free]. + [b]Note:[/b] If you want to move a child from one [Tree] to another, then instead of removing and adding it manually you can use [method move_before] or [method move_after]. diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 9ec461cad48..8afd4af19d4 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -677,23 +677,26 @@ TreeItem *TreeItem::create_child(int p_index) { tree->queue_redraw(); } - TreeItem *l_prev = nullptr; - TreeItem *c = first_child; - int idx = 0; + TreeItem *item_prev = nullptr; + TreeItem *item_next = first_child; - while (c) { - if (idx++ == p_index) { - c->prev = ti; - ti->next = c; + int idx = 0; + while (item_next) { + if (idx == p_index) { + item_next->prev = ti; + ti->next = item_next; break; } - l_prev = c; - c = c->next; + + item_prev = item_next; + item_next = item_next->next; + idx++; } - if (l_prev) { - l_prev->next = ti; - ti->prev = l_prev; + if (item_prev) { + item_prev->next = ti; + ti->prev = item_prev; + if (!children_cache.is_empty()) { if (ti->next) { children_cache.insert(p_index, ti); @@ -713,6 +716,46 @@ TreeItem *TreeItem::create_child(int p_index) { return ti; } +void TreeItem::add_child(TreeItem *p_item) { + ERR_FAIL_NULL(p_item); + ERR_FAIL_COND(p_item->tree); + ERR_FAIL_COND(p_item->parent); + + p_item->_change_tree(tree); + p_item->parent = this; + + TreeItem *item_prev = first_child; + while (item_prev && item_prev->next) { + item_prev = item_prev->next; + } + + if (item_prev) { + item_prev->next = p_item; + p_item->prev = item_prev; + } else { + first_child = p_item; + } + + if (!children_cache.is_empty()) { + children_cache.append(p_item); + } + + validate_cache(); +} + +void TreeItem::remove_child(TreeItem *p_item) { + ERR_FAIL_NULL(p_item); + ERR_FAIL_COND(p_item->parent != this); + + p_item->_unlink_from_tree(); + p_item->_change_tree(nullptr); + p_item->prev = nullptr; + p_item->next = nullptr; + p_item->parent = nullptr; + + validate_cache(); +} + Tree *TreeItem::get_tree() const { return tree; } @@ -888,6 +931,18 @@ TypedArray TreeItem::get_children() { return arr; } +void TreeItem::clear_children() { + TreeItem *c = first_child; + while (c) { + TreeItem *aux = c; + c = c->get_next(); + aux->parent = nullptr; // So it won't try to recursively auto-remove from me in here. + memdelete(aux); + } + + first_child = nullptr; +}; + int TreeItem::get_index() { int idx = 0; TreeItem *c = this; @@ -943,7 +998,7 @@ void TreeItem::move_before(TreeItem *p_item) { } else { parent->first_child = this; // If the cache is empty, it has not been built but there - // are items in the tree (note p_item != nullptr,) so we cannot update it. + // are items in the tree (note p_item != nullptr) so we cannot update it. if (!parent->children_cache.is_empty()) { parent->children_cache.insert(0, this); } @@ -1003,21 +1058,6 @@ void TreeItem::move_after(TreeItem *p_item) { validate_cache(); } -void TreeItem::remove_child(TreeItem *p_item) { - ERR_FAIL_NULL(p_item); - ERR_FAIL_COND(p_item->parent != this); - - p_item->_unlink_from_tree(); - p_item->prev = nullptr; - p_item->next = nullptr; - p_item->parent = nullptr; - - if (tree) { - tree->queue_redraw(); - } - validate_cache(); -} - void TreeItem::set_selectable(int p_column, bool p_selectable) { ERR_FAIL_INDEX(p_column, cells.size()); cells.write[p_column].selectable = p_selectable; @@ -1542,6 +1582,9 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("is_folding_disabled"), &TreeItem::is_folding_disabled); ClassDB::bind_method(D_METHOD("create_child", "index"), &TreeItem::create_child, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_child", "child"), &TreeItem::add_child); + ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::remove_child); + ClassDB::bind_method(D_METHOD("get_tree"), &TreeItem::get_tree); ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next); @@ -1563,8 +1606,6 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::move_before); ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::move_after); - ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::remove_child); - { MethodInfo mi; mi.name = "call_recursive"; @@ -1585,28 +1626,17 @@ void TreeItem::_bind_methods() { BIND_ENUM_CONSTANT(CELL_MODE_CUSTOM); } -void TreeItem::clear_children() { - TreeItem *c = first_child; - while (c) { - TreeItem *aux = c; - c = c->get_next(); - aux->parent = nullptr; // so it won't try to recursively autoremove from me in here - memdelete(aux); - } - - first_child = nullptr; -}; - TreeItem::TreeItem(Tree *p_tree) { tree = p_tree; } TreeItem::~TreeItem() { _unlink_from_tree(); + _change_tree(nullptr); + validate_cache(); prev = nullptr; clear_children(); - _change_tree(nullptr); } /**********************************************/ @@ -4484,6 +4514,7 @@ bool Tree::is_column_expanding(int p_column) const { return columns[p_column].expand; } + int Tree::get_column_expand_ratio(int p_column) const { ERR_FAIL_INDEX_V(p_column, columns.size(), 1); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 24b649b0407..a5122bb1a79 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -336,6 +336,8 @@ public: /* Item manipulation */ TreeItem *create_child(int p_index = -1); + void add_child(TreeItem *p_item); + void remove_child(TreeItem *p_item); Tree *get_tree() const; @@ -354,6 +356,7 @@ public: int get_visible_child_count(); int get_child_count(); TypedArray get_children(); + void clear_children(); int get_index(); #ifdef DEV_ENABLED @@ -366,12 +369,8 @@ public: void move_before(TreeItem *p_item); void move_after(TreeItem *p_item); - void remove_child(TreeItem *p_item); - void call_recursive(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); - void clear_children(); - ~TreeItem(); };