Browse Source

Merge pull request #84995 from Bitlytic/reparent-center

Add an option to center children around the new parent when reparenting
Rémi Verschelde 1 year ago
parent
commit
d3f64bf2d2

+ 3 - 0
doc/classes/EditorSettings.xml

@@ -220,6 +220,9 @@
 		<member name="docks/scene_tree/auto_expand_to_selected" type="bool" setter="" getter="">
 		<member name="docks/scene_tree/auto_expand_to_selected" type="bool" setter="" getter="">
 			If [code]true[/code], the scene tree dock will automatically unfold nodes when a node that has folded parents is selected.
 			If [code]true[/code], the scene tree dock will automatically unfold nodes when a node that has folded parents is selected.
 		</member>
 		</member>
+		<member name="docks/scene_tree/center_node_on_reparent" type="bool" setter="" getter="">
+			If [code]true[/code], new node created when reparenting node(s) will be positioned at the average position of the selected node(s).
+		</member>
 		<member name="docks/scene_tree/start_create_dialog_fully_expanded" type="bool" setter="" getter="">
 		<member name="docks/scene_tree/start_create_dialog_fully_expanded" type="bool" setter="" getter="">
 			If [code]true[/code], the Create dialog (Create New Node/Create New Resource) will start with all its sections expanded. Otherwise, sections will be collapsed until the user starts searching (which will automatically expand sections as needed).
 			If [code]true[/code], the Create dialog (Create New Node/Create New Resource) will start with all its sections expanded. Otherwise, sections will be collapsed until the user starts searching (which will automatically expand sections as needed).
 		</member>
 		</member>

+ 1 - 0
editor/editor_settings.cpp

@@ -538,6 +538,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	// SceneTree
 	// SceneTree
 	_initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false);
 	_initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false);
 	_initial_set("docks/scene_tree/auto_expand_to_selected", true);
 	_initial_set("docks/scene_tree/auto_expand_to_selected", true);
+	_initial_set("docks/scene_tree/center_node_on_reparent", false);
 
 
 	// FileSystem
 	// FileSystem
 	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "docks/filesystem/thumbnail_size", 64, "32,128,16")
 	EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "docks/filesystem/thumbnail_size", 64, "32,128,16")

+ 55 - 0
editor/scene_tree_dock.cpp

@@ -1059,6 +1059,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 		case TOOL_AUTO_EXPAND: {
 		case TOOL_AUTO_EXPAND: {
 			scene_tree->set_auto_expand_selected(!EDITOR_GET("docks/scene_tree/auto_expand_to_selected"), true);
 			scene_tree->set_auto_expand_selected(!EDITOR_GET("docks/scene_tree/auto_expand_to_selected"), true);
 		} break;
 		} break;
+		case TOOL_CENTER_PARENT: {
+			EditorSettings::get_singleton()->set("docks/scene_tree/center_node_on_reparent", !EDITOR_GET("docks/scene_tree/center_node_on_reparent"));
+		} break;
 		case TOOL_SCENE_EDITABLE_CHILDREN: {
 		case TOOL_SCENE_EDITABLE_CHILDREN: {
 			if (!profile_allow_editing) {
 			if (!profile_allow_editing) {
 				break;
 				break;
@@ -2687,6 +2690,9 @@ void SceneTreeDock::_create() {
 		int smaller_path_to_top = first->get_path_to(scene_root).get_name_count();
 		int smaller_path_to_top = first->get_path_to(scene_root).get_name_count();
 		Node *top_node = first;
 		Node *top_node = first;
 
 
+		bool center_parent = EDITOR_GET("docks/scene_tree/center_node_on_reparent");
+		Vector<Node *> top_level_nodes;
+
 		for (List<Node *>::Element *E = selection.front()->next(); E; E = E->next()) {
 		for (List<Node *>::Element *E = selection.front()->next(); E; E = E->next()) {
 			Node *n = E->get();
 			Node *n = E->get();
 			ERR_FAIL_NULL(n);
 			ERR_FAIL_NULL(n);
@@ -2698,10 +2704,17 @@ void SceneTreeDock::_create() {
 					top_node = n;
 					top_node = n;
 					smaller_path_to_top = path_length;
 					smaller_path_to_top = path_length;
 					only_one_top_node = true;
 					only_one_top_node = true;
+					if (center_parent) {
+						top_level_nodes.clear();
+						top_level_nodes.append(n);
+					}
 				} else if (smaller_path_to_top == path_length) {
 				} else if (smaller_path_to_top == path_length) {
 					if (only_one_top_node && top_node->get_parent() != n->get_parent()) {
 					if (only_one_top_node && top_node->get_parent() != n->get_parent()) {
 						only_one_top_node = false;
 						only_one_top_node = false;
 					}
 					}
+					if (center_parent) {
+						top_level_nodes.append(n);
+					}
 				}
 				}
 			}
 			}
 		}
 		}
@@ -2722,6 +2735,44 @@ void SceneTreeDock::_create() {
 
 
 		// This works because editor_selection was cleared and populated with last created node in _do_create()
 		// This works because editor_selection was cleared and populated with last created node in _do_create()
 		Node *last_created = editor_selection->get_selected_node_list().front()->get();
 		Node *last_created = editor_selection->get_selected_node_list().front()->get();
+
+		if (center_parent) {
+			// Find parent type and only average positions of relevant nodes.
+			Node3D *parent_node_3d = Object::cast_to<Node3D>(last_created);
+			if (parent_node_3d) {
+				Vector3 position;
+				uint32_t node_count = 0;
+				for (const Node *node : nodes) {
+					const Node3D *node_3d = Object::cast_to<Node3D>(node);
+					if (node_3d) {
+						position += node_3d->get_global_position();
+						node_count++;
+					}
+				}
+
+				if (node_count > 0) {
+					parent_node_3d->set_global_position(position / node_count);
+				}
+			}
+
+			Node2D *parent_node_2d = Object::cast_to<Node2D>(last_created);
+			if (parent_node_2d) {
+				Vector2 position;
+				uint32_t node_count = 0;
+				for (const Node *node : nodes) {
+					const Node2D *node_2d = Object::cast_to<Node2D>(node);
+					if (node_2d) {
+						position += node_2d->get_global_position();
+						node_count++;
+					}
+				}
+
+				if (node_count > 0) {
+					parent_node_2d->set_global_position(position / (real_t)node_count);
+				}
+			}
+		}
+
 		_do_reparent(last_created, -1, nodes, true);
 		_do_reparent(last_created, -1, nodes, true);
 	}
 	}
 
 
@@ -3446,6 +3497,10 @@ void SceneTreeDock::_update_tree_menu() {
 	tree_menu->add_check_item(TTR("Auto Expand to Selected"), TOOL_AUTO_EXPAND);
 	tree_menu->add_check_item(TTR("Auto Expand to Selected"), TOOL_AUTO_EXPAND);
 	tree_menu->set_item_checked(tree_menu->get_item_index(TOOL_AUTO_EXPAND), EDITOR_GET("docks/scene_tree/auto_expand_to_selected"));
 	tree_menu->set_item_checked(tree_menu->get_item_index(TOOL_AUTO_EXPAND), EDITOR_GET("docks/scene_tree/auto_expand_to_selected"));
 
 
+	tree_menu->add_check_item(TTR("Center Node on Reparent"), TOOL_CENTER_PARENT);
+	tree_menu->set_item_checked(tree_menu->get_item_index(TOOL_CENTER_PARENT), EDITOR_GET("docks/scene_tree/center_node_on_reparent"));
+	tree_menu->set_item_tooltip(tree_menu->get_item_index(TOOL_CENTER_PARENT), TTR("If enabled, Reparent to New Node will create the new node in the center of the selected nodes, if possible."));
+
 	PopupMenu *resource_list = memnew(PopupMenu);
 	PopupMenu *resource_list = memnew(PopupMenu);
 	resource_list->set_name("AllResources");
 	resource_list->set_name("AllResources");
 	resource_list->connect("about_to_popup", callable_mp(this, &SceneTreeDock::_list_all_subresources).bind(resource_list));
 	resource_list->connect("about_to_popup", callable_mp(this, &SceneTreeDock::_list_all_subresources).bind(resource_list));

+ 1 - 0
editor/scene_tree_dock.h

@@ -94,6 +94,7 @@ class SceneTreeDock : public VBoxContainer {
 		TOOL_CREATE_3D_SCENE,
 		TOOL_CREATE_3D_SCENE,
 		TOOL_CREATE_USER_INTERFACE,
 		TOOL_CREATE_USER_INTERFACE,
 		TOOL_CREATE_FAVORITE,
 		TOOL_CREATE_FAVORITE,
+		TOOL_CENTER_PARENT,
 
 
 	};
 	};