Merge pull request #62666 from AThousandShips/tree_folding

Tree recursive folding (like Scene Tree Dock)
This commit is contained in:
Rémi Verschelde 2022-09-21 15:36:43 +02:00
commit 057dd292e4
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.
This controls the drop sections, i.e. the decision and drawing of possible drop locations based on the mouse position.
</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="hide_folding" type="bool" setter="set_hide_folding" getter="is_folding_hidden" default="false">
If [code]true[/code], the folding arrow is hidden.

View file

@ -321,6 +321,14 @@
Returns the [Tree] that owns this TreeItem.
</description>
</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">
<return type="bool" />
<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.
</description>
</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">
<return type="void" />
<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: {
// Expand or collapse the folder
if (selected_strings.size() == 1) {
bool is_collapsed = (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);
}
tree->get_selected()->set_collapsed_recursive(p_option == FOLDER_COLLAPSE_ALL);
}
} break;
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);
_set_collapsed_recursive(selected_item, !collapsed);
bool collapsed = selected_item->is_any_collapsed();
selected_item->set_collapsed_recursive(!collapsed);
tree->ensure_cursor_is_visible();
@ -1223,17 +1223,6 @@ void SceneTreeDock::add_root_node(Node *p_node) {
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) {
switch (p_what) {
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();
}
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) {
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->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));

View file

@ -137,7 +137,6 @@ class SceneTreeDock : public VBoxContainer {
HBoxContainer *tool_hbc = nullptr;
void _tool_selected(int p_tool, bool p_confirm_override = false);
void _property_selected(int p_idx);
void _node_collapsed(Object *p_obj);
Node *property_drop_node = nullptr;
String resource_drop_path;
@ -188,9 +187,6 @@ class SceneTreeDock : public VBoxContainer {
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);
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);
enum ReplaceOwnerMode {

View file

@ -563,6 +563,57 @@ bool TreeItem::is_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) {
if (visible == p_visible) {
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("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("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))) {
p_item->set_collapsed(!p_item->is_collapsed());
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());
}
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()) {
p_item->set_collapsed(!p_item->is_collapsed());
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());
}
return -1; //collapse/uncollapse because nothing can be done with item
}
@ -5026,6 +5088,14 @@ bool Tree::is_folding_hidden() const {
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) {
if (drop_mode_flags == p_flags) {
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("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("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_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, "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::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");

View file

@ -173,6 +173,8 @@ private:
}
}
bool _is_any_collapsed(bool p_only_visible);
protected:
static void _bind_methods();
@ -272,6 +274,9 @@ public:
void set_collapsed(bool p_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);
bool is_visible();
@ -613,6 +618,8 @@ private:
bool hide_folding = false;
bool enable_recursive_folding = true;
int _count_selected_items(TreeItem *p_from) const;
bool _is_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);
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);
int get_drop_mode_flags() const;