Improve file move and copy operations

This commit is contained in:
kobewi 2023-03-25 20:58:37 +01:00
parent 5922b2149e
commit 4941d5f534
4 changed files with 146 additions and 74 deletions

View File

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

View File

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

View File

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

View File

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