From a42ead59da853cb4afb1dbfd77bdb16cdd90d266 Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Sun, 24 Dec 2023 13:59:32 +0800 Subject: [PATCH] Improve EditorDirDialog * Automatically selects the newly created directory * Automatically selects "res://" when nothing is selected * Fixes an error when overwrite/replace dialog appears * Changes "copy checkbox + action button" to "copy button + move button" * Double clicking a directory (un)collapses it instead of copy/move * Uses DirectoryCreateDialog for "Create Folder" --- editor/directory_create_dialog.cpp | 4 +- editor/filesystem_dock.cpp | 10 +- editor/filesystem_dock.h | 15 +-- editor/gui/editor_dir_dialog.cpp | 152 +++++++++++++---------------- editor/gui/editor_dir_dialog.h | 15 ++- 5 files changed, 91 insertions(+), 105 deletions(-) diff --git a/editor/directory_create_dialog.cpp b/editor/directory_create_dialog.cpp index 0efd11a6c121..aacd718fdec7 100644 --- a/editor/directory_create_dialog.cpp +++ b/editor/directory_create_dialog.cpp @@ -110,7 +110,7 @@ void DirectoryCreateDialog::ok_pressed() { err = da->make_dir_recursive(path); if (err == OK) { - emit_signal(SNAME("dir_created")); + emit_signal(SNAME("dir_created"), base_dir.path_join(path)); } else { EditorNode::get_singleton()->show_warning(TTR("Could not create folder.")); } @@ -131,7 +131,7 @@ void DirectoryCreateDialog::config(const String &p_base_dir) { } void DirectoryCreateDialog::_bind_methods() { - ADD_SIGNAL(MethodInfo("dir_created")); + ADD_SIGNAL(MethodInfo("dir_created", PropertyInfo(Variant::STRING, "path"))); } DirectoryCreateDialog::DirectoryCreateDialog() { diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index e5d03942e381..54c15069dc31 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1883,10 +1883,6 @@ Vector FileSystemDock::_check_existing() { return conflicting_items; } -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; @@ -2363,6 +2359,7 @@ void FileSystemDock::_file_option(int p_option, const Vector &p_selected } } if (to_move.size() > 0) { + move_dialog->config(p_selected); move_dialog->popup_centered_ratio(0.4); } } break; @@ -3847,7 +3844,8 @@ FileSystemDock::FileSystemDock() { move_dialog = memnew(EditorDirDialog); add_child(move_dialog); - move_dialog->connect("dir_selected", callable_mp(this, &FileSystemDock::_move_dialog_confirm)); + move_dialog->connect("move_pressed", callable_mp(this, &FileSystemDock::_move_operation_confirm).bind(false, OVERWRITE_UNDECIDED)); + move_dialog->connect("copy_pressed", callable_mp(this, &FileSystemDock::_move_operation_confirm).bind(true, OVERWRITE_UNDECIDED)); overwrite_dialog = memnew(ConfirmationDialog); add_child(overwrite_dialog); @@ -3884,7 +3882,7 @@ FileSystemDock::FileSystemDock() { make_dir_dialog = memnew(DirectoryCreateDialog); add_child(make_dir_dialog); - make_dir_dialog->connect("dir_created", callable_mp(this, &FileSystemDock::_rescan)); + make_dir_dialog->connect("dir_created", callable_mp(this, &FileSystemDock::_rescan).unbind(1)); make_scene_dialog = memnew(SceneCreateDialog); add_child(make_scene_dialog); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index d7203c15bd57..6bc7d0513072 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -102,6 +102,12 @@ class FileSystemDock : public VBoxContainer { FILE_SORT_MAX, }; + enum Overwrite { + OVERWRITE_UNDECIDED, + OVERWRITE_REPLACE, + OVERWRITE_RENAME, + }; + private: enum FileMenu { FILE_OPEN, @@ -133,12 +139,6 @@ class FileSystemDock : public VBoxContainer { FILE_NEW_SCENE, }; - enum Overwrite { - OVERWRITE_UNDECIDED, - OVERWRITE_REPLACE, - OVERWRITE_RENAME, - }; - HashMap folder_colors; Dictionary assigned_folder_colors; @@ -290,7 +290,6 @@ class FileSystemDock : public VBoxContainer { void _duplicate_operation_confirm(); void _overwrite_dialog_action(bool p_overwrite); Vector _check_existing(); - 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); @@ -410,4 +409,6 @@ class FileSystemDock : public VBoxContainer { ~FileSystemDock(); }; +VARIANT_ENUM_CAST(FileSystemDock::Overwrite); + #endif // FILESYSTEM_DOCK_H diff --git a/editor/gui/editor_dir_dialog.cpp b/editor/gui/editor_dir_dialog.cpp index fada434a03e4..8821a0eeaed4 100644 --- a/editor/gui/editor_dir_dialog.cpp +++ b/editor/gui/editor_dir_dialog.cpp @@ -30,22 +30,18 @@ #include "editor_dir_dialog.h" -#include "core/io/dir_access.h" -#include "core/os/keyboard.h" -#include "core/os/os.h" +#include "editor/directory_create_dialog.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/box_container.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) { updating = true; - String path = p_dir->get_path(); + const String path = p_dir->get_path(); - p_item->set_metadata(0, p_dir->get_path()); + p_item->set_metadata(0, path); p_item->set_icon(0, tree->get_editor_theme_icon(SNAME("Folder"))); p_item->set_icon_modulate(0, tree->get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog"))); @@ -59,6 +55,10 @@ void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p p_item->set_text(0, p_dir->get_name()); } + if (path == new_dir_path || !p_item->get_parent()) { + p_item->select(0); + } + updating = false; for (int i = 0; i < p_dir->get_subdir_count(); i++) { TreeItem *ti = tree->create_item(p_item); @@ -66,6 +66,22 @@ void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p } } +void EditorDirDialog::config(const Vector &p_paths) { + ERR_FAIL_COND(p_paths.is_empty()); + + if (p_paths.size() == 1) { + String path = p_paths[0]; + if (path.ends_with("/")) { + path = path.substr(0, path.length() - 1); + } + // TRANSLATORS: %s is the file name that will be moved or duplicated. + set_title(vformat(TTR("Move/Duplicate: %s"), path.get_file())); + } else { + // TRANSLATORS: %d is the number of files that will be moved or duplicated. + set_title(vformat(TTRN("Move/Duplicate %d Item", "Move/Duplicate %d Items", p_paths.size()), p_paths.size())); + } +} + void EditorDirDialog::reload(const String &p_path) { if (!is_visible()) { must_reload = true; @@ -76,13 +92,10 @@ void EditorDirDialog::reload(const String &p_path) { TreeItem *root = tree->create_item(); _update_dir(root, EditorFileSystem::get_singleton()->get_filesystem(), p_path); _item_collapsed(root); + new_dir_path.clear(); 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: { @@ -112,14 +125,6 @@ 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(p_item); @@ -135,100 +140,83 @@ void EditorDirDialog::_item_collapsed(Object *p_item) { } void EditorDirDialog::_item_activated() { - _ok_pressed(); // From AcceptDialog. + TreeItem *ti = tree->get_selected(); + ERR_FAIL_NULL(ti); + if (ti->get_child_count() > 0) { + ti->set_collapsed(!ti->is_collapsed()); + } } -void EditorDirDialog::ok_pressed() { +void EditorDirDialog::_copy_pressed() { TreeItem *ti = tree->get_selected(); - if (!ti) { - return; - } + ERR_FAIL_NULL(ti); - String dir = ti->get_metadata(0); - emit_signal(SNAME("dir_selected"), dir); hide(); + emit_signal(SNAME("copy_pressed"), ti->get_metadata(0)); } -void EditorDirDialog::_make_dir() { +void EditorDirDialog::ok_pressed() { TreeItem *ti = tree->get_selected(); - if (!ti) { - mkdirerr->set_text(TTR("Please select a base directory first.")); - mkdirerr->popup_centered(); - return; - } + ERR_FAIL_NULL(ti); - makedialog->popup_centered(Size2(250, 80)); - makedirname->grab_focus(); + hide(); + emit_signal(SNAME("move_pressed"), ti->get_metadata(0)); } -void EditorDirDialog::_make_dir_confirm() { +void EditorDirDialog::_make_dir() { TreeItem *ti = tree->get_selected(); - if (!ti) { - return; - } - - String dir = ti->get_metadata(0); - - Ref d = DirAccess::open(dir); - ERR_FAIL_COND_MSG(d.is_null(), "Cannot open directory '" + dir + "'."); - - const String stripped_dirname = makedirname->get_text().strip_edges(); + ERR_FAIL_NULL(ti); + makedialog->config(ti->get_metadata(0)); + makedialog->popup_centered(); +} - if (d->dir_exists(stripped_dirname)) { - mkdirerr->set_text(TTR("Could not create folder. File with that name already exists.")); - mkdirerr->popup_centered(); - return; +void EditorDirDialog::_make_dir_confirm(const String &p_path) { + // Multiple level of directories can be created at once. + String base_dir = p_path.get_base_dir(); + while (true) { + opened_paths.insert(base_dir + "/"); + if (base_dir == "res://") { + break; + } + base_dir = base_dir.get_base_dir(); } - Error err = d->make_dir(stripped_dirname); - if (err != OK) { - mkdirerr->popup_centered(Size2(250, 80) * EDSCALE); - } else { - opened_paths.insert(dir); - EditorFileSystem::get_singleton()->scan_changes(); // We created a dir, so rescan changes. - } - makedirname->set_text(""); // reset label + new_dir_path = p_path + "/"; + EditorFileSystem::get_singleton()->scan_changes(); // We created a dir, so rescan changes. } void EditorDirDialog::_bind_methods() { - ADD_SIGNAL(MethodInfo("dir_selected", PropertyInfo(Variant::STRING, "dir"))); + ADD_SIGNAL(MethodInfo("copy_pressed", PropertyInfo(Variant::STRING, "dir"))); + ADD_SIGNAL(MethodInfo("move_pressed", PropertyInfo(Variant::STRING, "dir"))); } EditorDirDialog::EditorDirDialog() { - set_title(TTR("Choose a Directory")); set_hide_on_ok(false); VBoxContainer *vb = memnew(VBoxContainer); add_child(vb); + HBoxContainer *hb = memnew(HBoxContainer); + vb->add_child(hb); + + hb->add_child(memnew(Label(TTR("Choose target directory:")))); + hb->add_spacer(); + + makedir = memnew(Button(TTR("Create Folder"))); + hb->add_child(makedir); + makedir->connect("pressed", callable_mp(this, &EditorDirDialog::_make_dir)); + 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)); + set_ok_button_text(TTR("Move")); - makedir = add_button(TTR("Create Folder"), DisplayServer::get_singleton()->get_swap_cancel_ok(), "makedir"); - makedir->connect("pressed", callable_mp(this, &EditorDirDialog::_make_dir)); + copy = add_button(TTR("Copy"), !DisplayServer::get_singleton()->get_swap_cancel_ok()); + copy->connect("pressed", callable_mp(this, &EditorDirDialog::_copy_pressed)); - makedialog = memnew(ConfirmationDialog); - makedialog->set_title(TTR("Create Folder")); + makedialog = memnew(DirectoryCreateDialog); add_child(makedialog); - - VBoxContainer *makevb = memnew(VBoxContainer); - makedialog->add_child(makevb); - - makedirname = memnew(LineEdit); - makevb->add_margin_child(TTR("Name:"), makedirname); - makedialog->register_text_enter(makedirname); - makedialog->connect("confirmed", callable_mp(this, &EditorDirDialog::_make_dir_confirm)); - - mkdirerr = memnew(AcceptDialog); - mkdirerr->set_text(TTR("Could not create folder.")); - add_child(mkdirerr); - - set_ok_button_text(TTR("Move")); + makedialog->connect("dir_created", callable_mp(this, &EditorDirDialog::_make_dir_confirm)); } diff --git a/editor/gui/editor_dir_dialog.h b/editor/gui/editor_dir_dialog.h index 9f2b48c164b7..40badec21210 100644 --- a/editor/gui/editor_dir_dialog.h +++ b/editor/gui/editor_dir_dialog.h @@ -33,7 +33,7 @@ #include "scene/gui/dialogs.h" -class CheckBox; +class DirectoryCreateDialog; class EditorFileSystemDirectory; class Tree; class TreeItem; @@ -41,25 +41,24 @@ class TreeItem; class EditorDirDialog : public ConfirmationDialog { GDCLASS(EditorDirDialog, ConfirmationDialog); - ConfirmationDialog *makedialog = nullptr; - LineEdit *makedirname = nullptr; - AcceptDialog *mkdirerr = nullptr; + DirectoryCreateDialog *makedialog = nullptr; Button *makedir = nullptr; + Button *copy = nullptr; HashSet opened_paths; + String new_dir_path; 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()); void _make_dir(); - void _make_dir_confirm(); + void _make_dir_confirm(const String &p_path); + void _copy_pressed(); void ok_pressed() override; bool must_reload = false; @@ -69,8 +68,8 @@ class EditorDirDialog : public ConfirmationDialog { static void _bind_methods(); public: + void config(const Vector &p_paths); void reload(const String &p_path = ""); - bool is_copy_pressed() const; EditorDirDialog(); };