瀏覽代碼

Merge pull request #62666 from AThousandShips/tree_folding

Tree recursive folding (like Scene Tree Dock)
Rémi Verschelde 3 年之前
父節點
當前提交
057dd292e4
共有 7 個文件被更改,包括 107 次插入78 次删除
  1. 3 0
      doc/classes/Tree.xml
  2. 15 0
      doc/classes/TreeItem.xml
  3. 1 16
      editor/filesystem_dock.cpp
  4. 2 56
      editor/scene_tree_dock.cpp
  5. 0 4
      editor/scene_tree_dock.h
  6. 76 2
      scene/gui/tree.cpp
  7. 10 0
      scene/gui/tree.h

+ 3 - 0
doc/classes/Tree.xml

@@ -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.

+ 15 - 0
doc/classes/TreeItem.xml

@@ -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" />

+ 1 - 16
editor/filesystem_dock.cpp

@@ -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: {

+ 2 - 56
editor/scene_tree_dock.cpp

@@ -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));
 

+ 0 - 4
editor/scene_tree_dock.h

@@ -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 {

+ 76 - 2
scene/gui/tree.cpp

@@ -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");

+ 10 - 0
scene/gui/tree.h

@@ -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;