Add context menu to editor file dialogs.

This commit is contained in:
Ray Koopa 2017-12-02 01:37:32 +01:00
parent 581ee23a15
commit 5fbd71af17
4 changed files with 187 additions and 17 deletions

View File

@ -35,6 +35,7 @@
#include "scene/gui/tab_container.h"
#include "scene/gui/tree.h"
class EditorFileDialog;
class EditorFileSystemDirectory;
class EditorNode;

View File

@ -28,12 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "editor_file_dialog.h"
#include "dependency_editor.h"
#include "editor_resource_preview.h"
#include "editor_scale.h"
#include "editor_settings.h"
#include "os/file_access.h"
#include "os/keyboard.h"
#include "os/os.h"
#include "print_string.h"
#include "scene/gui/center_container.h"
#include "scene/gui/label.h"
@ -159,6 +160,10 @@ void EditorFileDialog::_unhandled_input(const Ref<InputEvent> &p_event) {
_make_dir();
handled = true;
}
if (ED_IS_SHORTCUT("file_dialog/delete", p_event)) {
_delete_items();
handled = true;
}
if (ED_IS_SHORTCUT("file_dialog/focus_path", p_event)) {
dir->grab_focus();
handled = true;
@ -512,6 +517,106 @@ void EditorFileDialog::_item_dc_selected(int p_item) {
}
}
void EditorFileDialog::_item_list_item_rmb_selected(int p_item, const Vector2 &p_pos) {
// Right click on specific file(s) or folder(s).
item_menu->clear();
item_menu->set_size(Size2(1, 1));
// Allow specific actions only on one item.
bool single_item_selected = item_list->get_selected_items().size() == 1;
// Disallow deleting the .import folder, Godot kills a cat if you do and it is possibly a senseless novice action.
bool allow_delete = true;
for (int i = 0; i < item_list->get_item_count(); i++) {
if (!item_list->is_selected(i)) {
continue;
}
Dictionary item_meta = item_list->get_item_metadata(i);
if (item_meta["path"] == "res://.import") {
allow_delete = false;
break;
}
}
if (single_item_selected) {
item_menu->add_icon_item(get_icon("CopyNodePath", "EditorIcons"), TTR("Copy Path"), ITEM_MENU_COPY_PATH);
}
if (allow_delete) {
item_menu->add_icon_item(get_icon("Remove", "EditorIcons"), TTR("Delete"), ITEM_MENU_DELETE, KEY_DELETE);
}
if (single_item_selected) {
item_menu->add_separator();
item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Show In File Manager"), ITEM_MENU_SHOW_IN_EXPLORER);
}
if (item_menu->get_item_count() > 0) {
item_menu->set_position(item_list->get_global_position() + p_pos);
item_menu->popup();
}
}
void EditorFileDialog::_item_list_rmb_clicked(const Vector2 &p_pos) {
// Right click on folder background. Deselect all files so that actions are applied on the current folder.
for (int i = 0; i < item_list->get_item_count(); i++) {
item_list->unselect(i);
}
item_menu->clear();
item_menu->set_size(Size2(1, 1));
if (can_create_dir) {
item_menu->add_icon_item(get_icon("folder", "FileDialog"), TTR("New Folder.."), ITEM_MENU_NEW_FOLDER, KEY_MASK_CMD | KEY_N);
}
item_menu->add_icon_item(get_icon("Reload", "EditorIcons"), TTR("Refresh"), ITEM_MENU_REFRESH, KEY_F5);
item_menu->add_separator();
item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Show In File Manager"), ITEM_MENU_SHOW_IN_EXPLORER);
item_menu->set_position(item_list->get_global_position() + p_pos);
item_menu->popup();
}
void EditorFileDialog::_item_menu_id_pressed(int p_option) {
switch (p_option) {
case ITEM_MENU_COPY_PATH: {
Dictionary item_meta = item_list->get_item_metadata(item_list->get_current());
OS::get_singleton()->set_clipboard(item_meta["path"]);
} break;
case ITEM_MENU_DELETE: {
_delete_items();
} break;
case ITEM_MENU_REFRESH: {
invalidate();
} break;
case ITEM_MENU_NEW_FOLDER: {
_make_dir();
} break;
case ITEM_MENU_SHOW_IN_EXPLORER: {
String path;
int idx = item_list->get_current();
if (idx == -1 || item_list->get_selected_items().size() == 0) {
// Folder background was clicked. Open this folder.
path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir());
} else {
// Specific item was clicked. Open folders directly, or the folder containing a selected file.
Dictionary item_meta = item_list->get_item_metadata(idx);
path = ProjectSettings::get_singleton()->globalize_path(item_meta["path"]);
if (!item_meta["dir"]) {
path = path.get_base_dir();
}
}
OS::get_singleton()->shell_open(String("file://") + path);
} break;
}
}
bool EditorFileDialog::_is_open_should_be_disabled() {
if (mode == MODE_OPEN_ANY || mode == MODE_SAVE_FILE)
@ -617,7 +722,7 @@ void EditorFileDialog::update_file_list() {
Dictionary d;
d["name"] = dir_name;
d["path"] = String();
d["path"] = cdir.plus_file(dir_name);
d["dir"] = true;
item_list->set_item_metadata(item_list->get_item_count() - 1, d);
@ -657,8 +762,6 @@ void EditorFileDialog::update_file_list() {
}
}
String base_dir = dir_access->get_current_dir();
while (!files.empty()) {
bool match = patterns.empty();
@ -679,7 +782,7 @@ void EditorFileDialog::update_file_list() {
if (get_icon_func) {
Ref<Texture> icon = get_icon_func(base_dir.plus_file(files.front()->get()));
Ref<Texture> icon = get_icon_func(cdir.plus_file(files.front()->get()));
//ti->set_icon(0,icon);
if (display_mode == DISPLAY_THUMBNAILS) {
@ -698,12 +801,11 @@ void EditorFileDialog::update_file_list() {
Dictionary d;
d["name"] = files.front()->get();
d["dir"] = false;
String fullpath = base_dir.plus_file(files.front()->get());
String fullpath = cdir.plus_file(files.front()->get());
if (display_mode == DISPLAY_THUMBNAILS) {
EditorResourcePreview::get_singleton()->queue_resource_preview(fullpath, this, "_thumbnail_result", fullpath);
}
d["path"] = base_dir.plus_file(files.front()->get());
d["path"] = fullpath;
//ti->set_metadata(0,d);
item_list->set_item_metadata(item_list->get_item_count() - 1, d);
@ -723,7 +825,7 @@ void EditorFileDialog::update_file_list() {
fav_down->set_disabled(true);
get_ok()->set_disabled(_is_open_should_be_disabled());
for (int i = 0; i < favorites->get_item_count(); i++) {
if (favorites->get_item_metadata(i) == base_dir) {
if (favorites->get_item_metadata(i) == cdir) {
favorites->select(i);
favorite->set_pressed(true);
if (i > 0) {
@ -854,27 +956,27 @@ void EditorFileDialog::set_mode(Mode p_mode) {
case MODE_OPEN_FILE:
get_ok()->set_text(TTR("Open"));
set_title(TTR("Open a File"));
makedir->hide();
can_create_dir = false;
break;
case MODE_OPEN_FILES:
get_ok()->set_text(TTR("Open"));
set_title(TTR("Open File(s)"));
makedir->hide();
can_create_dir = false;
break;
case MODE_OPEN_DIR:
get_ok()->set_text(TTR("Open"));
set_title(TTR("Open a Directory"));
makedir->show();
can_create_dir = true;
break;
case MODE_OPEN_ANY:
get_ok()->set_text(TTR("Open"));
set_title(TTR("Open a File or Directory"));
makedir->show();
can_create_dir = true;
break;
case MODE_SAVE_FILE:
get_ok()->set_text(TTR("Save"));
set_title(TTR("Save a File"));
makedir->show();
can_create_dir = true;
break;
}
@ -883,6 +985,12 @@ void EditorFileDialog::set_mode(Mode p_mode) {
} else {
item_list->set_select_mode(ItemList::SELECT_SINGLE);
}
if (can_create_dir) {
makedir->show();
} else {
makedir->hide();
}
}
EditorFileDialog::Mode EditorFileDialog::get_mode() const {
@ -954,6 +1062,28 @@ void EditorFileDialog::_make_dir() {
makedirname->grab_focus();
}
void EditorFileDialog::_delete_items() {
// Collect the selected folders and files to delete and check them in the deletion dependency dialog.
Vector<String> folders;
Vector<String> files;
for (int i = 0; i < item_list->get_item_count(); i++) {
if (!item_list->is_selected(i)) {
continue;
}
Dictionary item_meta = item_list->get_item_metadata(i);
if (item_meta["dir"]) {
folders.push_back(item_meta["path"]);
} else {
files.push_back(item_meta["path"]);
}
}
if (folders.size() + files.size() > 0) {
remove_dialog->set_size(Size2(1, 1));
remove_dialog->show(folders, files);
}
}
void EditorFileDialog::_select_drive(int p_idx) {
String d = drives->get_item_text(p_idx);
@ -1181,6 +1311,9 @@ void EditorFileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("_item_selected"), &EditorFileDialog::_item_selected);
ClassDB::bind_method(D_METHOD("_items_clear_selection"), &EditorFileDialog::_items_clear_selection);
ClassDB::bind_method(D_METHOD("_item_list_item_rmb_selected"), &EditorFileDialog::_item_list_item_rmb_selected);
ClassDB::bind_method(D_METHOD("_item_list_rmb_clicked"), &EditorFileDialog::_item_list_rmb_clicked);
ClassDB::bind_method(D_METHOD("_item_menu_id_pressed"), &EditorFileDialog::_item_menu_id_pressed);
ClassDB::bind_method(D_METHOD("_item_db_selected"), &EditorFileDialog::_item_dc_selected);
ClassDB::bind_method(D_METHOD("_dir_entered"), &EditorFileDialog::_dir_entered);
ClassDB::bind_method(D_METHOD("_file_entered"), &EditorFileDialog::_file_entered);
@ -1317,6 +1450,7 @@ EditorFileDialog::EditorFileDialog() {
ED_SHORTCUT("file_dialog/toggle_favorite", TTR("Toggle Favorite"), KEY_MASK_ALT | KEY_F);
ED_SHORTCUT("file_dialog/toggle_mode", TTR("Toggle Mode"), KEY_MASK_ALT | KEY_V);
ED_SHORTCUT("file_dialog/create_folder", TTR("Create Folder"), KEY_MASK_CMD | KEY_N);
ED_SHORTCUT("file_dialog/delete", TTR("Delete"), KEY_DELETE);
ED_SHORTCUT("file_dialog/focus_path", TTR("Focus Path"), KEY_MASK_CMD | KEY_D);
ED_SHORTCUT("file_dialog/move_favorite_up", TTR("Move Favorite Up"), KEY_MASK_CMD | KEY_UP);
ED_SHORTCUT("file_dialog/move_favorite_down", TTR("Move Favorite Down"), KEY_MASK_CMD | KEY_DOWN);
@ -1423,10 +1557,21 @@ EditorFileDialog::EditorFileDialog() {
list_vb->add_child(memnew(Label(TTR("Directories & Files:"))));
preview_hb->add_child(list_vb);
// Item (files and folders) list with context menu
item_list = memnew(ItemList);
item_list->set_v_size_flags(SIZE_EXPAND_FILL);
item_list->connect("item_rmb_selected", this, "_item_list_item_rmb_selected");
item_list->connect("rmb_clicked", this, "_item_list_rmb_clicked");
item_list->set_allow_rmb_select(true);
list_vb->add_child(item_list);
item_menu = memnew(PopupMenu);
item_menu->connect("id_pressed", this, "_item_menu_id_pressed");
add_child(item_menu);
// Other stuff
preview_vb = memnew(VBoxContainer);
preview_hb->add_child(preview_vb);
CenterContainer *prev_cc = memnew(CenterContainer);
@ -1465,9 +1610,11 @@ EditorFileDialog::EditorFileDialog() {
confirm_save = memnew(ConfirmationDialog);
confirm_save->set_as_toplevel(true);
add_child(confirm_save);
confirm_save->connect("confirmed", this, "_save_confirm_pressed");
remove_dialog = memnew(DependencyRemoveDialog);
add_child(remove_dialog);
makedialog = memnew(ConfirmationDialog);
makedialog->set_title(TTR("Create Folder"));
VBoxContainer *makevb = memnew(VBoxContainer);

View File

@ -39,6 +39,9 @@
#include "scene/gui/split_container.h"
#include "scene/gui/texture_rect.h"
#include "scene/gui/tool_button.h"
class DependencyRemoveDialog;
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@ -75,6 +78,14 @@ public:
static RegisterFunc unregister_func;
private:
enum ItemMenu {
ITEM_MENU_COPY_PATH,
ITEM_MENU_DELETE,
ITEM_MENU_REFRESH,
ITEM_MENU_NEW_FOLDER,
ITEM_MENU_SHOW_IN_EXPLORER
};
ConfirmationDialog *makedialog;
LineEdit *makedirname;
@ -83,6 +94,7 @@ private:
//Button *action;
VBoxContainer *vbox;
Mode mode;
bool can_create_dir;
LineEdit *dir;
ToolButton *dir_prev;
@ -91,6 +103,7 @@ private:
OptionButton *drives;
ItemList *item_list;
PopupMenu *item_menu;
TextureRect *preview;
VBoxContainer *preview_vb;
HSplitContainer *list_hb;
@ -100,6 +113,7 @@ private:
OptionButton *filter;
DirAccess *dir_access;
ConfirmationDialog *confirm_save;
DependencyRemoveDialog *remove_dialog;
ToolButton *mode_thumbnails;
ToolButton *mode_list;
@ -146,6 +160,10 @@ private:
void _items_clear_selection();
void _item_dc_selected(int p_item);
void _item_list_item_rmb_selected(int p_item, const Vector2 &p_pos);
void _item_list_rmb_clicked(const Vector2 &p_pos);
void _item_menu_id_pressed(int p_option);
void _select_drive(int p_idx);
void _dir_entered(String p_dir);
void _file_entered(const String &p_file);
@ -156,6 +174,8 @@ private:
void _make_dir();
void _make_dir_confirm();
void _delete_items();
void _update_drives();
void _go_up();

View File

@ -1244,8 +1244,10 @@ void EditorFileSystem::update_file(const String &p_file) {
if (!FileAccess::exists(p_file)) {
//was removed
_delete_internal_files(p_file);
memdelete(fs->files[cpos]);
fs->files.remove(cpos);
if (cpos != -1) { // Might've never been part of the editor file system (*.* files deleted in Open dialog).
memdelete(fs->files[cpos]);
fs->files.remove(cpos);
}
call_deferred("emit_signal", "filesystem_changed"); //update later
return;
}