mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 05:31:01 +00:00 
			
		
		
		
	Improve node pasting
This commit is contained in:
		
							parent
							
								
									01eefa2c50
								
							
						
					
					
						commit
						6630eb3b5a
					
				
					 2 changed files with 124 additions and 76 deletions
				
			
		|  | @ -445,8 +445,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { | ||||||
| 				break; | 				break; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			bool was_empty = false; | ||||||
| 			if (!node_clipboard.is_empty()) { | 			if (!node_clipboard.is_empty()) { | ||||||
| 				_clear_clipboard(); | 				_clear_clipboard(); | ||||||
|  | 			} else { | ||||||
|  | 				was_empty = true; | ||||||
| 			} | 			} | ||||||
| 			clipboard_source_scene = editor->get_edited_scene()->get_scene_file_path(); | 			clipboard_source_scene = editor->get_edited_scene()->get_scene_file_path(); | ||||||
| 
 | 
 | ||||||
|  | @ -464,81 +467,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { | ||||||
| 			if (p_tool == TOOL_CUT) { | 			if (p_tool == TOOL_CUT) { | ||||||
| 				_delete_confirm(true); | 				_delete_confirm(true); | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			if (was_empty) { | ||||||
|  | 				_update_create_root_dialog(); | ||||||
|  | 			} | ||||||
| 		} break; | 		} break; | ||||||
| 		case TOOL_PASTE: { | 		case TOOL_PASTE: { | ||||||
| 			if (node_clipboard.is_empty() || !edited_scene) { | 			paste_nodes(); | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			bool has_cycle = false; |  | ||||||
| 			if (!edited_scene->get_scene_file_path().is_empty()) { |  | ||||||
| 				for (Node *E : node_clipboard) { |  | ||||||
| 					if (edited_scene->get_scene_file_path() == E->get_scene_file_path()) { |  | ||||||
| 						has_cycle = true; |  | ||||||
| 						break; |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (has_cycle) { |  | ||||||
| 				current_option = -1; |  | ||||||
| 				accept->set_text(TTR("Can't paste root node into the same scene.")); |  | ||||||
| 				accept->popup_centered(); |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			Node *paste_parent = edited_scene; |  | ||||||
| 			List<Node *> selection = editor_selection->get_selected_node_list(); |  | ||||||
| 			if (selection.size() > 0) { |  | ||||||
| 				paste_parent = selection.back()->get(); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			Node *owner = paste_parent->get_owner(); |  | ||||||
| 			if (!owner) { |  | ||||||
| 				owner = paste_parent; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			editor_data->get_undo_redo().create_action(TTR("Paste Node(s)")); |  | ||||||
| 			editor_data->get_undo_redo().add_do_method(editor_selection, "clear"); |  | ||||||
| 
 |  | ||||||
| 			Map<RES, RES> resource_remap; |  | ||||||
| 			String target_scene = editor->get_edited_scene()->get_scene_file_path(); |  | ||||||
| 			if (target_scene != clipboard_source_scene) { |  | ||||||
| 				if (!clipboard_resource_remap.has(target_scene)) { |  | ||||||
| 					Map<RES, RES> remap; |  | ||||||
| 					for (Node *E : node_clipboard) { |  | ||||||
| 						_create_remap_for_node(E, remap); |  | ||||||
| 					} |  | ||||||
| 					clipboard_resource_remap[target_scene] = remap; |  | ||||||
| 				} |  | ||||||
| 				resource_remap = clipboard_resource_remap[target_scene]; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			for (Node *node : node_clipboard) { |  | ||||||
| 				Map<const Node *, Node *> duplimap; |  | ||||||
| 
 |  | ||||||
| 				Node *dup = node->duplicate_from_editor(duplimap, resource_remap); |  | ||||||
| 
 |  | ||||||
| 				ERR_CONTINUE(!dup); |  | ||||||
| 
 |  | ||||||
| 				editor_data->get_undo_redo().add_do_method(paste_parent, "add_child", dup, true); |  | ||||||
| 
 |  | ||||||
| 				for (KeyValue<const Node *, Node *> &E2 : duplimap) { |  | ||||||
| 					Node *d = E2.value; |  | ||||||
| 					editor_data->get_undo_redo().add_do_method(d, "set_owner", owner); |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				editor_data->get_undo_redo().add_do_method(dup, "set_owner", owner); |  | ||||||
| 				editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", dup); |  | ||||||
| 				editor_data->get_undo_redo().add_undo_method(paste_parent, "remove_child", dup); |  | ||||||
| 				editor_data->get_undo_redo().add_do_reference(dup); |  | ||||||
| 
 |  | ||||||
| 				if (node_clipboard.size() == 1) { |  | ||||||
| 					editor_data->get_undo_redo().add_do_method(editor, "push_item", dup); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			editor_data->get_undo_redo().commit_action(); |  | ||||||
| 		} break; | 		} break; | ||||||
| 		case TOOL_REPLACE: { | 		case TOOL_REPLACE: { | ||||||
| 			if (!profile_allow_editing) { | 			if (!profile_allow_editing) { | ||||||
|  | @ -1306,6 +1241,12 @@ void SceneTreeDock::_notification(int p_what) { | ||||||
| 			button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); | 			button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); | ||||||
| 			button_custom->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_NEW, false)); | 			button_custom->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_NEW, false)); | ||||||
| 
 | 
 | ||||||
|  | 			button_clipboard = memnew(Button); | ||||||
|  | 			node_shortcuts->add_child(button_clipboard); | ||||||
|  | 			button_clipboard->set_text(TTR("Paste From Clipboard")); | ||||||
|  | 			button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons"))); | ||||||
|  | 			button_clipboard->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_PASTE, false)); | ||||||
|  | 
 | ||||||
| 			node_shortcuts->add_spacer(); | 			node_shortcuts->add_spacer(); | ||||||
| 			create_root_dialog->add_child(node_shortcuts); | 			create_root_dialog->add_child(node_shortcuts); | ||||||
| 			_update_create_root_dialog(); | 			_update_create_root_dialog(); | ||||||
|  | @ -1330,6 +1271,7 @@ void SceneTreeDock::_notification(int p_what) { | ||||||
| 			button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons"))); | 			button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons"))); | ||||||
| 			button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons"))); | 			button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons"))); | ||||||
| 			button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); | 			button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); | ||||||
|  | 			button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons"))); | ||||||
| 
 | 
 | ||||||
| 			filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); | 			filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); | ||||||
| 			filter->set_clear_button_enabled(true); | 			filter->set_clear_button_enabled(true); | ||||||
|  | @ -2740,10 +2682,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (profile_allow_editing) { | 	if (profile_allow_editing) { | ||||||
| 		menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT); | 		menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCut"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT); | ||||||
| 		menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY); | 		menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY); | ||||||
| 		if (selection.size() == 1 && !node_clipboard.is_empty()) { | 		if (selection.size() == 1 && !node_clipboard.is_empty()) { | ||||||
| 			menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE); | 			menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE); | ||||||
| 		} | 		} | ||||||
| 		menu->add_separator(); | 		menu->add_separator(); | ||||||
| 	} | 	} | ||||||
|  | @ -3022,6 +2964,108 @@ void SceneTreeDock::open_instance_child_dialog() { | ||||||
| 	_tool_selected(TOOL_INSTANTIATE, true); | 	_tool_selected(TOOL_INSTANTIATE, true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | List<Node *> SceneTreeDock::paste_nodes() { | ||||||
|  | 	List<Node *> pasted_nodes; | ||||||
|  | 
 | ||||||
|  | 	if (node_clipboard.is_empty()) { | ||||||
|  | 		return pasted_nodes; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool has_cycle = false; | ||||||
|  | 	if (edited_scene && !edited_scene->get_scene_file_path().is_empty()) { | ||||||
|  | 		for (Node *E : node_clipboard) { | ||||||
|  | 			if (edited_scene->get_scene_file_path() == E->get_scene_file_path()) { | ||||||
|  | 				has_cycle = true; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (has_cycle) { | ||||||
|  | 		current_option = -1; | ||||||
|  | 		accept->set_text(TTR("Can't paste root node into the same scene.")); | ||||||
|  | 		accept->popup_centered(); | ||||||
|  | 		return pasted_nodes; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Node *paste_parent = edited_scene; | ||||||
|  | 	List<Node *> selection = editor_selection->get_selected_node_list(); | ||||||
|  | 	if (selection.size() > 0) { | ||||||
|  | 		paste_parent = selection.back()->get(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Node *owner = nullptr; | ||||||
|  | 	if (paste_parent) { | ||||||
|  | 		owner = paste_parent->get_owner(); | ||||||
|  | 	} | ||||||
|  | 	if (!owner) { | ||||||
|  | 		owner = paste_parent; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	UndoRedo &ur = editor_data->get_undo_redo(); | ||||||
|  | 	ur.create_action(TTR("Paste Node(s)")); | ||||||
|  | 	ur.add_do_method(editor_selection, "clear"); | ||||||
|  | 
 | ||||||
|  | 	Map<RES, RES> resource_remap; | ||||||
|  | 	String target_scene; | ||||||
|  | 	if (edited_scene) { | ||||||
|  | 		target_scene = edited_scene->get_scene_file_path(); | ||||||
|  | 	} | ||||||
|  | 	if (target_scene != clipboard_source_scene) { | ||||||
|  | 		if (!clipboard_resource_remap.has(target_scene)) { | ||||||
|  | 			Map<RES, RES> remap; | ||||||
|  | 			for (Node *E : node_clipboard) { | ||||||
|  | 				_create_remap_for_node(E, remap); | ||||||
|  | 			} | ||||||
|  | 			clipboard_resource_remap[target_scene] = remap; | ||||||
|  | 		} | ||||||
|  | 		resource_remap = clipboard_resource_remap[target_scene]; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (Node *node : node_clipboard) { | ||||||
|  | 		Map<const Node *, Node *> duplimap; | ||||||
|  | 
 | ||||||
|  | 		Node *dup = node->duplicate_from_editor(duplimap, resource_remap); | ||||||
|  | 		ERR_CONTINUE(!dup); | ||||||
|  | 
 | ||||||
|  | 		pasted_nodes.push_back(dup); | ||||||
|  | 
 | ||||||
|  | 		if (!paste_parent) { | ||||||
|  | 			paste_parent = dup; | ||||||
|  | 			owner = dup; | ||||||
|  | 			ur.add_do_method(editor, "set_edited_scene", dup); | ||||||
|  | 		} else { | ||||||
|  | 			ur.add_do_method(paste_parent, "add_child", dup, true); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for (KeyValue<const Node *, Node *> &E2 : duplimap) { | ||||||
|  | 			Node *d = E2.value; | ||||||
|  | 			if (d != dup) { | ||||||
|  | 				ur.add_do_method(d, "set_owner", owner); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (dup != owner) { | ||||||
|  | 			ur.add_do_method(dup, "set_owner", owner); | ||||||
|  | 		} | ||||||
|  | 		ur.add_do_method(editor_selection, "add_node", dup); | ||||||
|  | 
 | ||||||
|  | 		if (dup == paste_parent) { | ||||||
|  | 			ur.add_undo_method(editor, "set_edited_scene", (Object *)nullptr); | ||||||
|  | 		} else { | ||||||
|  | 			ur.add_undo_method(paste_parent, "remove_child", dup); | ||||||
|  | 		} | ||||||
|  | 		ur.add_do_reference(dup); | ||||||
|  | 
 | ||||||
|  | 		if (node_clipboard.size() == 1) { | ||||||
|  | 			ur.add_do_method(editor, "push_item", dup); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ur.commit_action(); | ||||||
|  | 	return pasted_nodes; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void SceneTreeDock::add_remote_tree_editor(Control *p_remote) { | void SceneTreeDock::add_remote_tree_editor(Control *p_remote) { | ||||||
| 	ERR_FAIL_COND(remote_tree != nullptr); | 	ERR_FAIL_COND(remote_tree != nullptr); | ||||||
| 	add_child(p_remote); | 	add_child(p_remote); | ||||||
|  | @ -3121,6 +3165,7 @@ void SceneTreeDock::_update_create_root_dialog() { | ||||||
| 			beginner_nodes->show(); | 			beginner_nodes->show(); | ||||||
| 			favorite_nodes->hide(); | 			favorite_nodes->hide(); | ||||||
| 		} | 		} | ||||||
|  | 		button_clipboard->set_visible(!node_clipboard.is_empty()); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -123,6 +123,7 @@ class SceneTreeDock : public VBoxContainer { | ||||||
| 	Button *button_3d; | 	Button *button_3d; | ||||||
| 	Button *button_ui; | 	Button *button_ui; | ||||||
| 	Button *button_custom; | 	Button *button_custom; | ||||||
|  | 	Button *button_clipboard; | ||||||
| 
 | 
 | ||||||
| 	HBoxContainer *button_hb; | 	HBoxContainer *button_hb; | ||||||
| 	Button *edit_local, *edit_remote; | 	Button *edit_local, *edit_remote; | ||||||
|  | @ -308,6 +309,8 @@ public: | ||||||
| 	void open_add_child_dialog(); | 	void open_add_child_dialog(); | ||||||
| 	void open_instance_child_dialog(); | 	void open_instance_child_dialog(); | ||||||
| 
 | 
 | ||||||
|  | 	List<Node *> paste_nodes(); | ||||||
|  | 
 | ||||||
| 	ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; } | 	ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; } | ||||||
| 
 | 
 | ||||||
| 	SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data); | 	SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 kobewi
						kobewi