Tree recursive folding

This commit is contained in:
Ninni Pipping 2022-07-01 15:43:39 +02:00
parent 307f560b39
commit df9eaf5f47
7 changed files with 107 additions and 78 deletions

View File

@ -312,6 +312,9 @@
The drop mode as an OR combination of flags. See [enum DropModeFlags] constants. Once dropping is done, reverts to [constant DROP_MODE_DISABLED]. Setting this during [method Control._can_drop_data] is recommended. The drop mode as an OR combination of flags. See [enum DropModeFlags] constants. Once dropping is done, reverts to [constant DROP_MODE_DISABLED]. Setting this during [method Control._can_drop_data] is recommended.
This controls the drop sections, i.e. the decision and drawing of possible drop locations based on the mouse position. This controls the drop sections, i.e. the decision and drawing of possible drop locations based on the mouse position.
</member> </member>
<member name="enable_recursive_folding" type="bool" setter="set_enable_recursive_folding" getter="is_recursive_folding_enabled" default="true">
If [code]true[/code], recursive folding is enabled for this [Tree]. Holding down Shift while clicking the fold arrow collapses or uncollapses the [TreeItem] and all its descendants.
</member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" /> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" />
<member name="hide_folding" type="bool" setter="set_hide_folding" getter="is_folding_hidden" default="false"> <member name="hide_folding" type="bool" setter="set_hide_folding" getter="is_folding_hidden" default="false">
If [code]true[/code], the folding arrow is hidden. If [code]true[/code], the folding arrow is hidden.

View File

@ -321,6 +321,14 @@
Returns the [Tree] that owns this TreeItem. Returns the [Tree] that owns this TreeItem.
</description> </description>
</method> </method>
<method name="is_any_collapsed">
<return type="bool" />
<param index="0" name="only_visible" type="bool" default="false" />
<description>
Returns [code]true[/code] if this [TreeItem], or any of its descendants, is collapsed.
If [param only_visible] is [code]true[/code] it ignores non-visible [TreeItem]s.
</description>
</method>
<method name="is_button_disabled" qualifiers="const"> <method name="is_button_disabled" qualifiers="const">
<return type="bool" /> <return type="bool" />
<param index="0" name="column" type="int" /> <param index="0" name="column" type="int" />
@ -442,6 +450,13 @@
If [code]true[/code], the given [param column] is checked. Clears column's indeterminate status. If [code]true[/code], the given [param column] is checked. Clears column's indeterminate status.
</description> </description>
</method> </method>
<method name="set_collapsed_recursive">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
Collapses or uncollapses this [TreeItem] and all the descendants of this item.
</description>
</method>
<method name="set_custom_as_button"> <method name="set_custom_as_button">
<return type="void" /> <return type="void" />
<param index="0" name="column" type="int" /> <param index="0" name="column" type="int" />

View File

@ -1751,22 +1751,7 @@ void FileSystemDock::_tree_rmb_option(int p_option) {
case FOLDER_COLLAPSE_ALL: { case FOLDER_COLLAPSE_ALL: {
// Expand or collapse the folder // Expand or collapse the folder
if (selected_strings.size() == 1) { if (selected_strings.size() == 1) {
bool is_collapsed = (p_option == FOLDER_COLLAPSE_ALL); tree->get_selected()->set_collapsed_recursive(p_option == FOLDER_COLLAPSE_ALL);
Vector<TreeItem *> needs_check;
needs_check.push_back(tree->get_selected());
while (needs_check.size()) {
needs_check[0]->set_collapsed(is_collapsed);
TreeItem *child = needs_check[0]->get_first_child();
while (child) {
needs_check.push_back(child);
child = child->get_next();
}
needs_check.remove_at(0);
}
} }
} break; } break;
default: { default: {

View File

@ -441,8 +441,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
} }
} }
bool collapsed = _is_collapsed_recursive(selected_item); bool collapsed = selected_item->is_any_collapsed();
_set_collapsed_recursive(selected_item, !collapsed); selected_item->set_collapsed_recursive(!collapsed);
tree->ensure_cursor_is_visible(); tree->ensure_cursor_is_visible();
@ -1223,17 +1223,6 @@ void SceneTreeDock::add_root_node(Node *p_node) {
editor_data->get_undo_redo()->commit_action(); editor_data->get_undo_redo()->commit_action();
} }
void SceneTreeDock::_node_collapsed(Object *p_obj) {
TreeItem *ti = Object::cast_to<TreeItem>(p_obj);
if (!ti) {
return;
}
if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
_set_collapsed_recursive(ti, ti->is_collapsed());
}
}
void SceneTreeDock::_notification(int p_what) { void SceneTreeDock::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
@ -1945,48 +1934,6 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
editor_data->get_undo_redo()->commit_action(); editor_data->get_undo_redo()->commit_action();
} }
bool SceneTreeDock::_is_collapsed_recursive(TreeItem *p_item) const {
bool is_branch_collapsed = false;
List<TreeItem *> needs_check;
needs_check.push_back(p_item);
while (!needs_check.is_empty()) {
TreeItem *item = needs_check.back()->get();
needs_check.pop_back();
TreeItem *child = item->get_first_child();
is_branch_collapsed = item->is_collapsed() && child;
if (is_branch_collapsed) {
break;
}
while (child) {
needs_check.push_back(child);
child = child->get_next();
}
}
return is_branch_collapsed;
}
void SceneTreeDock::_set_collapsed_recursive(TreeItem *p_item, bool p_collapsed) {
List<TreeItem *> to_collapse;
to_collapse.push_back(p_item);
while (!to_collapse.is_empty()) {
TreeItem *item = to_collapse.back()->get();
to_collapse.pop_back();
item->set_collapsed(p_collapsed);
TreeItem *child = item->get_first_child();
while (child) {
to_collapse.push_back(child);
child = child->get_next();
}
}
}
void SceneTreeDock::_script_created(Ref<Script> p_script) { void SceneTreeDock::_script_created(Ref<Script> p_script) {
List<Node *> selected = editor_selection->get_selected_node_list(); List<Node *> selected = editor_selection->get_selected_node_list();
@ -3532,7 +3479,6 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
scene_tree->connect("nodes_dragged", callable_mp(this, &SceneTreeDock::_nodes_drag_begin)); scene_tree->connect("nodes_dragged", callable_mp(this, &SceneTreeDock::_nodes_drag_begin));
scene_tree->get_scene_tree()->connect("item_double_clicked", callable_mp(this, &SceneTreeDock::_focus_node)); scene_tree->get_scene_tree()->connect("item_double_clicked", callable_mp(this, &SceneTreeDock::_focus_node));
scene_tree->get_scene_tree()->connect("item_collapsed", callable_mp(this, &SceneTreeDock::_node_collapsed));
editor_selection->connect("selection_changed", callable_mp(this, &SceneTreeDock::_selection_changed)); editor_selection->connect("selection_changed", callable_mp(this, &SceneTreeDock::_selection_changed));

View File

@ -137,7 +137,6 @@ class SceneTreeDock : public VBoxContainer {
HBoxContainer *tool_hbc = nullptr; HBoxContainer *tool_hbc = nullptr;
void _tool_selected(int p_tool, bool p_confirm_override = false); void _tool_selected(int p_tool, bool p_confirm_override = false);
void _property_selected(int p_idx); void _property_selected(int p_idx);
void _node_collapsed(Object *p_obj);
Node *property_drop_node = nullptr; Node *property_drop_node = nullptr;
String resource_drop_path; String resource_drop_path;
@ -188,9 +187,6 @@ class SceneTreeDock : public VBoxContainer {
void _node_reparent(NodePath p_path, bool p_keep_global_xform); void _node_reparent(NodePath p_path, bool p_keep_global_xform);
void _do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform); void _do_reparent(Node *p_new_parent, int p_position_in_parent, Vector<Node *> p_nodes, bool p_keep_global_xform);
bool _is_collapsed_recursive(TreeItem *p_item) const;
void _set_collapsed_recursive(TreeItem *p_item, bool p_collapsed);
void _set_owners(Node *p_owner, const Array &p_nodes); void _set_owners(Node *p_owner, const Array &p_nodes);
enum ReplaceOwnerMode { enum ReplaceOwnerMode {

View File

@ -563,6 +563,57 @@ bool TreeItem::is_collapsed() {
return collapsed; return collapsed;
} }
void TreeItem::set_collapsed_recursive(bool p_collapsed) {
if (!tree) {
return;
}
set_collapsed(p_collapsed);
TreeItem *child = get_first_child();
while (child) {
child->set_collapsed_recursive(p_collapsed);
child = child->get_next();
}
}
bool TreeItem::_is_any_collapsed(bool p_only_visible) {
TreeItem *child = get_first_child();
// Check on children directly first (avoid recursing if possible).
while (child) {
if (child->get_first_child() && child->is_collapsed() && (!p_only_visible || (child->is_visible() && child->get_visible_child_count()))) {
return true;
}
child = child->get_next();
}
child = get_first_child();
// Otherwise recurse on children.
while (child) {
if (child->get_first_child() && (!p_only_visible || (child->is_visible() && child->get_visible_child_count())) && child->_is_any_collapsed(p_only_visible)) {
return true;
}
child = child->get_next();
}
return false;
}
bool TreeItem::is_any_collapsed(bool p_only_visible) {
if (p_only_visible && !is_visible()) {
return false;
}
// Collapsed if this is collapsed and it has children (only considers visible if only visible is set).
if (is_collapsed() && get_first_child() && (!p_only_visible || get_visible_child_count())) {
return true;
}
return _is_any_collapsed(p_only_visible);
}
void TreeItem::set_visible(bool p_visible) { void TreeItem::set_visible(bool p_visible) {
if (visible == p_visible) { if (visible == p_visible) {
return; return;
@ -1406,6 +1457,9 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collapsed", "enable"), &TreeItem::set_collapsed); ClassDB::bind_method(D_METHOD("set_collapsed", "enable"), &TreeItem::set_collapsed);
ClassDB::bind_method(D_METHOD("is_collapsed"), &TreeItem::is_collapsed); ClassDB::bind_method(D_METHOD("is_collapsed"), &TreeItem::is_collapsed);
ClassDB::bind_method(D_METHOD("set_collapsed_recursive", "enable"), &TreeItem::set_collapsed_recursive);
ClassDB::bind_method(D_METHOD("is_any_collapsed", "only_visible"), &TreeItem::is_any_collapsed, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_visible", "enable"), &TreeItem::set_visible); ClassDB::bind_method(D_METHOD("set_visible", "enable"), &TreeItem::set_visible);
ClassDB::bind_method(D_METHOD("is_visible"), &TreeItem::is_visible); ClassDB::bind_method(D_METHOD("is_visible"), &TreeItem::is_visible);
@ -2572,7 +2626,11 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
} }
if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + theme_cache.item_margin))) { if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + theme_cache.item_margin))) {
if (enable_recursive_folding && p_mod->is_shift_pressed()) {
p_item->set_collapsed_recursive(!p_item->is_collapsed());
} else {
p_item->set_collapsed(!p_item->is_collapsed()); p_item->set_collapsed(!p_item->is_collapsed());
}
return -1; return -1;
} }
@ -2623,7 +2681,11 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
} }
if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) { if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) {
if (enable_recursive_folding && p_mod->is_shift_pressed()) {
p_item->set_collapsed_recursive(!p_item->is_collapsed());
} else {
p_item->set_collapsed(!p_item->is_collapsed()); p_item->set_collapsed(!p_item->is_collapsed());
}
return -1; //collapse/uncollapse because nothing can be done with item return -1; //collapse/uncollapse because nothing can be done with item
} }
@ -5026,6 +5088,14 @@ bool Tree::is_folding_hidden() const {
return hide_folding; return hide_folding;
} }
void Tree::set_enable_recursive_folding(bool p_enable) {
enable_recursive_folding = p_enable;
}
bool Tree::is_recursive_folding_enabled() const {
return enable_recursive_folding;
}
void Tree::set_drop_mode_flags(int p_flags) { void Tree::set_drop_mode_flags(int p_flags) {
if (drop_mode_flags == p_flags) { if (drop_mode_flags == p_flags) {
return; return;
@ -5129,6 +5199,9 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_folding", "hide"), &Tree::set_hide_folding); ClassDB::bind_method(D_METHOD("set_hide_folding", "hide"), &Tree::set_hide_folding);
ClassDB::bind_method(D_METHOD("is_folding_hidden"), &Tree::is_folding_hidden); ClassDB::bind_method(D_METHOD("is_folding_hidden"), &Tree::is_folding_hidden);
ClassDB::bind_method(D_METHOD("set_enable_recursive_folding", "enable"), &Tree::set_enable_recursive_folding);
ClassDB::bind_method(D_METHOD("is_recursive_folding_enabled"), &Tree::is_recursive_folding_enabled);
ClassDB::bind_method(D_METHOD("set_drop_mode_flags", "flags"), &Tree::set_drop_mode_flags); ClassDB::bind_method(D_METHOD("set_drop_mode_flags", "flags"), &Tree::set_drop_mode_flags);
ClassDB::bind_method(D_METHOD("get_drop_mode_flags"), &Tree::get_drop_mode_flags); ClassDB::bind_method(D_METHOD("get_drop_mode_flags"), &Tree::get_drop_mode_flags);
@ -5143,6 +5216,7 @@ void Tree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_folding"), "set_hide_folding", "is_folding_hidden"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_folding"), "set_hide_folding", "is_folding_hidden");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_recursive_folding"), "set_enable_recursive_folding", "is_recursive_folding_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_root"), "set_hide_root", "is_root_hidden"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_root"), "set_hide_root", "is_root_hidden");
ADD_PROPERTY(PropertyInfo(Variant::INT, "drop_mode_flags", PROPERTY_HINT_FLAGS, "On Item,In Between"), "set_drop_mode_flags", "get_drop_mode_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "drop_mode_flags", PROPERTY_HINT_FLAGS, "On Item,In Between"), "set_drop_mode_flags", "get_drop_mode_flags");
ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Row,Multi"), "set_select_mode", "get_select_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Row,Multi"), "set_select_mode", "get_select_mode");

View File

@ -173,6 +173,8 @@ private:
} }
} }
bool _is_any_collapsed(bool p_only_visible);
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -272,6 +274,9 @@ public:
void set_collapsed(bool p_collapsed); void set_collapsed(bool p_collapsed);
bool is_collapsed(); bool is_collapsed();
void set_collapsed_recursive(bool p_collapsed);
bool is_any_collapsed(bool p_only_visible = false);
void set_visible(bool p_visible); void set_visible(bool p_visible);
bool is_visible(); bool is_visible();
@ -613,6 +618,8 @@ private:
bool hide_folding = false; bool hide_folding = false;
bool enable_recursive_folding = true;
int _count_selected_items(TreeItem *p_from) const; int _count_selected_items(TreeItem *p_from) const;
bool _is_branch_selected(TreeItem *p_from) const; bool _is_branch_selected(TreeItem *p_from) const;
bool _is_sibling_branch_selected(TreeItem *p_from) const; bool _is_sibling_branch_selected(TreeItem *p_from) const;
@ -712,6 +719,9 @@ public:
void set_hide_folding(bool p_hide); void set_hide_folding(bool p_hide);
bool is_folding_hidden() const; bool is_folding_hidden() const;
void set_enable_recursive_folding(bool p_enable);
bool is_recursive_folding_enabled() const;
void set_drop_mode_flags(int p_flags); void set_drop_mode_flags(int p_flags);
int get_drop_mode_flags() const; int get_drop_mode_flags() const;