Unify FileDialog context menus

This commit is contained in:
kobewi
2025-10-01 14:49:52 +02:00
parent 7fbc3a5307
commit 9af58c1133
3 changed files with 101 additions and 30 deletions

View File

@ -207,6 +207,9 @@
<member name="current_path" type="String" setter="set_current_path" getter="get_current_path"> <member name="current_path" type="String" setter="set_current_path" getter="get_current_path">
The currently selected file path of the file dialog. The currently selected file path of the file dialog.
</member> </member>
<member name="deleting_enabled" type="bool" setter="set_customization_flag_enabled" getter="is_customization_flag_enabled" default="true">
If [code]true[/code], the context menu will show the "Delete" option, which allows moving files and folders to trash.
</member>
<member name="dialog_hide_on_ok" type="bool" setter="set_hide_on_ok" getter="get_hide_on_ok" overrides="AcceptDialog" default="false" /> <member name="dialog_hide_on_ok" type="bool" setter="set_hide_on_ok" getter="get_hide_on_ok" overrides="AcceptDialog" default="false" />
<member name="display_mode" type="int" setter="set_display_mode" getter="get_display_mode" enum="FileDialog.DisplayMode" default="0"> <member name="display_mode" type="int" setter="set_display_mode" getter="get_display_mode" enum="FileDialog.DisplayMode" default="0">
Display mode of the dialog's file list. 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. [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.
</member> </member>
<member name="folder_creation_enabled" type="bool" setter="set_customization_flag_enabled" getter="is_customization_flag_enabled" default="true"> <member name="folder_creation_enabled" type="bool" setter="set_customization_flag_enabled" getter="is_customization_flag_enabled" default="true">
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.
</member> </member>
<member name="hidden_files_toggle_enabled" type="bool" setter="set_customization_flag_enabled" getter="is_customization_flag_enabled" default="true"> <member name="hidden_files_toggle_enabled" type="bool" setter="set_customization_flag_enabled" getter="is_customization_flag_enabled" default="true">
If [code]true[/code], shows the toggle hidden files button. 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. If enabled, the [FileDialog] will warn the user before overwriting files in save mode.
Equivalent to [member overwrite_warning_enabled]. Equivalent to [member overwrite_warning_enabled].
</constant> </constant>
<constant name="CUSTOMIZATION_DELETE" value="8" enum="Customization">
If enabled, the context menu will show the "Delete" option, which allows moving files and folders to trash.
Equivalent to [member deleting_enabled].
</constant>
</constants> </constants>
<theme_items> <theme_items>
<theme_item name="file_disabled_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.25)"> <theme_item name="file_disabled_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.25)">

View File

@ -653,6 +653,11 @@ int FileDialog::_get_selected_file_idx() {
return selected.is_empty() ? -1 : selected[0]; 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) { void FileDialog::_file_list_multi_selected(int p_item, bool p_selected) {
if (p_selected) { if (p_selected) {
_file_list_selected(p_item); _file_list_selected(p_item);
@ -715,13 +720,32 @@ void FileDialog::update_file_name() {
} }
void FileDialog::_item_menu_id_pressed(int p_option) { void FileDialog::_item_menu_id_pressed(int p_option) {
int selected = _get_selected_file_idx();
switch (p_option) { 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: { case ITEM_MENU_SHOW_IN_EXPLORER: {
String path; String path;
int selected = _get_selected_file_idx();
if (selected > -1) { if (selected > -1) {
Dictionary d = file_list->get_item_metadata(selected); path = _get_item_path(selected);
path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir().path_join(d["name"]));
} else { } else {
path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir()); 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; } break;
case ITEM_MENU_SHOW_BUNDLE_CONTENT: { case ITEM_MENU_SHOW_BUNDLE_CONTENT: {
int selected = _get_selected_file_idx();
if (selected == -1) { if (selected == -1) {
return; return;
} }
Dictionary d = file_list->get_item_metadata(selected); Dictionary meta = file_list->get_item_metadata(selected);
_change_dir(d["name"]); _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) { 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(""); 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) { void FileDialog::_empty_clicked(const Vector2 &p_pos, MouseButton p_button) {
if (p_button == MouseButton::RIGHT) { if (p_button == MouseButton::RIGHT) {
item_menu->clear(); _popup_menu(p_pos, -1);
#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
} else if (p_button == MouseButton::LEFT) { } else if (p_button == MouseButton::LEFT) {
deselect_all(); 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) { void FileDialog::_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_button) {
if (p_button == MouseButton::RIGHT) { if (p_button == MouseButton::RIGHT) {
item_menu->clear(); _popup_menu(p_pos, p_item);
#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
} }
} }
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() { void FileDialog::update_file_list() {
file_list->clear(); 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() { void FileDialog::_filename_filter_selected() {
int selected = _get_selected_file_idx(); int selected = _get_selected_file_idx();
if (selected > -1) { if (selected > -1) {
@ -1518,7 +1565,8 @@ void FileDialog::_setup_button(Button *p_button, const Ref<Texture2D> &p_icon) {
} }
void FileDialog::_update_make_dir_visible() { 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 { 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, "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, "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, "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_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"); 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_RECENT);
BIND_ENUM_CONSTANT(CUSTOMIZATION_LAYOUT); BIND_ENUM_CONSTANT(CUSTOMIZATION_LAYOUT);
BIND_ENUM_CONSTANT(CUSTOMIZATION_OVERWRITE_WARNING); 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_CONSTANT, FileDialog, thumbnail_size);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, parent_folder); BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, parent_folder);
@ -2495,6 +2545,11 @@ FileDialog::FileDialog() {
add_child(confirm_save, false, INTERNAL_MODE_FRONT); add_child(confirm_save, false, INTERNAL_MODE_FRONT);
confirm_save->connect(SceneStringName(confirmed), callable_mp(this, &FileDialog::_save_confirm_pressed)); 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 = memnew(ConfirmationDialog);
make_dir_dialog->set_title(ETR("Create Folder")); make_dir_dialog->set_title(ETR("Create Folder"));
add_child(make_dir_dialog, false, INTERNAL_MODE_FRONT); add_child(make_dir_dialog, false, INTERNAL_MODE_FRONT);

View File

@ -131,6 +131,9 @@ public:
enum ItemMenu { enum ItemMenu {
ITEM_MENU_COPY_PATH, ITEM_MENU_COPY_PATH,
ITEM_MENU_DELETE,
ITEM_MENU_REFRESH,
ITEM_MENU_NEW_FOLDER,
ITEM_MENU_SHOW_IN_EXPLORER, ITEM_MENU_SHOW_IN_EXPLORER,
ITEM_MENU_SHOW_BUNDLE_CONTENT, ITEM_MENU_SHOW_BUNDLE_CONTENT,
}; };
@ -144,6 +147,7 @@ public:
CUSTOMIZATION_RECENT, CUSTOMIZATION_RECENT,
CUSTOMIZATION_LAYOUT, CUSTOMIZATION_LAYOUT,
CUSTOMIZATION_OVERWRITE_WARNING, CUSTOMIZATION_OVERWRITE_WARNING,
CUSTOMIZATION_DELETE,
CUSTOMIZATION_MAX CUSTOMIZATION_MAX
}; };
@ -163,6 +167,7 @@ private:
static inline DisplayMode default_display_mode = DISPLAY_THUMBNAILS; static inline DisplayMode default_display_mode = DISPLAY_THUMBNAILS;
bool show_hidden_files = false; bool show_hidden_files = false;
bool use_native_dialog = false; bool use_native_dialog = false;
bool can_create_folders = true;
bool customization_flags[CUSTOMIZATION_MAX]; // Initialized to true in the constructor. bool customization_flags[CUSTOMIZATION_MAX]; // Initialized to true in the constructor.
inline static LocalVector<String> global_favorites; inline static LocalVector<String> global_favorites;
@ -241,6 +246,7 @@ private:
FlowContainer *flow_checkbox_options = nullptr; FlowContainer *flow_checkbox_options = nullptr;
GridContainer *grid_select_options = nullptr; GridContainer *grid_select_options = nullptr;
ConfirmationDialog *delete_dialog = nullptr;
ConfirmationDialog *make_dir_dialog = nullptr; ConfirmationDialog *make_dir_dialog = nullptr;
LineEdit *new_dir_name = nullptr; LineEdit *new_dir_name = nullptr;
AcceptDialog *mkdirerr = nullptr; AcceptDialog *mkdirerr = nullptr;
@ -289,10 +295,12 @@ private:
void _item_menu_id_pressed(int p_option); void _item_menu_id_pressed(int p_option);
void _empty_clicked(const Vector2 &p_pos, MouseButton p_button); void _empty_clicked(const Vector2 &p_pos, MouseButton p_button);
void _item_clicked(int p_item, 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(); void _focus_file_text();
int _get_selected_file_idx(); 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_multi_selected(int p_item, bool p_selected);
void _file_list_selected(int p_item); void _file_list_selected(int p_item);
void _file_list_item_activated(int p_item); void _file_list_item_activated(int p_item);
@ -307,6 +315,7 @@ private:
void _filename_filter_changed(); void _filename_filter_changed();
void _filename_filter_selected(); void _filename_filter_selected();
void _file_list_select_first(); void _file_list_select_first();
void _delete_confirm();
void _make_dir(); void _make_dir();
void _make_dir_confirm(); void _make_dir_confirm();
void _go_up(); void _go_up();