Skip to content

Commit

Permalink
Merge pull request #75330 from KoBeWi/make_haste_with_copy_paste
Browse files Browse the repository at this point in the history
Improve file move and copy operations
  • Loading branch information
akien-mga committed Apr 3, 2023
2 parents b237df8 + 4941d5f commit 41ed64a
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 74 deletions.
35 changes: 27 additions & 8 deletions editor/editor_dir_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand All @@ -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: {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
}
Expand All @@ -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));

Expand All @@ -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);
Expand All @@ -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"));
}
12 changes: 9 additions & 3 deletions editor/editor_dir_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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());
Expand All @@ -66,6 +70,8 @@ class EditorDirDialog : public ConfirmationDialog {

public:
void reload(const String &p_path = "");
bool is_copy_pressed() const;

EditorDirDialog();
};

Expand Down
160 changes: 99 additions & 61 deletions editor/filesystem_dock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand All @@ -1681,58 +1705,93 @@ 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();
return;
}
}

// 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);
}
}
}

Expand Down Expand Up @@ -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--) {
Expand All @@ -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;

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
Loading

0 comments on commit 41ed64a

Please sign in to comment.