Browse Source

Merge pull request #75330 from KoBeWi/make_haste_with_copy_paste

Improve file move and copy operations
Rémi Verschelde 2 years ago
parent
commit
41ed64ae1c
4 changed files with 146 additions and 74 deletions
  1. 27 8
      editor/editor_dir_dialog.cpp
  2. 9 3
      editor/editor_dir_dialog.h
  3. 99 61
      editor/filesystem_dock.cpp
  4. 11 2
      editor/filesystem_dock.h

+ 27 - 8
editor/editor_dir_dialog.cpp

@@ -30,11 +30,14 @@
 
 #include "editor_dir_dialog.h"
 
+#include "core/io/dir_access.h"
 #include "core/os/keyboard.h"
 #include "core/os/os.h"
 #include "editor/editor_file_system.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/tree.h"
 #include "servers/display_server.h"
 
 void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path) {
@@ -56,8 +59,6 @@ void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p
 		p_item->set_text(0, p_dir->get_name());
 	}
 
-	//this should be handled by EditorFileSystem already
-	//bool show_hidden = EDITOR_GET("filesystem/file_dialog/show_hidden_files");
 	updating = false;
 	for (int i = 0; i < p_dir->get_subdir_count(); i++) {
 		TreeItem *ti = tree->create_item(p_item);
@@ -78,6 +79,10 @@ void EditorDirDialog::reload(const String &p_path) {
 	must_reload = false;
 }
 
+bool EditorDirDialog::is_copy_pressed() const {
+	return copy->is_pressed();
+}
+
 void EditorDirDialog::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_ENTER_TREE: {
@@ -107,6 +112,14 @@ void EditorDirDialog::_notification(int p_what) {
 	}
 }
 
+void EditorDirDialog::_copy_toggled(bool p_pressed) {
+	if (p_pressed) {
+		set_ok_button_text(TTR("Copy"));
+	} else {
+		set_ok_button_text(TTR("Move"));
+	}
+}
+
 void EditorDirDialog::_item_collapsed(Object *p_item) {
 	TreeItem *item = Object::cast_to<TreeItem>(p_item);
 
@@ -172,8 +185,7 @@ void EditorDirDialog::_make_dir_confirm() {
 		mkdirerr->popup_centered(Size2(250, 80) * EDSCALE);
 	} else {
 		opened_paths.insert(dir);
-		//reload(dir.path_join(makedirname->get_text()));
-		EditorFileSystem::get_singleton()->scan_changes(); //we created a dir, so rescan changes
+		EditorFileSystem::get_singleton()->scan_changes(); // We created a dir, so rescan changes.
 	}
 	makedirname->set_text(""); // reset label
 }
@@ -186,11 +198,19 @@ EditorDirDialog::EditorDirDialog() {
 	set_title(TTR("Choose a Directory"));
 	set_hide_on_ok(false);
 
-	tree = memnew(Tree);
-	add_child(tree);
+	VBoxContainer *vb = memnew(VBoxContainer);
+	add_child(vb);
 
+	tree = memnew(Tree);
+	vb->add_child(tree);
+	tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	tree->connect("item_activated", callable_mp(this, &EditorDirDialog::_item_activated));
 
+	copy = memnew(CheckBox);
+	vb->add_child(copy);
+	copy->set_text(TTR("Copy File(s)"));
+	copy->connect("toggled", callable_mp(this, &EditorDirDialog::_copy_toggled));
+
 	makedir = add_button(TTR("Create Folder"), DisplayServer::get_singleton()->get_swap_cancel_ok(), "makedir");
 	makedir->connect("pressed", callable_mp(this, &EditorDirDialog::_make_dir));
 
@@ -200,7 +220,6 @@ EditorDirDialog::EditorDirDialog() {
 
 	VBoxContainer *makevb = memnew(VBoxContainer);
 	makedialog->add_child(makevb);
-	//makedialog->set_child_rect(makevb);
 
 	makedirname = memnew(LineEdit);
 	makevb->add_margin_child(TTR("Name:"), makedirname);
@@ -211,5 +230,5 @@ EditorDirDialog::EditorDirDialog() {
 	mkdirerr->set_text(TTR("Could not create folder."));
 	add_child(mkdirerr);
 
-	set_ok_button_text(TTR("Choose"));
+	set_ok_button_text(TTR("Move"));
 }

+ 9 - 3
editor/editor_dir_dialog.h

@@ -31,10 +31,12 @@
 #ifndef EDITOR_DIR_DIALOG_H
 #define EDITOR_DIR_DIALOG_H
 
-#include "core/io/dir_access.h"
-#include "editor/editor_file_system.h"
 #include "scene/gui/dialogs.h"
-#include "scene/gui/tree.h"
+
+class CheckBox;
+class EditorFileSystemDirectory;
+class Tree;
+class TreeItem;
 
 class EditorDirDialog : public ConfirmationDialog {
 	GDCLASS(EditorDirDialog, ConfirmationDialog);
@@ -48,7 +50,9 @@ class EditorDirDialog : public ConfirmationDialog {
 
 	Tree *tree = nullptr;
 	bool updating = false;
+	CheckBox *copy = nullptr;
 
+	void _copy_toggled(bool p_pressed);
 	void _item_collapsed(Object *p_item);
 	void _item_activated();
 	void _update_dir(TreeItem *p_item, EditorFileSystemDirectory *p_dir, const String &p_select_path = String());
@@ -66,6 +70,8 @@ protected:
 
 public:
 	void reload(const String &p_path = "");
+	bool is_copy_pressed() const;
+
 	EditorDirDialog();
 };
 

+ 99 - 61
editor/filesystem_dock.cpp

@@ -1454,6 +1454,29 @@ void FileSystemDock::_update_project_settings_after_move(const HashMap<String, S
 	ProjectSettings::get_singleton()->save();
 }
 
+String FileSystemDock::_get_unique_name(const FileOrFolder &p_entry, const String &p_at_path) {
+	String new_path;
+	String new_path_base;
+
+	if (p_entry.is_file) {
+		new_path = p_at_path.path_join(p_entry.path.get_file());
+		new_path_base = new_path.get_basename() + " (%d)." + new_path.get_extension();
+	} else {
+		PackedStringArray path_split = p_entry.path.split("/");
+		new_path = p_at_path.path_join(path_split[path_split.size() - 2]);
+		new_path_base = new_path + " (%d)";
+	}
+
+	int exist_counter = 1;
+	Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+	while (da->file_exists(new_path) || da->dir_exists(new_path)) {
+		exist_counter++;
+		new_path = vformat(new_path_base, exist_counter);
+	}
+
+	return new_path;
+}
+
 void FileSystemDock::_update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const {
 	Vector<String> favorites_list = EditorSettings::get_singleton()->get_favorites();
 	Vector<String> new_favorites;
@@ -1658,8 +1681,9 @@ void FileSystemDock::_duplicate_operation_confirm() {
 	_rescan();
 }
 
-void FileSystemDock::_move_with_overwrite() {
-	_move_operation_confirm(to_move_path, true);
+void FileSystemDock::_overwrite_dialog_action(bool p_overwrite) {
+	overwrite_dialog->hide();
+	_move_operation_confirm(to_move_path, to_move_or_copy, p_overwrite ? OVERWRITE_REPLACE : OVERWRITE_RENAME);
 }
 
 Vector<String> FileSystemDock::_check_existing() {
@@ -1681,14 +1705,21 @@ Vector<String> FileSystemDock::_check_existing() {
 	return conflicting_items;
 }
 
-void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_overwrite) {
-	if (!p_overwrite) {
+void FileSystemDock::_move_dialog_confirm(const String &p_path) {
+	_move_operation_confirm(p_path, move_dialog->is_copy_pressed());
+}
+
+void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_copy, Overwrite p_overwrite) {
+	if (p_overwrite == OVERWRITE_UNDECIDED) {
 		to_move_path = p_to_path;
+		to_move_or_copy = p_copy;
+
 		Vector<String> conflicting_items = _check_existing();
 		if (!conflicting_items.is_empty()) {
 			// Ask to do something.
 			overwrite_dialog->set_text(vformat(
-					TTR("The following files or folders conflict with items in the target location '%s':\n\n%s\n\nDo you wish to overwrite them?"),
+					p_copy ? TTR("The following files or folders conflict with items in the target location '%s':\n\n%s\n\nDo you wish to overwrite them or rename the copied files?")
+						   : TTR("The following files or folders conflict with items in the target location '%s':\n\n%s\n\nDo you wish to overwrite them or rename the moved files?"),
 					to_move_path,
 					String("\n").join(conflicting_items)));
 			overwrite_dialog->popup_centered();
@@ -1696,43 +1727,71 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_ove
 		}
 	}
 
-	// Check groups.
+	Vector<String> new_paths;
+	new_paths.resize(to_move.size());
 	for (int i = 0; i < to_move.size(); i++) {
-		if (to_move[i].is_file && EditorFileSystem::get_singleton()->is_group_file(to_move[i].path)) {
-			EditorFileSystem::get_singleton()->move_group_file(to_move[i].path, p_to_path.path_join(to_move[i].path.get_file()));
+		if (p_overwrite == OVERWRITE_RENAME) {
+			new_paths.write[i] = _get_unique_name(to_move[i], p_to_path);
+		} else {
+			new_paths.write[i] = p_to_path.path_join(to_move[i].path.get_file());
 		}
 	}
 
-	HashMap<String, String> file_renames;
-	HashMap<String, String> folder_renames;
-	bool is_moved = false;
-	for (int i = 0; i < to_move.size(); i++) {
-		String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
-		String new_path = p_to_path.path_join(old_path.get_file());
-		if (old_path != new_path) {
-			_try_move_item(to_move[i], new_path, file_renames, folder_renames);
-			is_moved = true;
+	if (p_copy) {
+		bool is_copied = false;
+		for (int i = 0; i < to_move.size(); i++) {
+			String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
+			const String &new_path = new_paths[i];
+
+			if (old_path != new_path) {
+				_try_duplicate_item(to_move[i], new_paths[i]);
+				is_copied = true;
+			}
 		}
-	}
 
-	if (is_moved) {
-		int current_tab = EditorNode::get_singleton()->get_current_tab();
-		_save_scenes_after_move(file_renames); // Save scenes before updating.
-		_update_dependencies_after_move(file_renames);
-		_update_resource_paths_after_move(file_renames);
-		_update_project_settings_after_move(file_renames);
-		_update_favorites_list_after_move(file_renames, folder_renames);
+		if (is_copied) {
+			_rescan();
+		}
+	} else {
+		// Check groups.
+		for (int i = 0; i < to_move.size(); i++) {
+			if (to_move[i].is_file && EditorFileSystem::get_singleton()->is_group_file(to_move[i].path)) {
+				EditorFileSystem::get_singleton()->move_group_file(to_move[i].path, new_paths[i]);
+			}
+		}
 
-		EditorNode::get_singleton()->set_current_tab(current_tab);
+		bool is_moved = false;
+		HashMap<String, String> file_renames;
+		HashMap<String, String> folder_renames;
 
-		print_verbose("FileSystem: calling rescan.");
-		_rescan();
+		for (int i = 0; i < to_move.size(); i++) {
+			String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
+			const String &new_path = new_paths[i];
+			if (old_path != new_path) {
+				_try_move_item(to_move[i], new_path, file_renames, folder_renames);
+				is_moved = true;
+			}
+		}
 
-		print_verbose("FileSystem: saving moved scenes.");
-		_save_scenes_after_move(file_renames);
+		if (is_moved) {
+			int current_tab = EditorNode::get_singleton()->get_current_tab();
+			_save_scenes_after_move(file_renames); // Save scenes before updating.
+			_update_dependencies_after_move(file_renames);
+			_update_resource_paths_after_move(file_renames);
+			_update_project_settings_after_move(file_renames);
+			_update_favorites_list_after_move(file_renames, folder_renames);
 
-		path = p_to_path;
-		current_path->set_text(path);
+			EditorNode::get_singleton()->set_current_tab(current_tab);
+
+			print_verbose("FileSystem: calling rescan.");
+			_rescan();
+
+			print_verbose("FileSystem: saving moved scenes.");
+			_save_scenes_after_move(file_renames);
+
+			path = p_to_path;
+			current_path->set_text(path);
+		}
 	}
 }
 
@@ -1950,7 +2009,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
 		} break;
 
 		case FILE_MOVE: {
-			// Move the files to a given location.
+			// Move or copy the files to a given location.
 			to_move.clear();
 			Vector<String> collapsed_paths = _remove_self_included_paths(p_selected);
 			for (int i = collapsed_paths.size() - 1; i >= 0; i--) {
@@ -1960,7 +2019,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
 				}
 			}
 			if (to_move.size() > 0) {
-				move_dialog->popup_centered_ratio();
+				move_dialog->popup_centered_ratio(0.4);
 			}
 		} break;
 
@@ -2425,28 +2484,7 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data,
 			}
 			if (!to_move.is_empty()) {
 				if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
-					for (int i = 0; i < to_move.size(); i++) {
-						String new_path;
-						String new_path_base;
-
-						if (to_move[i].is_file) {
-							new_path = to_dir.path_join(to_move[i].path.get_file());
-							new_path_base = new_path.get_basename() + " (%d)." + new_path.get_extension();
-						} else {
-							PackedStringArray path_split = to_move[i].path.split("/");
-							new_path = to_dir.path_join(path_split[path_split.size() - 2]);
-							new_path_base = new_path + " (%d)";
-						}
-
-						int exist_counter = 1;
-						Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
-						while (da->file_exists(new_path) || da->dir_exists(new_path)) {
-							exist_counter++;
-							new_path = vformat(new_path_base, exist_counter);
-						}
-						_try_duplicate_item(to_move[i], new_path);
-					}
-					_rescan();
+					_move_operation_confirm(to_dir, true);
 				} else {
 					_move_operation_confirm(to_dir);
 				}
@@ -2637,7 +2675,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str
 	}
 
 	if (p_paths.size() > 1 || p_paths[0] != "res://") {
-		p_popup->add_icon_item(get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")), TTR("Move To..."), FILE_MOVE);
+		p_popup->add_icon_item(get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")), TTR("Move/Duplicate To..."), FILE_MOVE);
 		p_popup->add_icon_shortcut(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/delete"), FILE_REMOVE);
 	}
 
@@ -3278,9 +3316,8 @@ FileSystemDock::FileSystemDock() {
 	add_child(remove_dialog);
 
 	move_dialog = memnew(EditorDirDialog);
-	move_dialog->set_ok_button_text(TTR("Move"));
 	add_child(move_dialog);
-	move_dialog->connect("dir_selected", callable_mp(this, &FileSystemDock::_move_operation_confirm).bind(false));
+	move_dialog->connect("dir_selected", callable_mp(this, &FileSystemDock::_move_dialog_confirm));
 
 	rename_dialog = memnew(ConfirmationDialog);
 	VBoxContainer *rename_dialog_vb = memnew(VBoxContainer);
@@ -3294,9 +3331,10 @@ FileSystemDock::FileSystemDock() {
 	rename_dialog->connect("confirmed", callable_mp(this, &FileSystemDock::_rename_operation_confirm));
 
 	overwrite_dialog = memnew(ConfirmationDialog);
-	overwrite_dialog->set_ok_button_text(TTR("Overwrite"));
 	add_child(overwrite_dialog);
-	overwrite_dialog->connect("confirmed", callable_mp(this, &FileSystemDock::_move_with_overwrite));
+	overwrite_dialog->set_ok_button_text(TTR("Overwrite"));
+	overwrite_dialog->add_button(TTR("Keep Both"), true)->connect("pressed", callable_mp(this, &FileSystemDock::_overwrite_dialog_action).bind(false));
+	overwrite_dialog->connect("confirmed", callable_mp(this, &FileSystemDock::_overwrite_dialog_action).bind(true));
 
 	duplicate_dialog = memnew(ConfirmationDialog);
 	VBoxContainer *duplicate_dialog_vb = memnew(VBoxContainer);

+ 11 - 2
editor/filesystem_dock.h

@@ -104,6 +104,12 @@ private:
 		FILE_NEW_SCENE,
 	};
 
+	enum Overwrite {
+		OVERWRITE_UNDECIDED,
+		OVERWRITE_REPLACE,
+		OVERWRITE_RENAME,
+	};
+
 	FileSortOption file_sort = FILE_SORT_NAME;
 
 	VBoxContainer *scanning_vb = nullptr;
@@ -173,6 +179,7 @@ private:
 	FileOrFolder to_duplicate;
 	Vector<FileOrFolder> to_move;
 	String to_move_path;
+	bool to_move_or_copy = false;
 
 	Vector<String> history;
 	int history_pos;
@@ -227,6 +234,7 @@ private:
 	void _save_scenes_after_move(const HashMap<String, String> &p_renames) const;
 	void _update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const;
 	void _update_project_settings_after_move(const HashMap<String, String> &p_renames) const;
+	String _get_unique_name(const FileOrFolder &p_entry, const String &p_at_path);
 
 	void _resource_removed(const Ref<Resource> &p_resource);
 	void _file_removed(String p_file);
@@ -237,9 +245,10 @@ private:
 	void _make_scene_confirm();
 	void _rename_operation_confirm();
 	void _duplicate_operation_confirm();
-	void _move_with_overwrite();
+	void _overwrite_dialog_action(bool p_overwrite);
 	Vector<String> _check_existing();
-	void _move_operation_confirm(const String &p_to_path, bool p_overwrite = false);
+	void _move_dialog_confirm(const String &p_path);
+	void _move_operation_confirm(const String &p_to_path, bool p_copy = false, Overwrite p_overwrite = OVERWRITE_UNDECIDED);
 
 	void _tree_rmb_option(int p_option);
 	void _file_list_rmb_option(int p_option);