From 9af58c113390b886c3337db62dd05cfb8a4e5ac7 Mon Sep 17 00:00:00 2001 From: kobewi Date: Wed, 1 Oct 2025 14:49:52 +0200 Subject: [PATCH] Unify FileDialog context menus --- doc/classes/FileDialog.xml | 9 ++- scene/gui/file_dialog.cpp | 113 +++++++++++++++++++++++++++---------- scene/gui/file_dialog.h | 9 +++ 3 files changed, 101 insertions(+), 30 deletions(-) diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml index 697f23c36e5..b1cefac7e2d 100644 --- a/doc/classes/FileDialog.xml +++ b/doc/classes/FileDialog.xml @@ -207,6 +207,9 @@ The currently selected file path of the file dialog. + + If [code]true[/code], the context menu will show the "Delete" option, which allows moving files and folders to trash. + Display mode of the dialog's file list. @@ -232,7 +235,7 @@ [b]Note:[/b] Embedded file dialog and Windows file dialog support only file extensions, while Android, Linux, and macOS file dialogs also support MIME types. - If [code]true[/code], shows the button for creating new directories (when using [constant FILE_MODE_OPEN_DIR], [constant FILE_MODE_OPEN_ANY], or [constant FILE_MODE_SAVE_FILE]). + If [code]true[/code], shows the button for creating new directories (when using [constant FILE_MODE_OPEN_DIR], [constant FILE_MODE_OPEN_ANY], or [constant FILE_MODE_SAVE_FILE]), and the context menu will have the "New Folder..." option. If [code]true[/code], shows the toggle hidden files button. @@ -359,6 +362,10 @@ If enabled, the [FileDialog] will warn the user before overwriting files in save mode. Equivalent to [member overwrite_warning_enabled]. + + If enabled, the context menu will show the "Delete" option, which allows moving files and folders to trash. + Equivalent to [member deleting_enabled]. + diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 5fb8a336db7..7ad21a67c85 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -653,6 +653,11 @@ int FileDialog::_get_selected_file_idx() { return selected.is_empty() ? -1 : selected[0]; } +String FileDialog::_get_item_path(int p_idx) const { + const Dictionary meta = file_list->get_item_metadata(p_idx); + return ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir().path_join(meta["name"])); +} + void FileDialog::_file_list_multi_selected(int p_item, bool p_selected) { if (p_selected) { _file_list_selected(p_item); @@ -715,13 +720,32 @@ void FileDialog::update_file_name() { } void FileDialog::_item_menu_id_pressed(int p_option) { + int selected = _get_selected_file_idx(); switch (p_option) { + case ITEM_MENU_COPY_PATH: { + if (selected > -1) { + DisplayServer::get_singleton()->clipboard_set(_get_item_path(selected)); + } + } break; + + case ITEM_MENU_DELETE: { + if (selected > -1) { + delete_dialog->popup_centered(Size2(250, 80)); + } + } break; + + case ITEM_MENU_REFRESH: { + invalidate(); + } break; + + case ITEM_MENU_NEW_FOLDER: { + _make_dir(); + } break; + case ITEM_MENU_SHOW_IN_EXPLORER: { String path; - int selected = _get_selected_file_idx(); if (selected > -1) { - Dictionary d = file_list->get_item_metadata(selected); - path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir().path_join(d["name"])); + path = _get_item_path(selected); } else { path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir()); } @@ -730,12 +754,11 @@ void FileDialog::_item_menu_id_pressed(int p_option) { } break; case ITEM_MENU_SHOW_BUNDLE_CONTENT: { - int selected = _get_selected_file_idx(); if (selected == -1) { return; } - Dictionary d = file_list->get_item_metadata(selected); - _change_dir(d["name"]); + Dictionary meta = file_list->get_item_metadata(selected); + _change_dir(meta["name"]); if (mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES || mode == FILE_MODE_OPEN_DIR || mode == FILE_MODE_OPEN_ANY) { filename_edit->set_text(""); } @@ -746,15 +769,7 @@ void FileDialog::_item_menu_id_pressed(int p_option) { void FileDialog::_empty_clicked(const Vector2 &p_pos, MouseButton p_button) { if (p_button == MouseButton::RIGHT) { - item_menu->clear(); -#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) - // Opening the system file manager is not supported on the Android and web editors. - item_menu->add_item(ETR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); - - item_menu->set_position(file_list->get_screen_position() + p_pos); - item_menu->reset_size(); - item_menu->popup(); -#endif + _popup_menu(p_pos, -1); } else if (p_button == MouseButton::LEFT) { deselect_all(); } @@ -762,22 +777,49 @@ void FileDialog::_empty_clicked(const Vector2 &p_pos, MouseButton p_button) { void FileDialog::_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_button) { if (p_button == MouseButton::RIGHT) { - item_menu->clear(); -#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) - // Opening the system file manager is not supported on the Android and web editors. - Dictionary d = file_list->get_item_metadata(p_item); - if (d["bundle"]) { - item_menu->add_item(ETR("Show Package Contents"), ITEM_MENU_SHOW_BUNDLE_CONTENT); - } - item_menu->add_item(ETR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); - - item_menu->set_position(file_list->get_screen_position() + p_pos); - item_menu->reset_size(); - item_menu->popup(); -#endif + _popup_menu(p_pos, p_item); } } +void FileDialog::_popup_menu(const Vector2 &p_pos, int p_for_item) { + item_menu->clear(); + + if (p_for_item > -1) { + item_menu->add_item(ETR("Copy Path"), ITEM_MENU_COPY_PATH); + if (customization_flags[CUSTOMIZATION_DELETE]) { + item_menu->add_item(ETR("Delete"), ITEM_MENU_DELETE); + } + } else { + if (can_create_folders) { + item_menu->add_item(ETR("New Folder..."), ITEM_MENU_NEW_FOLDER); + } + item_menu->add_item(ETR("Refresh"), ITEM_MENU_REFRESH); + } + +#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) + // Opening the system file manager is not supported on the Android and web editors. + item_menu->add_separator(); + + Dictionary meta; + if (p_for_item > -1) { + meta = file_list->get_item_metadata(p_for_item); + } + + item_menu->add_item((p_for_item == -1 || meta["dir"]) ? ETR("Open in File Manager") : ETR("Show in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); + if (meta["bundle"]) { + item_menu->add_item(ETR("Show Package Contents"), ITEM_MENU_SHOW_BUNDLE_CONTENT); + } +#endif + + if (item_menu->get_item_count() == 0) { + return; + } + + item_menu->set_position(file_list->get_screen_position() + p_pos); + item_menu->reset_size(); + item_menu->popup(); +} + void FileDialog::update_file_list() { file_list->clear(); @@ -1063,6 +1105,11 @@ void FileDialog::_file_list_select_first() { } } +void FileDialog::_delete_confirm() { + OS::get_singleton()->move_to_trash(_get_item_path(_get_selected_file_idx())); + invalidate(); +} + void FileDialog::_filename_filter_selected() { int selected = _get_selected_file_idx(); if (selected > -1) { @@ -1518,7 +1565,8 @@ void FileDialog::_setup_button(Button *p_button, const Ref &p_icon) { } void FileDialog::_update_make_dir_visible() { - make_dir_container->set_visible(customization_flags[CUSTOMIZATION_CREATE_FOLDER] && mode != FILE_MODE_OPEN_FILE && mode != FILE_MODE_OPEN_FILES); + can_create_folders = customization_flags[CUSTOMIZATION_CREATE_FOLDER] && mode != FILE_MODE_OPEN_FILE && mode != FILE_MODE_OPEN_FILES; + make_dir_container->set_visible(can_create_folders); } FileDialog::Access FileDialog::get_access() const { @@ -2058,6 +2106,7 @@ void FileDialog::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "recent_list_enabled"), "set_customization_flag_enabled", "is_customization_flag_enabled", CUSTOMIZATION_RECENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "layout_toggle_enabled"), "set_customization_flag_enabled", "is_customization_flag_enabled", CUSTOMIZATION_LAYOUT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "overwrite_warning_enabled"), "set_customization_flag_enabled", "is_customization_flag_enabled", CUSTOMIZATION_OVERWRITE_WARNING); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "deleting_enabled"), "set_customization_flag_enabled", "is_customization_flag_enabled", CUSTOMIZATION_DELETE); ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir", PROPERTY_HINT_DIR, "", PROPERTY_USAGE_NONE), "set_current_dir", "get_current_dir"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file", PROPERTY_HINT_FILE_PATH, "*", PROPERTY_USAGE_NONE), "set_current_file", "get_current_file"); @@ -2089,6 +2138,7 @@ void FileDialog::_bind_methods() { BIND_ENUM_CONSTANT(CUSTOMIZATION_RECENT); BIND_ENUM_CONSTANT(CUSTOMIZATION_LAYOUT); BIND_ENUM_CONSTANT(CUSTOMIZATION_OVERWRITE_WARNING); + BIND_ENUM_CONSTANT(CUSTOMIZATION_DELETE); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, FileDialog, thumbnail_size); BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, parent_folder); @@ -2495,6 +2545,11 @@ FileDialog::FileDialog() { add_child(confirm_save, false, INTERNAL_MODE_FRONT); confirm_save->connect(SceneStringName(confirmed), callable_mp(this, &FileDialog::_save_confirm_pressed)); + delete_dialog = memnew(ConfirmationDialog); + delete_dialog->set_text(ETR("Delete the selected file?\nDepending on your filesystem configuration, the files will either be moved to the system trash or deleted permanently.")); + add_child(delete_dialog, false, INTERNAL_MODE_FRONT); + delete_dialog->connect(SceneStringName(confirmed), callable_mp(this, &FileDialog::_delete_confirm)); + make_dir_dialog = memnew(ConfirmationDialog); make_dir_dialog->set_title(ETR("Create Folder")); add_child(make_dir_dialog, false, INTERNAL_MODE_FRONT); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 1a28af42a66..dcf444189f8 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -131,6 +131,9 @@ public: enum ItemMenu { ITEM_MENU_COPY_PATH, + ITEM_MENU_DELETE, + ITEM_MENU_REFRESH, + ITEM_MENU_NEW_FOLDER, ITEM_MENU_SHOW_IN_EXPLORER, ITEM_MENU_SHOW_BUNDLE_CONTENT, }; @@ -144,6 +147,7 @@ public: CUSTOMIZATION_RECENT, CUSTOMIZATION_LAYOUT, CUSTOMIZATION_OVERWRITE_WARNING, + CUSTOMIZATION_DELETE, CUSTOMIZATION_MAX }; @@ -163,6 +167,7 @@ private: static inline DisplayMode default_display_mode = DISPLAY_THUMBNAILS; bool show_hidden_files = false; bool use_native_dialog = false; + bool can_create_folders = true; bool customization_flags[CUSTOMIZATION_MAX]; // Initialized to true in the constructor. inline static LocalVector global_favorites; @@ -241,6 +246,7 @@ private: FlowContainer *flow_checkbox_options = nullptr; GridContainer *grid_select_options = nullptr; + ConfirmationDialog *delete_dialog = nullptr; ConfirmationDialog *make_dir_dialog = nullptr; LineEdit *new_dir_name = nullptr; AcceptDialog *mkdirerr = nullptr; @@ -289,10 +295,12 @@ private: void _item_menu_id_pressed(int p_option); void _empty_clicked(const Vector2 &p_pos, MouseButton p_button); void _item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_button); + void _popup_menu(const Vector2 &p_pos, int p_for_item); void _focus_file_text(); int _get_selected_file_idx(); + String _get_item_path(int p_idx) const; void _file_list_multi_selected(int p_item, bool p_selected); void _file_list_selected(int p_item); void _file_list_item_activated(int p_item); @@ -307,6 +315,7 @@ private: void _filename_filter_changed(); void _filename_filter_selected(); void _file_list_select_first(); + void _delete_confirm(); void _make_dir(); void _make_dir_confirm(); void _go_up();