From c1cc178a82e00618e4ea15bf9e7b9ac58f9b7b50 Mon Sep 17 00:00:00 2001 From: kobewi Date: Tue, 2 Dec 2025 12:30:51 +0100 Subject: [PATCH] Make EditorFileDialog inherit FileDialog --- doc/classes/EditorFileDialog.xml | 217 +- doc/classes/FileDialog.xml | 7 + .../asset_library_editor_plugin.cpp | 1 + editor/docks/filesystem_dock.cpp | 7 +- editor/editor_node.cpp | 25 - editor/editor_node.h | 3 - editor/export/project_export.cpp | 32 - editor/export/project_export.h | 1 - editor/gui/editor_file_dialog.cpp | 2626 +---------------- editor/gui/editor_file_dialog.h | 344 +-- editor/project_manager/project_dialog.cpp | 2 - editor/project_manager/project_manager.cpp | 9 +- editor/script/script_create_dialog.cpp | 2 +- editor/settings/editor_settings.cpp | 31 +- editor/settings/editor_settings.h | 6 +- editor/settings/project_settings_editor.cpp | 2 +- editor/shader/shader_create_dialog.cpp | 3 +- editor/themes/theme_classic.cpp | 2 + editor/themes/theme_modern.cpp | 2 + .../4.5-stable.expected | 51 + .../editor/openxr_action_map_editor.cpp | 1 + scene/gui/file_dialog.cpp | 145 +- scene/gui/file_dialog.h | 19 +- 23 files changed, 253 insertions(+), 3285 deletions(-) diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index f74d966e4e7..713044e9102 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -1,36 +1,16 @@ - + A modified version of [FileDialog] used by the editor. - [EditorFileDialog] is an enhanced version of [FileDialog] available only to editor plugins. Additional features include list of favorited/recent files and the ability to see files as thumbnails grid instead of list. - Unlike [FileDialog], [EditorFileDialog] does not have a property for using native dialogs. Instead, native dialogs can be enabled globally via the [member EditorSettings.interface/editor/use_native_file_dialogs] editor setting. They are also enabled automatically when running in sandbox (e.g. on macOS). + [EditorFileDialog] is a [FileDialog] tweaked to work in the editor. It automatically handles favorite and recent directory lists, and synchronizes some properties with their corresponding editor settings. + [EditorFileDialog] will automatically show a native dialog based on the [member EditorSettings.interface/editor/use_native_file_dialogs] editor setting and ignores [member FileDialog.use_native_dialog]. [b]Note:[/b] [EditorFileDialog] is invisible by default. To make it visible, call one of the [code]popup_*[/code] methods from [Window] on the node, such as [method Window.popup_centered_clamped]. - - - - - - Adds a comma-separated file name [param filter] option to the [EditorFileDialog] with an optional [param description], which restricts what files can be picked. - A [param filter] should be of the form [code]"filename.extension"[/code], where filename and extension can be [code]*[/code] to match any string. Filters starting with [code].[/code] (i.e. empty filenames) are not allowed. - For example, a [param filter] of [code]"*.tscn, *.scn"[/code] and a [param description] of [code]"Scenes"[/code] results in filter text "Scenes (*.tscn, *.scn)". - - - - - - - - - Adds an additional [OptionButton] to the file dialog. If [param values] is empty, a [CheckBox] is added instead. - [param default_value_index] should be an index of the value in the [param values]. If [param values] is empty it should be either [code]1[/code] (checked), or [code]0[/code] (unchecked). - - @@ -39,199 +19,10 @@ This method is kept for compatibility and does nothing. As an alternative, you can display another dialog after showing the file dialog. - - - - Clear the filter for file names. - - - - - - Removes all filters except for "All Files (*.*)". - - - - - - Returns the value of the filter for file names. - - - - - - Returns the LineEdit for the selected file. - [b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member CanvasItem.visible] property. - - - - - - - Returns the default value index of the [OptionButton] or [CheckBox] with index [param option]. - - - - - - - Returns the name of the [OptionButton] or [CheckBox] with index [param option]. - - - - - - - Returns an array of values of the [OptionButton] with index [param option]. - - - - - - Returns a [Dictionary] with the selected values of the additional [OptionButton]s and/or [CheckBox]es. [Dictionary] keys are names and values are selected value indices. - - - - - - Returns the [VBoxContainer] used to display the file system. - [b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member CanvasItem.visible] property. - - - - - - Notify the [EditorFileDialog] that its view of the data is no longer accurate. Updates the view contents on next view update. - - - - - - Shows the [EditorFileDialog] at the default size and position for file dialogs in the editor, and selects the file name if there is a current file. - - - - - - - Sets the value of the filter for file names. - - - - - - - - Sets the default value index of the [OptionButton] or [CheckBox] with index [param option]. - - - - - - - - Sets the name of the [OptionButton] or [CheckBox] with index [param option]. - - - - - - - - Sets the option values of the [OptionButton] with index [param option]. - - - - The location from which the user may select a file, including [code]res://[/code], [code]user://[/code], and the local file system. - - - The currently occupied directory. - - - The currently selected file. - - - The file system path in the address bar. - - - + If [code]true[/code], the [EditorFileDialog] will not warn the user before overwriting files. - - The view format in which the [EditorFileDialog] displays resources to the user. - - - The dialog's open or save mode, which affects the selection behavior. - - - The available file type filters. For example, this shows only [code].png[/code] and [code].gd[/code] files: [code]set_filters(PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]))[/code]. Multiple file types can also be specified in a single filter. [code]"*.png, *.jpg, *.jpeg ; Supported Images"[/code] will show both PNG and JPEG files when selected. - - - The number of additional [OptionButton]s and [CheckBox]es in the dialog. - - - If [code]true[/code], hidden files and directories will be visible in the [EditorFileDialog]. This property is synchronized with [member EditorSettings.filesystem/file_dialog/show_hidden_files]. - - - - - - - Emitted when a directory is selected. - - - - - - Emitted when a file is selected. - - - - - - Emitted when the filter for file names changes. - - - - - - Emitted when multiple files are selected. - - - - - - The [EditorFileDialog] can select only one file. Accepting the window will open the file. - - - The [EditorFileDialog] can select multiple files. Accepting the window will open all files. - - - The [EditorFileDialog] can select only one directory. Accepting the window will open the directory. - - - The [EditorFileDialog] can select a file or directory. Accepting the window will open it. - - - The [EditorFileDialog] can select only one file. Accepting the window will save the file. - - - The [EditorFileDialog] can only view [code]res://[/code] directory contents. - - - The [EditorFileDialog] can only view [code]user://[/code] directory contents. - - - The [EditorFileDialog] can view the entire local file system. - - - The [EditorFileDialog] displays resources as thumbnails. - - - The [EditorFileDialog] displays resources as a list of filenames. - - diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml index ec6298ba4a0..3c59a7aa789 100644 --- a/doc/classes/FileDialog.xml +++ b/doc/classes/FileDialog.xml @@ -118,6 +118,12 @@ Returns [code]true[/code] if the provided [param flag] is enabled. + + + + Shows the [FileDialog] using the default size and position for file dialogs, and selects the file name if there is a current file. + + @@ -274,6 +280,7 @@ [b]Note:[/b] On Linux and macOS, sandboxed apps always use native dialogs to access the host file system. [b]Note:[/b] On macOS, sandboxed apps will save security-scoped bookmarks to retain access to the opened folders across multiple sessions. Use [method OS.get_granted_permissions] to get a list of saved bookmarks. [b]Note:[/b] Native dialogs are isolated from the base process, file dialog properties can't be modified once the dialog is shown. + [b]Note:[/b] This property is ignored in [EditorFileDialog]. diff --git a/editor/asset_library/asset_library_editor_plugin.cpp b/editor/asset_library/asset_library_editor_plugin.cpp index 42de15d8b6b..af6b959a4c6 100644 --- a/editor/asset_library/asset_library_editor_plugin.cpp +++ b/editor/asset_library/asset_library_editor_plugin.cpp @@ -30,6 +30,7 @@ #include "asset_library_editor_plugin.h" +#include "core/io/dir_access.h" #include "core/io/json.h" #include "core/io/stream_peer_tls.h" #include "core/os/keyboard.h" diff --git a/editor/docks/filesystem_dock.cpp b/editor/docks/filesystem_dock.cpp index 15c60c6bdeb..f3b9fa88568 100644 --- a/editor/docks/filesystem_dock.cpp +++ b/editor/docks/filesystem_dock.cpp @@ -2695,7 +2695,7 @@ int FileSystemDock::_get_menu_option_from_key(const Ref &p_key) { return FILE_MENU_OPEN_EXTERNAL; } else if (ED_IS_SHORTCUT("filesystem_dock/open_in_terminal", p_key)) { return FILE_MENU_OPEN_IN_TERMINAL; - } else if (ED_IS_SHORTCUT("file_dialog/focus_path", p_key)) { + } else if (ED_IS_SHORTCUT("filesystem_dock/focus_path", p_key)) { return EXTRA_FOCUS_PATH; } else if (ED_IS_SHORTCUT("editor/open_search", p_key)) { return EXTRA_FOCUS_FILTER; @@ -4234,6 +4234,11 @@ FileSystemDock::FileSystemDock() { ED_SHORTCUT("filesystem_dock/open_in_terminal", TTRC("Open in Terminal"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::T); #endif + ED_SHORTCUT("filesystem_dock/focus_path", TTRC("Focus Path"), KeyModifierMask::CMD_OR_CTRL | Key::L); + // Allow both Cmd + L and Cmd + Shift + G to match Safari's and Finder's shortcuts respectively. + ED_SHORTCUT_OVERRIDE_ARRAY("filesystem_dock/focus_path", "macos", + { int32_t(KeyModifierMask::META | Key::L), int32_t(KeyModifierMask::META | KeyModifierMask::SHIFT | Key::G) }); + // Properly translating color names would require a separate HashMap, so for simplicity they are provided as comments. folder_colors["red"] = Color(1.0, 0.271, 0.271); // TTR("Red") folder_colors["orange"] = Color(1.0, 0.561, 0.271); // TTR("Orange") diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index fa4f6bffa28..929dabd0882 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1029,8 +1029,6 @@ void EditorNode::_notification(int p_what) { if (EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog")) { FileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files")); FileDialog::set_default_display_mode(EDITOR_GET("filesystem/file_dialog/display_mode")); - EditorFileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files")); - EditorFileDialog::set_default_display_mode((EditorFileDialog::DisplayMode)EDITOR_GET("filesystem/file_dialog/display_mode").operator int()); } if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/tablet_driver")) { @@ -1251,10 +1249,6 @@ void EditorNode::_fs_changed() { E->invalidate(); } - for (EditorFileDialog *E : editor_file_dialogs) { - E->invalidate(); - } - _mark_unsaved_scenes(); // FIXME: Move this to a cleaner location, it's hacky to do this in _fs_changed. @@ -5967,14 +5961,6 @@ void EditorNode::_file_dialog_unregister(FileDialog *p_dialog) { singleton->file_dialogs.erase(p_dialog); } -void EditorNode::_editor_file_dialog_register(EditorFileDialog *p_dialog) { - singleton->editor_file_dialogs.insert(p_dialog); -} - -void EditorNode::_editor_file_dialog_unregister(EditorFileDialog *p_dialog) { - singleton->editor_file_dialogs.erase(p_dialog); -} - Vector EditorNode::_init_callbacks; void EditorNode::_begin_first_scan() { @@ -8038,8 +8024,6 @@ EditorNode::EditorNode() { FileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files")); FileDialog::set_default_display_mode(EDITOR_GET("filesystem/file_dialog/display_mode")); - EditorFileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files")); - EditorFileDialog::set_default_display_mode((EditorFileDialog::DisplayMode)EDITOR_GET("filesystem/file_dialog/display_mode").operator int()); int swap_cancel_ok = EDITOR_GET("interface/editor/accept_dialog_cancel_ok_buttons"); if (swap_cancel_ok != 0) { // 0 is auto, set in register_scene based on DisplayServer. @@ -8182,10 +8166,6 @@ EditorNode::EditorNode() { FileDialog::register_func = _file_dialog_register; FileDialog::unregister_func = _file_dialog_unregister; - EditorFileDialog::get_icon_func = _file_dialog_get_icon; - EditorFileDialog::register_func = _editor_file_dialog_register; - EditorFileDialog::unregister_func = _editor_file_dialog_unregister; - editor_export = memnew(EditorExport); add_child(editor_export); @@ -9263,12 +9243,7 @@ EditorNode::~EditorNode() { FileDialog::register_func = nullptr; FileDialog::unregister_func = nullptr; - EditorFileDialog::get_icon_func = nullptr; - EditorFileDialog::register_func = nullptr; - EditorFileDialog::unregister_func = nullptr; - file_dialogs.clear(); - editor_file_dialogs.clear(); singleton = nullptr; } diff --git a/editor/editor_node.h b/editor/editor_node.h index 16f95d604db..3db1ec62f77 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -481,7 +481,6 @@ private: HashSet textfile_extensions; HashSet other_file_extensions; HashSet file_dialogs; - HashSet editor_file_dialogs; Vector> resource_conversion_plugins; PrintHandlerList print_handler; @@ -521,8 +520,6 @@ private: static void _file_dialog_register(FileDialog *p_dialog); static void _file_dialog_unregister(FileDialog *p_dialog); - static void _editor_file_dialog_register(EditorFileDialog *p_dialog); - static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog); static void _file_access_close_error_notify(const String &p_str); static void _file_access_close_error_notify_impl(const String &p_str); diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 0ba920f0109..f0e1132d05a 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -1401,27 +1401,6 @@ void ProjectExportDialog::_open_export_template_manager() { EditorNode::get_singleton()->open_export_template_manager(); } -void ProjectExportDialog::_validate_export_path(const String &p_path) { - // Disable export via OK button or Enter key if LineEdit has an empty filename - bool invalid_path = (p_path.get_file().get_basename().is_empty()); - - // Check if state change before needlessly messing with signals - if (invalid_path && export_project->get_ok_button()->is_disabled()) { - return; - } - if (!invalid_path && !export_project->get_ok_button()->is_disabled()) { - return; - } - - if (invalid_path) { - export_project->get_ok_button()->set_disabled(true); - export_project->get_line_edit()->disconnect(SceneStringName(text_submitted), callable_mp(export_project, &EditorFileDialog::_file_submitted)); - } else { - export_project->get_ok_button()->set_disabled(false); - export_project->get_line_edit()->connect(SceneStringName(text_submitted), callable_mp(export_project, &EditorFileDialog::_file_submitted)); - } -} - void ProjectExportDialog::_export_project() { Ref current = get_current_preset(); ERR_FAIL_COND(current.is_null()); @@ -1446,16 +1425,6 @@ void ProjectExportDialog::_export_project() { export_project->set_current_file(default_filename); } } - - // Ensure that signal is connected if previous attempt left it disconnected - // with _validate_export_path. - // FIXME: This is a hack, we should instead change EditorFileDialog to allow - // disabling validation by the "text_submitted" signal. - if (!export_project->get_line_edit()->is_connected(SceneStringName(text_submitted), callable_mp(export_project, &EditorFileDialog::_file_submitted))) { - export_project->get_ok_button()->set_disabled(false); - export_project->get_line_edit()->connect(SceneStringName(text_submitted), callable_mp(export_project, &EditorFileDialog::_file_submitted)); - } - export_project->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); export_project->popup_file_dialog(); } @@ -2041,7 +2010,6 @@ ProjectExportDialog::ProjectExportDialog() { export_project->set_access(EditorFileDialog::ACCESS_FILESYSTEM); add_child(export_project); export_project->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_project_to_path)); - export_project->get_line_edit()->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_validate_export_path)); export_project->add_option(TTR("Export With Debug"), Vector(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true)); export_pck_zip->add_option(TTR("Export With Debug"), Vector(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true)); diff --git a/editor/export/project_export.h b/editor/export/project_export.h index f943102ebac..a0ee46c90d4 100644 --- a/editor/export/project_export.h +++ b/editor/export/project_export.h @@ -199,7 +199,6 @@ class ProjectExportDialog : public ConfirmationDialog { void _export_pck_zip(); void _export_pck_zip_selected(const String &p_path); - void _validate_export_path(const String &p_path); void _export_project(); void _export_project_to_path(const String &p_path); void _export_all_dialog(); diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index 9b4c41d7ed9..fe7ab8b5b3d 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -30,256 +30,75 @@ #include "editor_file_dialog.h" -#include "core/config/project_settings.h" -#include "core/os/keyboard.h" -#include "core/os/os.h" #include "editor/docks/filesystem_dock.h" -#include "editor/editor_node.h" #include "editor/file_system/dependency_editor.h" -#include "editor/file_system/editor_file_system.h" -#include "editor/inspector/editor_resource_preview.h" #include "editor/settings/editor_settings.h" -#include "editor/themes/editor_scale.h" -#include "scene/gui/center_container.h" -#include "scene/gui/check_box.h" -#include "scene/gui/flow_container.h" -#include "scene/gui/grid_container.h" -#include "scene/gui/label.h" -#include "scene/gui/line_edit.h" -#include "scene/gui/option_button.h" -#include "scene/gui/separator.h" -#include "scene/gui/split_container.h" -#include "scene/gui/texture_rect.h" -#include "servers/display/display_server.h" -void EditorFileDialog::_native_popup() { - // Show native dialog directly. - String root; - if (access == ACCESS_RESOURCES) { - root = ProjectSettings::get_singleton()->get_resource_path(); - } else if (access == ACCESS_USERDATA) { - root = OS::get_singleton()->get_user_data_dir(); - } +void EditorFileDialog::_item_menu_id_pressed(int p_option) { + // Use dependency dialog to delete the entry in the editor, but only for project files. + if (p_option == ITEM_MENU_DELETE && get_access() == ACCESS_RESOURCES) { + const PackedInt32Array selected = get_file_item_list()->get_selected_items(); + if (selected.is_empty()) { + return; + } - // Attach native file dialog to first persistent parent window. - Window *w = (is_transient() || is_transient_to_focused()) ? get_parent_visible_window() : nullptr; - while (w && w->get_flag(FLAG_POPUP) && w->get_parent_visible_window()) { - w = w->get_parent_visible_window(); - } - DisplayServer::WindowID wid = w ? w->get_window_id() : DisplayServer::INVALID_WINDOW_ID; + if (!dependency_remove_dialog) { + dependency_remove_dialog = memnew(DependencyRemoveDialog); + add_child(dependency_remove_dialog); + } - DisplayServer::get_singleton()->file_dialog_with_options_show(get_displayed_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), processed_filters, _get_options(), callable_mp(this, &EditorFileDialog::_native_dialog_cb), wid); -} + const Dictionary meta = get_file_item_list()->get_item_metadata(selected[0]); + const String delete_path = dir_access->get_current_dir().path_join(meta["name"]); -void EditorFileDialog::_popup_base(const Rect2i &p_rect) { - _update_option_controls(); - - bool use_native = DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (bool(EDITOR_GET("interface/editor/use_native_file_dialogs")) || OS::get_singleton()->is_sandboxed()); - if (use_native) { - _native_popup(); - } else { - // Show custom file dialog. - ConfirmationDialog::_popup_base(p_rect); - } -} - -void EditorFileDialog::set_visible(bool p_visible) { - if (p_visible) { - bool use_native = DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (bool(EDITOR_GET("interface/editor/use_native_file_dialogs")) || OS::get_singleton()->is_sandboxed()); - _update_option_controls(); - if (use_native) { - _native_popup(); + if (meta["dir"]) { + dependency_remove_dialog->show(Vector{ delete_path }, Vector()); } else { - // Show custom file dialog. - ConfirmationDialog::set_visible(p_visible); + dependency_remove_dialog->show(Vector(), Vector{ delete_path }); } - } else { - ConfirmationDialog::set_visible(p_visible); - } -} - -void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector &p_files, int p_filter, const Dictionary &p_selected_options) { - if (!p_ok) { - file->set_text(""); - emit_signal(SNAME("canceled")); return; } - - if (p_files.is_empty()) { - return; - } - - Vector files = p_files; - if (access != ACCESS_FILESYSTEM) { - for (String &file_name : files) { - file_name = ProjectSettings::get_singleton()->localize_path(file_name); - } - } - selected_options = p_selected_options; - - String f = files[0]; - - filter->select(p_filter); - dir->set_text(f.get_base_dir()); - file->set_text(f.get_file()); - _dir_submitted(f.get_base_dir()); - - if (mode == FILE_MODE_OPEN_FILES) { - emit_signal(SNAME("files_selected"), files); - } else { - if (mode == FILE_MODE_SAVE_FILE) { - bool valid = false; - - if (p_filter == filter->get_item_count() - 1) { - valid = true; // Match none. - } else if (filters.size() > 1 && p_filter == 0) { - // Match all filters. - for (int i = 0; i < filters.size(); i++) { - String flt = filters[i].get_slicec(';', 0); - for (int j = 0; j < flt.get_slice_count(","); j++) { - String str = flt.get_slicec(',', j).strip_edges(); - if (f.matchn(str)) { - valid = true; - break; - } - } - if (valid) { - break; - } - } - } else { - int idx = p_filter; - if (filters.size() > 1) { - idx--; - } - if (idx >= 0 && idx < filters.size()) { - String flt = filters[idx].get_slicec(';', 0); - int filter_slice_count = flt.get_slice_count(","); - for (int j = 0; j < filter_slice_count; j++) { - String str = flt.get_slicec(',', j).strip_edges(); - if (f.matchn(str)) { - valid = true; - break; - } - } - - if (!valid && filter_slice_count > 0) { - String str = flt.get_slicec(',', 0).strip_edges(); - f += str.substr(1); - file->set_text(f.get_file()); - valid = true; - } - } else { - valid = true; - } - } - - // Add first extension of filter if no valid extension is found. - if (!valid) { - int idx = p_filter; - String flt = filters[idx].get_slicec(';', 0); - String ext = flt.get_slicec(',', 0).strip_edges().get_extension(); - f += "." + ext; - } - emit_signal(SNAME("file_selected"), f); - } else if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) { - emit_signal(SNAME("file_selected"), f); - } else if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_DIR) { - emit_signal(SNAME("dir_selected"), f); - } - } + FileDialog::_item_menu_id_pressed(p_option); } -void EditorFileDialog::popup_file_dialog() { - popup_centered_clamped(Size2(1050, 700) * EDSCALE, 0.8); - _focus_file_text(); +bool EditorFileDialog::_should_use_native_popup() const { + return _can_use_native_popup() && (OS::get_singleton()->is_sandboxed() || EDITOR_GET("interface/editor/use_native_file_dialogs").operator bool()); } -void EditorFileDialog::_focus_file_text() { - int lp = file->get_text().rfind_char('.'); - if (lp != -1) { - file->select(0, lp); - file->grab_focus(); +bool EditorFileDialog::_should_hide_file(const String &p_file) const { + if (Engine::get_singleton()->is_project_manager_hint()) { + return false; } + const String full_path = dir_access->get_current_dir().path_join(p_file); + return EditorFileSystem::_should_skip_directory(full_path); } -VBoxContainer *EditorFileDialog::get_vbox() { - return vbox; +Color EditorFileDialog::_get_folder_color(const String &p_path) const { + return FileSystemDock::get_dir_icon_color(p_path, FileDialog::_get_folder_color(p_path)); } -void EditorFileDialog::_update_theme_item_cache() { - ConfirmationDialog::_update_theme_item_cache(); +void EditorFileDialog::_bind_methods() { +#ifndef DISABLE_DEPRECATED + ClassDB::bind_method(D_METHOD("add_side_menu", "menu", "title"), &EditorFileDialog::add_side_menu, DEFVAL("")); + ClassDB::bind_method(D_METHOD("set_disable_overwrite_warning", "disable"), &EditorFileDialog::set_disable_overwrite_warning); + ClassDB::bind_method(D_METHOD("is_overwrite_warning_disabled"), &EditorFileDialog::is_overwrite_warning_disabled); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_overwrite_warning", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_disable_overwrite_warning", "is_overwrite_warning_disabled"); +#endif +} - theme_cache.parent_folder = get_editor_theme_icon(SNAME("ArrowUp")); - theme_cache.forward_folder = get_editor_theme_icon(SNAME("Forward")); - theme_cache.back_folder = get_editor_theme_icon(SNAME("Back")); - theme_cache.reload = get_editor_theme_icon(SNAME("Reload")); - theme_cache.toggle_hidden = get_editor_theme_icon(SNAME("GuiVisibilityVisible")); - theme_cache.toggle_filename_filter = get_editor_theme_icon(SNAME("FilenameFilter")); - theme_cache.favorite = get_editor_theme_icon(SNAME("Favorites")); - theme_cache.mode_thumbnails = get_editor_theme_icon(SNAME("FileThumbnail")); - theme_cache.mode_list = get_editor_theme_icon(SNAME("FileList")); - theme_cache.favorites_up = get_editor_theme_icon(SNAME("MoveUp")); - theme_cache.favorites_down = get_editor_theme_icon(SNAME("MoveDown")); - theme_cache.create_folder = get_editor_theme_icon(SNAME("FolderCreate")); - theme_cache.open_folder = get_editor_theme_icon(SNAME("FolderBrowse")); - - theme_cache.filter_box = get_editor_theme_icon(SNAME("Search")); - theme_cache.file_sort_button = get_editor_theme_icon(SNAME("Sort")); - - theme_cache.file = get_editor_theme_icon(SNAME("File")); - theme_cache.folder = get_editor_theme_icon(SNAME("Folder")); - theme_cache.folder_icon_color = get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog")); - - theme_cache.action_copy = get_editor_theme_icon(SNAME("ActionCopy")); - theme_cache.action_delete = get_editor_theme_icon(SNAME("Remove")); - theme_cache.filesystem = get_editor_theme_icon(SNAME("Filesystem")); - - theme_cache.folder_medium_thumbnail = get_editor_theme_icon(SNAME("FolderMediumThumb")); - theme_cache.file_medium_thumbnail = get_editor_theme_icon(SNAME("FileMediumThumb")); - theme_cache.folder_big_thumbnail = get_editor_theme_icon(SNAME("FolderBigThumb")); - theme_cache.file_big_thumbnail = get_editor_theme_icon(SNAME("FileBigThumb")); - - theme_cache.progress[0] = get_editor_theme_icon("Progress1"); - theme_cache.progress[1] = get_editor_theme_icon("Progress2"); - theme_cache.progress[2] = get_editor_theme_icon("Progress3"); - theme_cache.progress[3] = get_editor_theme_icon("Progress4"); - theme_cache.progress[4] = get_editor_theme_icon("Progress5"); - theme_cache.progress[5] = get_editor_theme_icon("Progress6"); - theme_cache.progress[6] = get_editor_theme_icon("Progress7"); - theme_cache.progress[7] = get_editor_theme_icon("Progress8"); +void EditorFileDialog::_validate_property(PropertyInfo &p_property) const { + // Hide properties controlled by editor settings. + if (p_property.name == "use_native_dialog" || p_property.name == "show_hidden_files" || p_property.name == "display_mode") { + p_property.usage = PROPERTY_USAGE_NONE; + } } void EditorFileDialog::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_POSTINITIALIZE: { - set_translation_domain(SNAME("godot.editor")); - } break; - - case NOTIFICATION_TRANSLATION_CHANGED: { - update_filters(); - [[fallthrough]]; - } - - case NOTIFICATION_THEME_CHANGED: - case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - _update_icons(); - invalidate(); - } break; - - case NOTIFICATION_PROCESS: { - if (preview_waiting) { - preview_wheel_timeout -= get_process_delta_time(); - if (preview_wheel_timeout <= 0) { - preview_wheel_index++; - if (preview_wheel_index >= 8) { - preview_wheel_index = 0; - } - - Ref frame = theme_cache.progress[preview_wheel_index]; - preview->set_texture(frame); - preview_wheel_timeout = 0.1; - } + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible()) { + // Synchronize back favorites and recent directories, in case they have changed. + EditorSettings::get_singleton()->set_favorites(get_favorite_list(), false); + EditorSettings::get_singleton()->set_recent_dirs(get_recent_list(), false); } } break; @@ -287,2363 +106,8 @@ void EditorFileDialog::_notification(int p_what) { if (!EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog")) { break; } - bool is_showing_hidden = EDITOR_GET("filesystem/file_dialog/show_hidden_files"); - if (show_hidden_files != is_showing_hidden) { - set_show_hidden_files(is_showing_hidden); - } + set_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files")); set_display_mode((DisplayMode)EDITOR_GET("filesystem/file_dialog/display_mode").operator int()); - - // DO NOT CALL UPDATE FILE LIST HERE, ALL HUNDREDS OF HIDDEN DIALOGS WILL RESPOND, CALL INVALIDATE INSTEAD - invalidate(); - } break; - - case NOTIFICATION_VISIBILITY_CHANGED: { - if (!is_visible()) { - set_process_shortcut_input(false); - } - - invalidate(); // For consistency with the standard FileDialog. - } break; - - case NOTIFICATION_WM_WINDOW_FOCUS_IN: { - // Check if the current directory was removed externally (much less likely to happen while editor window is focused). - String previous_dir = get_current_dir(); - while (!dir_access->dir_exists(get_current_dir())) { - _go_up(); - - // In case we can't go further up, use some fallback and break. - if (get_current_dir() == previous_dir) { - _dir_submitted(OS::get_singleton()->get_user_data_dir()); - break; - } - } } break; } } - -void EditorFileDialog::shortcut_input(const Ref &p_event) { - ERR_FAIL_COND(p_event.is_null()); - - Ref k = p_event; - - if (k.is_valid()) { - if (k->is_pressed()) { - bool handled = false; - - if (ED_IS_SHORTCUT("file_dialog/go_back", p_event)) { - _go_back(); - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/go_forward", p_event)) { - _go_forward(); - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/go_up", p_event)) { - _go_up(); - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/refresh", p_event)) { - invalidate(); - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/toggle_hidden_files", p_event)) { - set_show_hidden_files(!show_hidden_files); - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/toggle_favorite", p_event)) { - _favorite_pressed(); - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/toggle_mode", p_event)) { - if (mode_thumbnails->is_pressed()) { - set_display_mode(DISPLAY_LIST); - } else { - set_display_mode(DISPLAY_THUMBNAILS); - } - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/create_folder", 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(); - dir->select_all(); - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/focus_filter", p_event)) { - show_search_filter_button->set_pressed(!show_search_filter_button->is_pressed()); - _focus_filter_box(); - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/move_favorite_up", p_event)) { - _favorite_move_up(); - handled = true; - } - if (ED_IS_SHORTCUT("file_dialog/move_favorite_down", p_event)) { - _favorite_move_down(); - handled = true; - } - - if (handled) { - set_input_as_handled(); - } - } - } -} - -void EditorFileDialog::set_enable_multiple_selection(bool p_enable) { - item_list->set_select_mode(p_enable ? ItemList::SELECT_MULTI : ItemList::SELECT_SINGLE); -} - -Vector EditorFileDialog::get_selected_files() const { - Vector list; - for (int i = 0; i < item_list->get_item_count(); i++) { - if (item_list->is_selected(i)) { - list.push_back(item_list->get_item_text(i)); - } - } - return list; -} - -void EditorFileDialog::update_dir() { - full_dir = dir_access->get_current_dir(); - if (drives->is_visible()) { - if (dir_access->get_current_dir().is_network_share_path()) { - _update_drives(false); - drives->add_item(RTR("Network")); - drives->set_item_disabled(-1, true); - drives->select(drives->get_item_count() - 1); - } else { - drives->select(dir_access->get_current_drive()); - } - } - dir->set_text(dir_access->get_current_dir(false)); - - filter_box->clear(); - - // Disable "Open" button only when selecting file(s) mode. - get_ok_button()->set_disabled(_is_open_should_be_disabled()); - switch (mode) { - case FILE_MODE_OPEN_FILE: - case FILE_MODE_OPEN_FILES: - file->set_text(""); - set_ok_button_text(TTRC("Open")); - break; - case FILE_MODE_OPEN_ANY: - case FILE_MODE_OPEN_DIR: - file->set_text(""); - set_ok_button_text(TTRC("Select Current Folder")); - break; - case FILE_MODE_SAVE_FILE: - // FIXME: Implement, or refactor to avoid duplication with set_mode - break; - } -} - -void EditorFileDialog::_dir_submitted(const String &p_dir) { - String new_dir = p_dir; -#ifdef WINDOWS_ENABLED - if (drives->is_visible() && !new_dir.is_network_share_path() && new_dir.is_absolute_path() && new_dir.find(":/") == -1 && new_dir.find(":\\") == -1) { - // Non network path without X:/ prefix on Windows, add drive letter. - new_dir = drives->get_item_text(drives->get_selected()).path_join(new_dir); - } -#endif - dir_access->change_dir(new_dir); - invalidate(); - update_dir(); - _push_history(); -} - -void EditorFileDialog::_file_submitted(const String &p_file) { - _action_pressed(); -} - -void EditorFileDialog::_save_confirm_pressed() { - String f = dir_access->get_current_dir().path_join(file->get_text()); - _save_to_recent(); - hide(); - emit_signal(SNAME("file_selected"), f); -} - -void EditorFileDialog::_post_popup() { - ConfirmationDialog::_post_popup(); - - // Check if the current path doesn't exist and correct it. - String current = dir_access->get_current_dir(); - while (!dir_access->dir_exists(current)) { - current = current.get_base_dir(); - } - set_current_dir(current); - - if (mode == FILE_MODE_SAVE_FILE) { - file->grab_focus(true); - } else { - item_list->grab_focus(true); - } - - bool is_open_directory_mode = mode == FILE_MODE_OPEN_DIR; - PopupMenu *p = file_sort_button->get_popup(); - p->set_item_disabled(2, is_open_directory_mode); - p->set_item_disabled(3, is_open_directory_mode); - p->set_item_disabled(4, is_open_directory_mode); - p->set_item_disabled(5, is_open_directory_mode); - if (is_open_directory_mode) { - file_box->set_visible(false); - } else { - file_box->set_visible(true); - } - - if (!get_current_file().is_empty()) { - _request_single_thumbnail(get_current_dir().path_join(get_current_file())); - } - - local_history.clear(); - local_history_pos = -1; - _push_history(); - - set_process_shortcut_input(true); -} - -void EditorFileDialog::_thumbnail_result(const String &p_path, const Ref &p_preview, const Ref &p_small_preview) { - if (display_mode == DISPLAY_LIST || p_preview.is_null()) { - return; - } - - for (int i = 0; i < item_list->get_item_count(); i++) { - Dictionary d = item_list->get_item_metadata(i); - String pname = d["path"]; - if (pname == p_path) { - item_list->set_item_icon(i, p_preview); - item_list->set_item_tag_icon(i, Ref()); - } - } -} - -void EditorFileDialog::_thumbnail_done(const String &p_path, const Ref &p_preview, const Ref &p_small_preview) { - set_process(false); - preview_waiting = false; - - if (p_preview.is_valid() && get_current_path() == p_path) { - preview->set_texture(p_preview); - if (display_mode == DISPLAY_THUMBNAILS) { - preview_vb->hide(); - } else { - preview_vb->show(); - } - - } else { - preview_vb->hide(); - preview->set_texture(Ref()); - } -} - -void EditorFileDialog::_request_single_thumbnail(const String &p_path) { - if (!FileAccess::exists(p_path) || !previews_enabled || !EditorResourcePreview::get_singleton()) { - return; - } - - set_process(true); - preview_waiting = true; - preview_wheel_timeout = 0; - EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, callable_mp(this, &EditorFileDialog::_thumbnail_done)); -} - -void EditorFileDialog::_action_pressed() { - // Accept selection in the custom dialog. - if (mode == FILE_MODE_OPEN_FILES) { - String fbase = dir_access->get_current_dir(); - - Vector files; - for (int i = 0; i < item_list->get_item_count(); i++) { - if (item_list->is_selected(i)) { - files.push_back(fbase.path_join(item_list->get_item_text(i))); - } - } - - if (files.size()) { - _save_to_recent(); - hide(); - emit_signal(SNAME("files_selected"), files); - } - - return; - } - - String file_text = file->get_text(); - String f = file_text.is_absolute_path() ? file_text : dir_access->get_current_dir().path_join(file_text); - - if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && (dir_access->file_exists(f) || dir_access->is_bundle(f))) { - _save_to_recent(); - hide(); - emit_signal(SNAME("file_selected"), f); - } else if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_DIR) { - String path = dir_access->get_current_dir(); - - path = path.replace_char('\\', '/'); - - for (int i = 0; i < item_list->get_item_count(); i++) { - if (item_list->is_selected(i)) { - Dictionary d = item_list->get_item_metadata(i); - if (d["dir"]) { - path = path.path_join(d["name"]); - - break; - } - } - } - - _save_to_recent(); - hide(); - emit_signal(SNAME("dir_selected"), path); - } - - if (mode == FILE_MODE_SAVE_FILE) { - bool valid = false; - - if (filter->get_selected() == filter->get_item_count() - 1) { - valid = true; // Match none. - } else if (filters.size() > 1 && filter->get_selected() == 0) { - // Match all filters. - for (int i = 0; i < filters.size(); i++) { - String flt = filters[i].get_slicec(';', 0); - for (int j = 0; j < flt.get_slice_count(","); j++) { - String str = flt.get_slicec(',', j).strip_edges(); - if (f.matchn(str)) { - valid = true; - break; - } - } - if (valid) { - break; - } - } - } else { - int idx = filter->get_selected(); - if (filters.size() > 1) { - idx--; - } - if (idx >= 0 && idx < filters.size()) { - String flt = filters[idx].get_slicec(';', 0); - int filter_slice_count = flt.get_slice_count(","); - for (int j = 0; j < filter_slice_count; j++) { - String str = (flt.get_slicec(',', j).strip_edges()); - if (f.matchn(str)) { - valid = true; - break; - } - } - - if (!valid && filter_slice_count > 0) { - String str = flt.get_slicec(',', 0).strip_edges(); - f += str.substr(1); - _request_single_thumbnail(get_current_dir().path_join(f.get_file())); - file->set_text(f.get_file()); - valid = true; - } - } else { - valid = true; - } - } - - // First check we're not having an empty name. - String file_name = file_text.strip_edges().get_file(); - if (file_name.is_empty()) { - error_dialog->set_text(TTRC("Cannot save file with an empty filename.")); - error_dialog->popup_centered(Size2(250, 80) * EDSCALE); - return; - } - - // Add first extension of filter if no valid extension is found. - if (!valid) { - int idx = filter->get_selected(); - String flt = filters[idx].get_slicec(';', 0); - String ext = flt.get_slicec(',', 0).strip_edges().get_extension(); - f += "." + ext; - } - - if (file_name.begins_with(".")) { // Could still happen if typed manually. - error_dialog->set_text(TTRC("Cannot save file with a name starting with a dot.")); - error_dialog->popup_centered(Size2(250, 80) * EDSCALE); - return; - } - - if (dir_access->file_exists(f) && !disable_overwrite_warning) { - confirm_save->set_text(vformat(TTR("File \"%s\" already exists.\nDo you want to overwrite it?"), f)); - confirm_save->popup_centered(Size2(250, 80) * EDSCALE); - } else { - _save_to_recent(); - hide(); - emit_signal(SNAME("file_selected"), f); - } - } -} - -void EditorFileDialog::_cancel_pressed() { - file->set_text(""); - invalidate(); - hide(); -} - -void EditorFileDialog::_item_selected(int p_item) { - int current = p_item; - if (current < 0 || current >= item_list->get_item_count()) { - return; - } - - Dictionary d = item_list->get_item_metadata(current); - - if (!d["dir"]) { - file->set_text(d["name"]); - _request_single_thumbnail(get_current_dir().path_join(get_current_file())); - - if (mode != FILE_MODE_SAVE_FILE) { - // FILE_MODE_OPEN_ANY can alternate this text depending on what's selected. - set_ok_button_text(TTRC("Open")); - } - } else if (mode == FILE_MODE_OPEN_DIR || mode == FILE_MODE_OPEN_ANY) { - file->set_text(""); - set_ok_button_text(TTRC("Select This Folder")); - } - - get_ok_button()->set_disabled(_is_open_should_be_disabled()); -} - -void EditorFileDialog::_multi_selected(int p_item, bool p_selected) { - int current = p_item; - if (current < 0 || current >= item_list->get_item_count()) { - return; - } - - Dictionary d = item_list->get_item_metadata(current); - - if (!d["dir"] && p_selected) { - file->set_text(d["name"]); - _request_single_thumbnail(get_current_dir().path_join(get_current_file())); - } - - get_ok_button()->set_disabled(_is_open_should_be_disabled()); -} - -void EditorFileDialog::_items_clear_selection(const Vector2 &p_pos, MouseButton p_mouse_button_index) { - if (p_mouse_button_index != MouseButton::LEFT) { - return; - } - - item_list->deselect_all(); - - // If nothing is selected, then block Open button. - switch (mode) { - case FILE_MODE_OPEN_FILE: - case FILE_MODE_OPEN_FILES: - set_ok_button_text(TTRC("Open")); - get_ok_button()->set_disabled(!item_list->is_anything_selected()); - break; - - case FILE_MODE_OPEN_ANY: - case FILE_MODE_OPEN_DIR: - file->set_text(""); - get_ok_button()->set_disabled(false); - set_ok_button_text(TTRC("Select Current Folder")); - break; - - case FILE_MODE_SAVE_FILE: - // FIXME: Implement, or refactor to avoid duplication with set_mode - break; - } -} - -void EditorFileDialog::_push_history() { - local_history.resize(local_history_pos + 1); - String new_path = dir_access->get_current_dir(); - if (local_history.is_empty() || new_path != local_history[local_history_pos]) { - local_history.push_back(new_path); - local_history_pos++; - dir_prev->set_disabled(local_history_pos == 0); - dir_next->set_disabled(true); - } -} - -void EditorFileDialog::_item_dc_selected(int p_item) { - int current = p_item; - if (current < 0 || current >= item_list->get_item_count()) { - return; - } - - Dictionary d = item_list->get_item_metadata(current); - - if (d["dir"]) { - dir_access->change_dir(d["name"]); - callable_mp(this, &EditorFileDialog::update_file_list).call_deferred(); - callable_mp(this, &EditorFileDialog::update_dir).call_deferred(); - - _push_history(); - - } else { - _action_pressed(); - } -} - -void EditorFileDialog::_item_list_item_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index) { - if (p_mouse_button_index != MouseButton::RIGHT) { - return; - } - - // Right click on specific file(s) or folder(s). - item_menu->clear(); - item_menu->reset_size(); - - // 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 (String(item_meta["path"]).begins_with(ProjectSettings::get_singleton()->get_project_data_path())) { - allow_delete = false; - break; - } - } - - if (single_item_selected) { - item_menu->add_icon_item(theme_cache.action_copy, TTRC("Copy Path"), ITEM_MENU_COPY_PATH); - } - if (allow_delete) { - item_menu->add_icon_item(theme_cache.action_delete, TTRC("Delete"), ITEM_MENU_DELETE, Key::KEY_DELETE); - } - -#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) - // Opening the system file manager is not supported on the Android and web editors. - if (single_item_selected) { - item_menu->add_separator(); - Dictionary item_meta = item_list->get_item_metadata(p_item); - String item_text = item_meta["dir"] ? TTRC("Open in File Manager") : TTRC("Show in File Manager"); - item_menu->add_icon_item(theme_cache.filesystem, item_text, ITEM_MENU_SHOW_IN_EXPLORER); - } -#endif - if (single_item_selected) { - Dictionary item_meta = item_list->get_item_metadata(p_item); - if (item_meta["bundle"]) { - item_menu->add_icon_item(theme_cache.open_folder, TTRC("Show Package Contents"), ITEM_MENU_SHOW_BUNDLE_CONTENT); - } - } - - if (item_menu->get_item_count() > 0) { - item_menu->set_position(item_list->get_screen_position() + p_pos); - item_menu->reset_size(); - item_menu->popup(); - } -} - -void EditorFileDialog::_item_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index) { - if (p_mouse_button_index != MouseButton::RIGHT && p_mouse_button_index != MouseButton::LEFT) { - return; - } - - // Left or 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->deselect(i); - } - - if (p_mouse_button_index != MouseButton::RIGHT) { - return; - } - - item_menu->clear(); - item_menu->reset_size(); - - if (can_create_dir) { - item_menu->add_icon_item(theme_cache.folder, TTRC("New Folder..."), ITEM_MENU_NEW_FOLDER, KeyModifierMask::CMD_OR_CTRL | Key::N); - } - item_menu->add_icon_item(theme_cache.reload, TTRC("Refresh"), ITEM_MENU_REFRESH, Key::F5); -#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(); - item_menu->add_icon_item(theme_cache.filesystem, TTRC("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); -#endif - - item_menu->set_position(item_list->get_screen_position() + p_pos); - item_menu->reset_size(); - 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()); - DisplayServer::get_singleton()->clipboard_set(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->is_anything_selected()) { - // 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"]); - } - OS::get_singleton()->shell_show_in_file_manager(path, true); - } break; - - case ITEM_MENU_SHOW_BUNDLE_CONTENT: { - String path; - int idx = item_list->get_current(); - if (idx == -1 || !item_list->is_anything_selected()) { - return; - } - Dictionary item_meta = item_list->get_item_metadata(idx); - dir_access->change_dir(item_meta["path"]); - callable_mp(this, &EditorFileDialog::update_file_list).call_deferred(); - callable_mp(this, &EditorFileDialog::update_dir).call_deferred(); - - _push_history(); - } break; - } -} - -bool EditorFileDialog::_is_open_should_be_disabled() { - if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_SAVE_FILE) { - return false; - } - - Vector items = item_list->get_selected_items(); - if (items.is_empty()) { - return mode != FILE_MODE_OPEN_DIR; // In "Open folder" mode, having nothing selected picks the current folder. - } - - for (int i = 0; i < items.size(); i++) { - Dictionary d = item_list->get_item_metadata(items.get(i)); - - if (((mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES) && d["dir"]) || (mode == FILE_MODE_OPEN_DIR && !d["dir"])) { - return true; - } - } - - return false; -} - -void EditorFileDialog::update_file_name() { - int idx = filter->get_selected() - 1; - if ((idx == -1 && filter->get_item_count() == 2) || (filter->get_item_count() > 2 && idx >= 0 && idx < filter->get_item_count() - 2)) { - if (idx == -1) { - idx += 1; - } - String filter_str = filters[idx]; - String file_str = file->get_text(); - String base_name = file_str.get_basename(); - Vector filter_substr = filter_str.split(";"); - if (filter_substr.size() >= 2) { - file_str = base_name + "." + filter_substr[0].strip_edges().get_extension().to_lower(); - } else { - file_str = base_name + "." + filter_str.strip_edges().get_extension().to_lower(); - } - file->set_text(file_str); - } -} - -// DO NOT USE THIS FUNCTION UNLESS NEEDED, CALL INVALIDATE() INSTEAD. -void EditorFileDialog::update_file_list() { - int thumbnail_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size"); - thumbnail_size *= EDSCALE; - Ref folder_thumbnail; - Ref file_thumbnail; - - item_list->clear(); - - // Scroll back to the top after opening a directory - item_list->get_v_scroll_bar()->set_value(0); - - if (display_mode == DISPLAY_THUMBNAILS) { - item_list->set_max_columns(0); - item_list->set_icon_mode(ItemList::ICON_MODE_TOP); - item_list->set_fixed_column_width(thumbnail_size * 3 / 2); - item_list->set_max_text_lines(2); - item_list->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); - item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size)); - - if (thumbnail_size < 64) { - folder_thumbnail = theme_cache.folder_medium_thumbnail; - file_thumbnail = theme_cache.file_medium_thumbnail; - } else { - folder_thumbnail = theme_cache.folder_big_thumbnail; - file_thumbnail = theme_cache.file_big_thumbnail; - } - - preview_vb->hide(); - - } else { - item_list->set_icon_mode(ItemList::ICON_MODE_LEFT); - item_list->set_max_columns(1); - item_list->set_max_text_lines(1); - item_list->set_fixed_column_width(0); - item_list->set_fixed_icon_size(Size2()); - if (preview->get_texture().is_valid()) { - preview_vb->show(); - } - } - - String cdir = dir_access->get_current_dir(); - - dir_access->list_dir_begin(); - - List file_infos; - List dirs; - - String item = dir_access->get_next(); - - while (!item.is_empty()) { - if (item == "." || item == "..") { - item = dir_access->get_next(); - continue; - } - - bool matches_search = true; - if (search_string.length() > 0) { - matches_search = item.find(search_string) != -1; - } - - FileInfo file_info; - file_info.name = item; - file_info.path = cdir.path_join(file_info.name); - file_info.type = item.get_extension(); - file_info.modified_time = FileAccess::get_modified_time(file_info.path); - - if (matches_search) { - if (show_hidden_files) { - if (!dir_access->current_is_dir()) { - file_infos.push_back(file_info); - } else { - dirs.push_back(file_info.name); - } - } else if (!dir_access->current_is_hidden()) { - String full_path = cdir == "res://" ? file_info.name : dir_access->get_current_dir() + "/" + file_info.name; - if (dir_access->current_is_dir()) { - if (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path)) { - dirs.push_back(file_info.name); - } - } else { - file_infos.push_back(file_info); - } - } - } - item = dir_access->get_next(); - } - - dirs.sort_custom(); - bool reverse_directories = file_sort == FileSortOption::FILE_SORT_NAME_REVERSE; - if (reverse_directories) { - dirs.reverse(); - } - sort_file_info_list(file_infos, file_sort); - - List patterns; - // build filter - if (filter->get_selected() == filter->get_item_count() - 1) { - // match all - } else if (filters.size() > 1 && filter->get_selected() == 0) { - // match all filters - for (int i = 0; i < filters.size(); i++) { - String f = filters[i].get_slicec(';', 0); - for (int j = 0; j < f.get_slice_count(","); j++) { - patterns.push_back(f.get_slicec(',', j).strip_edges()); - } - } - } else { - int idx = filter->get_selected(); - if (filters.size() > 1) { - idx--; - } - - if (idx >= 0 && idx < filters.size()) { - String f = filters[idx].get_slicec(';', 0); - for (int j = 0; j < f.get_slice_count(","); j++) { - patterns.push_back(f.get_slicec(',', j).strip_edges()); - } - } - } - - while (!dirs.is_empty()) { - const String &dir_name = dirs.front()->get(); - - bool bundle = dir_access->is_bundle(dir_name); - bool found = true; - if (bundle) { - bool match = patterns.is_empty(); - for (const String &E : patterns) { - if (dir_name.matchn(E)) { - match = true; - break; - } - } - found = match; - } - - if (found) { - item_list->add_item(dir_name); - - if (display_mode == DISPLAY_THUMBNAILS) { - item_list->set_item_icon(-1, bundle ? file_thumbnail : folder_thumbnail); - } else { - item_list->set_item_icon(-1, bundle ? theme_cache.file : theme_cache.folder); - } - - Dictionary d; - d["name"] = dir_name; - d["path"] = cdir.path_join(dir_name); - d["dir"] = !bundle; - d["bundle"] = bundle; - - item_list->set_item_metadata(-1, d); - item_list->set_item_icon_modulate(-1, FileSystemDock::get_dir_icon_color(String(d["path"]), theme_cache.folder_icon_color)); - } - - dirs.pop_front(); - } - - while (!file_infos.is_empty()) { - bool match = patterns.is_empty(); - - FileInfo file_info = file_infos.front()->get(); - for (const String &E : patterns) { - if (file_info.name.matchn(E)) { - match = true; - break; - } - } - - if (match) { - item_list->add_item(file_info.name); - - if (get_icon_func) { - Ref icon = get_icon_func(file_info.path); - if (display_mode == DISPLAY_THUMBNAILS) { - Ref thumbnail; - if (get_thumbnail_func) { - thumbnail = get_thumbnail_func(file_info.path); - } - if (thumbnail.is_null()) { - thumbnail = file_thumbnail; - } - - item_list->set_item_icon(-1, thumbnail); - item_list->set_item_tag_icon(-1, icon); - } else { - item_list->set_item_icon(-1, icon); - } - } - - Dictionary d; - d["name"] = file_info.name; - d["dir"] = false; - d["bundle"] = false; - d["path"] = file_info.path; - item_list->set_item_metadata(-1, d); - - if (display_mode == DISPLAY_THUMBNAILS && previews_enabled && EditorResourcePreview::get_singleton()) { - EditorResourcePreview::get_singleton()->queue_resource_preview(file_info.path, callable_mp(this, &EditorFileDialog::_thumbnail_result)); - } - - if (file->get_text() == file_info.name) { - item_list->set_current(item_list->get_item_count() - 1); - } - } - - file_infos.pop_front(); - } - - if (favorites->get_current() >= 0) { - favorites->deselect(favorites->get_current()); - } - - favorite->set_pressed(false); - fav_up->set_disabled(true); - fav_down->set_disabled(true); - get_ok_button()->set_disabled(_is_open_should_be_disabled()); - for (int i = 0; i < favorites->get_item_count(); i++) { - if (favorites->get_item_metadata(i) == cdir || favorites->get_item_metadata(i) == cdir + "/") { - favorites->select(i); - favorite->set_pressed(true); - if (i > 0) { - fav_up->set_disabled(false); - } - if (i < favorites->get_item_count() - 1) { - fav_down->set_disabled(false); - } - break; - } - } -} - -void EditorFileDialog::_filter_selected(int) { - update_file_name(); - update_file_list(); -} - -void EditorFileDialog::_search_filter_selected() { - Vector items = item_list->get_selected_items(); - if (!items.is_empty()) { - int index = items[0]; - file->set_text(item_list->get_item_text(index)); - file->emit_signal(SceneStringName(text_submitted), file->get_text()); - } -} - -void EditorFileDialog::update_filters() { - filter->clear(); - processed_filters.clear(); - - if (filters.size() > 1) { - String all_filters; - String all_mime; - String all_filters_full; - String all_mime_full; - - const int max_filters = 5; - - // "All Recognized" display name. - for (int i = 0; i < MIN(max_filters, filters.size()); i++) { - String flt = filters[i].get_slicec(';', 0).strip_edges(); - if (!all_filters.is_empty() && !flt.is_empty()) { - all_filters += ", "; - } - all_filters += flt; - - String mime = filters[i].get_slicec(';', 2).strip_edges(); - if (!all_mime.is_empty() && !mime.is_empty()) { - all_mime += ", "; - } - all_mime += mime; - } - - // "All Recognized" filter. - for (int i = 0; i < filters.size(); i++) { - String flt = filters[i].get_slicec(';', 0).strip_edges(); - if (!all_filters_full.is_empty() && !flt.is_empty()) { - all_filters_full += ","; - } - all_filters_full += flt; - - String mime = filters[i].get_slicec(';', 2).strip_edges(); - if (!all_mime_full.is_empty() && !mime.is_empty()) { - all_mime_full += ","; - } - all_mime_full += mime; - } - - String native_all_name; - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE_MIME)) { - native_all_name += all_filters; - } - if (!native_all_name.is_empty()) { - native_all_name += ", "; - } - native_all_name += all_mime; - - if (max_filters < filters.size()) { - all_filters += ", ..."; - native_all_name += ", ..."; - } - - filter->add_item(atr(ETR("All Recognized")) + " (" + all_filters + ")"); - processed_filters.push_back(all_filters_full + ";" + atr(ETR("All Recognized")) + " (" + native_all_name + ")" + ";" + all_mime_full); - } - for (int i = 0; i < filters.size(); i++) { - String flt = filters[i].get_slicec(';', 0).strip_edges(); - String desc = filters[i].get_slicec(';', 1).strip_edges(); - String mime = filters[i].get_slicec(';', 2).strip_edges(); - String native_name; - if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE_MIME)) { - native_name += flt; - } - if (!native_name.is_empty() && !mime.is_empty()) { - native_name += ", "; - } - native_name += mime; - if (!desc.is_empty()) { - filter->add_item(atr(desc) + " (" + flt + ")"); - processed_filters.push_back(flt + ";" + atr(desc) + " (" + native_name + ");" + mime); - } else { - filter->add_item("(" + flt + ")"); - processed_filters.push_back(flt + ";(" + native_name + ");" + mime); - } - } - - String f = TTR("All Files") + " (*.*)"; - filter->add_item(f); - processed_filters.push_back("*.*;" + f + ";application/octet-stream"); -} - -void EditorFileDialog::clear_filters() { - filters.clear(); - update_filters(); - invalidate(); -} - -void EditorFileDialog::clear_search_filter() { - set_search_filter(""); - update_search_filter_gui(); - invalidate(); -} - -void EditorFileDialog::update_search_filter_gui() { - filter_hb->set_visible(show_search_filter); - if (!show_search_filter) { - search_string.clear(); - } - if (filter_box->get_text() == search_string) { - return; - } - filter_box->set_text(search_string); -} - -void EditorFileDialog::add_filter(const String &p_filter, const String &p_description) { - if (p_description.is_empty()) { - filters.push_back(p_filter); - } else { - filters.push_back(vformat("%s ; %s", p_filter, p_description)); - } - update_filters(); - invalidate(); -} - -void EditorFileDialog::set_filters(const Vector &p_filters) { - if (filters == p_filters) { - return; - } - filters = p_filters; - update_filters(); - invalidate(); -} - -void EditorFileDialog::set_search_filter(const String &p_search_filter) { - if (search_string == p_search_filter) { - return; - } - search_string = p_search_filter; - update_search_filter_gui(); - emit_signal(SNAME("filename_filter_changed"), filter); - invalidate(); -} - -Vector EditorFileDialog::get_filters() const { - return filters; -} - -String EditorFileDialog::get_search_filter() const { - return search_string; -} - -String EditorFileDialog::get_current_dir() const { - return dir_access->get_current_dir(); -} - -String EditorFileDialog::get_current_file() const { - return file->get_text(); -} - -String EditorFileDialog::get_current_path() const { - return dir_access->get_current_dir().path_join(file->get_text()); -} - -void EditorFileDialog::set_current_dir(const String &p_dir) { - if (p_dir.is_relative_path()) { - dir_access->change_dir(OS::get_singleton()->get_resource_dir()); - } - dir_access->change_dir(p_dir); - update_dir(); - invalidate(); -} - -void EditorFileDialog::set_current_file(const String &p_file) { - file->set_text(p_file); - update_dir(); - invalidate(); - - if (is_visible()) { - _focus_file_text(); - _request_single_thumbnail(get_current_dir().path_join(get_current_file())); - } -} - -void EditorFileDialog::set_current_path(const String &p_path) { - if (!p_path.size()) { - return; - } - int pos = MAX(p_path.rfind_char('/'), p_path.rfind_char('\\')); - if (pos == -1) { - set_current_file(p_path); - } else { - String path_dir = p_path.substr(0, pos); - String path_file = p_path.substr(pos + 1); - set_current_dir(path_dir); - set_current_file(path_file); - } -} - -void EditorFileDialog::set_file_mode(FileMode p_mode) { - mode = p_mode; - switch (mode) { - case FILE_MODE_OPEN_FILE: - set_ok_button_text(TTRC("Open")); - set_title(TTRC("Open a File")); - can_create_dir = false; - break; - case FILE_MODE_OPEN_FILES: - set_ok_button_text(TTRC("Open")); - set_title(TTRC("Open File(s)")); - can_create_dir = false; - break; - case FILE_MODE_OPEN_DIR: - set_ok_button_text(TTRC("Open")); - set_title(TTRC("Open a Directory")); - can_create_dir = true; - break; - case FILE_MODE_OPEN_ANY: - set_ok_button_text(TTRC("Open")); - set_title(TTRC("Open a File or Directory")); - can_create_dir = true; - break; - case FILE_MODE_SAVE_FILE: - set_ok_button_text(TTRC("Save")); - set_title(TTRC("Save a File")); - can_create_dir = true; - break; - } - - if (mode == FILE_MODE_OPEN_FILES) { - item_list->set_select_mode(ItemList::SELECT_MULTI); - } else { - item_list->set_select_mode(ItemList::SELECT_SINGLE); - } - - makedir_sep->set_visible(can_create_dir); - makedir->set_visible(can_create_dir); -} - -EditorFileDialog::FileMode EditorFileDialog::get_file_mode() const { - return mode; -} - -void EditorFileDialog::set_access(Access p_access) { - ERR_FAIL_INDEX(p_access, 3); - if (access == p_access) { - return; - } - switch (p_access) { - case ACCESS_FILESYSTEM: { - dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - } break; - case ACCESS_RESOURCES: { - dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); - } break; - case ACCESS_USERDATA: { - dir_access = DirAccess::create(DirAccess::ACCESS_USERDATA); - } break; - } - access = p_access; - _update_drives(); - invalidate(); - update_filters(); - update_dir(); -} - -void EditorFileDialog::invalidate() { - if (!is_visible() || is_invalidating) { - return; - } - - is_invalidating = true; - callable_mp(this, &EditorFileDialog::_invalidate).call_deferred(); -} - -void EditorFileDialog::_invalidate() { - if (!is_invalidating) { - return; - } - - update_file_list(); - _update_favorites(); - _update_recent(); - - is_invalidating = false; -} - -EditorFileDialog::Access EditorFileDialog::get_access() const { - return access; -} - -void EditorFileDialog::_make_dir_confirm() { - const String stripped_dirname = makedirname->get_text().strip_edges(); - - if (stripped_dirname.is_empty()) { - error_dialog->set_text(TTRC("The path specified is invalid.")); - error_dialog->popup_centered(Size2(250, 50) * EDSCALE); - makedirname->set_text(""); // Reset label. - return; - } - - if (dir_access->dir_exists(stripped_dirname)) { - error_dialog->set_text(TTRC("Could not create folder. File with that name already exists.")); - error_dialog->popup_centered(Size2(250, 50) * EDSCALE); - makedirname->set_text(""); // Reset label. - return; - } - - Error err = dir_access->make_dir(stripped_dirname); - if (err == OK) { - dir_access->change_dir(stripped_dirname); - invalidate(); - update_filters(); - update_dir(); - _push_history(); - if (access != ACCESS_FILESYSTEM) { - EditorFileSystem::get_singleton()->scan_changes(); //we created a dir, so rescan changes - } - } else { - error_dialog->set_text(TTRC("Could not create folder.")); - error_dialog->popup_centered(Size2(250, 50) * EDSCALE); - } - makedirname->set_text(""); // reset label -} - -void EditorFileDialog::_make_dir() { - makedialog->popup_centered(Size2(250, 80) * EDSCALE); - makedirname->grab_focus(); -} - -void EditorFileDialog::_focus_filter_box() { - filter_box->grab_focus(); - filter_box->select_all(); -} - -void EditorFileDialog::_filter_changed(const String &p_text) { - search_string = p_text; - invalidate(); - - if (item_list->get_selected_items().size() > 0) { - item_list->deselect_all(); - if (item_list->get_item_count() > 0) { - item_list->call_deferred("select", 0); - } - } -} - -void EditorFileDialog::_file_sort_popup(int p_id) { - for (int i = 0; i != static_cast(FileSortOption::FILE_SORT_MAX); i++) { - file_sort_button->get_popup()->set_item_checked(i, (i == p_id)); - } - file_sort = static_cast(p_id); - invalidate(); -} - -void EditorFileDialog::_delete_items() { - // Collect the selected folders and files to delete and check them in the deletion dependency dialog. - Vector folders; - Vector 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) { - if (access == ACCESS_FILESYSTEM) { - global_remove_dialog->popup_centered(); - } else { - dep_remove_dialog->reset_size(); - dep_remove_dialog->show(folders, files); - } - } -} - -void EditorFileDialog::_delete_files_global() { - // Delete files outside of the project directory without dependency checks. - 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); - // Only delete empty directories for safety. - dir_access->remove(item_meta["path"]); - } - update_file_list(); -} - -void EditorFileDialog::_select_drive(int p_idx) { - String d = drives->get_item_text(p_idx); - dir_access->change_dir(d); - file->set_text(""); - invalidate(); - update_dir(); - _push_history(); -} - -void EditorFileDialog::_update_drives(bool p_select) { - int dc = dir_access->get_drive_count(); - if (dc == 0 || access != ACCESS_FILESYSTEM) { - shortcuts_container->hide(); - drives_container->hide(); - drives->hide(); - } else { - drives->clear(); - Node *dp = drives->get_parent(); - if (dp) { - dp->remove_child(drives); - } - dp = dir_access->drives_are_shortcuts() ? shortcuts_container : drives_container; - shortcuts_container->set_visible(dir_access->drives_are_shortcuts()); - drives_container->set_visible(!dir_access->drives_are_shortcuts()); - dp->add_child(drives); - drives->show(); - - for (int i = 0; i < dir_access->get_drive_count(); i++) { - String d = dir_access->get_drive(i); - drives->add_item(dir_access->get_drive(i)); - } - if (p_select) { - drives->select(dir_access->get_current_drive()); - } - } -} - -void EditorFileDialog::_update_icons() { - // Update icons. - - mode_thumbnails->set_button_icon(theme_cache.mode_thumbnails); - mode_list->set_button_icon(theme_cache.mode_list); - - if (is_layout_rtl()) { - dir_prev->set_button_icon(theme_cache.forward_folder); - dir_next->set_button_icon(theme_cache.back_folder); - } else { - dir_prev->set_button_icon(theme_cache.back_folder); - dir_next->set_button_icon(theme_cache.forward_folder); - } - dir_up->set_button_icon(theme_cache.parent_folder); - - refresh->set_button_icon(theme_cache.reload); - favorite->set_button_icon(theme_cache.favorite); - show_hidden->set_button_icon(theme_cache.toggle_hidden); - makedir->set_button_icon(theme_cache.create_folder); - - filter_box->set_right_icon(theme_cache.filter_box); - file_sort_button->set_button_icon(theme_cache.file_sort_button); - show_search_filter_button->set_button_icon(theme_cache.toggle_filename_filter); - filter_box->set_clear_button_enabled(true); - - fav_up->set_button_icon(theme_cache.favorites_up); - fav_down->set_button_icon(theme_cache.favorites_down); -} - -void EditorFileDialog::_favorite_selected(int p_idx) { - Error change_dir_result = dir_access->change_dir(favorites->get_item_metadata(p_idx)); - if (change_dir_result != OK) { - error_dialog->set_text(TTRC("Favorited folder does not exist anymore and will be removed.")); - error_dialog->popup_centered(Size2(250, 50) * EDSCALE); - - bool res = (access == ACCESS_RESOURCES); - - Vector favorited = EditorSettings::get_singleton()->get_favorites(); - String dir_to_remove = favorites->get_item_metadata(p_idx); - - bool found = false; - for (int i = 0; i < favorited.size(); i++) { - bool cres = favorited[i].begins_with("res://"); - if (cres != res) { - continue; - } - - if (favorited[i] == dir_to_remove) { - found = true; - break; - } - } - - if (found) { - favorited.erase(favorites->get_item_metadata(p_idx)); - favorites->remove_item(p_idx); - EditorSettings::get_singleton()->set_favorites(favorited); - } - } else { - update_dir(); - invalidate(); - _push_history(); - } -} - -void EditorFileDialog::_favorite_move_up() { - int current = favorites->get_current(); - - if (current > 0 && current < favorites->get_item_count()) { - Vector favorited = EditorSettings::get_singleton()->get_favorites(); - - int a_idx = favorited.find(String(favorites->get_item_metadata(current - 1))); - int b_idx = favorited.find(String(favorites->get_item_metadata(current))); - - if (a_idx == -1 || b_idx == -1) { - return; - } - SWAP(favorited.write[a_idx], favorited.write[b_idx]); - - EditorSettings::get_singleton()->set_favorites(favorited); - - _update_favorites(); - update_file_list(); - } -} - -void EditorFileDialog::_favorite_move_down() { - int current = favorites->get_current(); - - if (current >= 0 && current < favorites->get_item_count() - 1) { - Vector favorited = EditorSettings::get_singleton()->get_favorites(); - - int a_idx = favorited.find(String(favorites->get_item_metadata(current + 1))); - int b_idx = favorited.find(String(favorites->get_item_metadata(current))); - - if (a_idx == -1 || b_idx == -1) { - return; - } - SWAP(favorited.write[a_idx], favorited.write[b_idx]); - - EditorSettings::get_singleton()->set_favorites(favorited); - - _update_favorites(); - update_file_list(); - } -} - -void EditorFileDialog::_update_favorites() { - bool access_resources = (access == ACCESS_RESOURCES); - - String current = get_current_dir(); - favorites->clear(); - - favorite->set_pressed(false); - - Vector favorited = EditorSettings::get_singleton()->get_favorites(); - Vector favorited_paths; - Vector favorited_names; - - bool fav_changed = false; - int current_favorite = -1; - for (int i = 0; i < favorited.size(); i++) { - String name = favorited[i]; - - if (access_resources != name.begins_with("res://")) { - continue; - } - - if (!name.ends_with("/")) { - continue; - } - - if (!dir_access->dir_exists(name)) { - // Remove invalid directory from the list of Favorited directories. - favorited.remove_at(i--); - fav_changed = true; - continue; - } - - // Compute favorite display text. - if (access_resources && name == "res://") { - if (name == current) { - current_favorite = favorited_paths.size(); - } - name = "/"; - favorited_paths.append(favorited[i]); - favorited_names.append(name); - } else { - if (name == current || name == current + "/") { - current_favorite = favorited_paths.size(); - } - name = name.trim_suffix("/"); - name = name.get_file(); - favorited_paths.append(favorited[i]); - favorited_names.append(name); - } - } - - if (fav_changed) { - EditorSettings::get_singleton()->set_favorites(favorited); - } - - EditorNode::disambiguate_filenames(favorited_paths, favorited_names); - - for (int i = 0; i < favorited_paths.size(); i++) { - favorites->add_item(favorited_names[i], theme_cache.folder); - favorites->set_item_tooltip(-1, favorited_paths[i]); - favorites->set_item_metadata(-1, favorited_paths[i]); - favorites->set_item_icon_modulate(-1, FileSystemDock::get_dir_icon_color(favorited_paths[i], theme_cache.folder_icon_color)); - - if (i == current_favorite) { - favorite->set_pressed(true); - favorites->set_current(favorites->get_item_count() - 1); - recent->deselect_all(); - } - } - - fav_up->set_disabled(current_favorite < 1); - fav_down->set_disabled(current_favorite == -1 || favorited_paths.size() - 1 <= current_favorite); -} - -void EditorFileDialog::_favorite_pressed() { - bool access_resources = (access == ACCESS_RESOURCES); - - String cd = get_current_dir(); - if (!cd.ends_with("/")) { - cd += "/"; - } - - Vector favorited = EditorSettings::get_singleton()->get_favorites(); - - bool found = false; - for (const String &name : favorited) { - if (access_resources != name.begins_with("res://")) { - continue; - } - - if (name == cd) { - found = true; - break; - } - } - - if (found) { - favorited.erase(cd); - } else { - favorited.push_back(cd); - } - - EditorSettings::get_singleton()->set_favorites(favorited); - - _update_favorites(); -} - -void EditorFileDialog::_update_recent() { - recent->clear(); - - bool access_resources = (access == ACCESS_RESOURCES); - Vector recentd = EditorSettings::get_singleton()->get_recent_dirs(); - Vector recentd_paths; - Vector recentd_names; - bool modified = false; - - for (int i = 0; i < recentd.size(); i++) { - String name = recentd[i]; - if (access_resources != name.begins_with("res://")) { - continue; - } - - if (!dir_access->dir_exists(name)) { - // Remove invalid directory from the list of Recent directories. - recentd.remove_at(i--); - modified = true; - continue; - } - - // Compute recent directory display text. - if (access_resources && name == "res://") { - name = "/"; - } else { - name = name.trim_suffix("/"); - name = name.get_file(); - } - recentd_paths.append(recentd[i]); - recentd_names.append(name); - } - - EditorNode::disambiguate_filenames(recentd_paths, recentd_names); - - for (int i = 0; i < recentd_paths.size(); i++) { - recent->add_item(recentd_names[i], theme_cache.folder); - recent->set_item_tooltip(-1, recentd_paths[i]); - recent->set_item_metadata(-1, recentd_paths[i]); - recent->set_item_icon_modulate(-1, FileSystemDock::get_dir_icon_color(recentd_paths[i], theme_cache.folder_icon_color)); - } - - if (modified) { - EditorSettings::get_singleton()->set_recent_dirs(recentd); - } -} - -void EditorFileDialog::_recent_selected(int p_idx) { - Vector recentd = EditorSettings::get_singleton()->get_recent_dirs(); - ERR_FAIL_INDEX(p_idx, recentd.size()); - - dir_access->change_dir(recent->get_item_metadata(p_idx)); - update_file_list(); - update_dir(); - _push_history(); -} - -void EditorFileDialog::_go_up() { - dir_access->change_dir(get_current_dir().trim_suffix("/").get_base_dir()); - update_file_list(); - update_dir(); - _push_history(); -} - -void EditorFileDialog::_go_back() { - if (local_history_pos <= 0) { - return; - } - - local_history_pos--; - dir_access->change_dir(local_history[local_history_pos]); - update_file_list(); - update_dir(); - - dir_prev->set_disabled(local_history_pos == 0); - dir_next->set_disabled(local_history_pos == local_history.size() - 1); -} - -void EditorFileDialog::_go_forward() { - if (local_history_pos >= local_history.size() - 1) { - return; - } - - local_history_pos++; - dir_access->change_dir(local_history[local_history_pos]); - update_file_list(); - update_dir(); - - dir_prev->set_disabled(local_history_pos == 0); - dir_next->set_disabled(local_history_pos == local_history.size() - 1); -} - -void EditorFileDialog::set_display_mode(DisplayMode p_mode) { - if (display_mode == p_mode) { - return; - } - if (p_mode == DISPLAY_THUMBNAILS) { - mode_list->set_pressed(false); - mode_thumbnails->set_pressed(true); - } else { - mode_thumbnails->set_pressed(false); - mode_list->set_pressed(true); - } - display_mode = p_mode; - invalidate(); -} - -EditorFileDialog::DisplayMode EditorFileDialog::get_display_mode() const { - return display_mode; -} - -TypedArray EditorFileDialog::_get_options() const { - TypedArray out; - for (const EditorFileDialog::Option &opt : options) { - Dictionary dict; - dict["name"] = opt.name; - dict["values"] = opt.values; - dict["default"] = (int)selected_options.get(opt.name, opt.default_idx); - out.push_back(dict); - } - return out; -} - -void EditorFileDialog::_option_changed_checkbox_toggled(bool p_pressed, const String &p_name) { - if (selected_options.has(p_name)) { - selected_options[p_name] = p_pressed; - } -} - -void EditorFileDialog::_option_changed_item_selected(int p_idx, const String &p_name) { - if (selected_options.has(p_name)) { - selected_options[p_name] = p_idx; - } -} - -void EditorFileDialog::_update_option_controls() { - if (!options_dirty) { - return; - } - options_dirty = false; - - while (flow_checkbox_options->get_child_count() > 0) { - Node *child = flow_checkbox_options->get_child(0); - flow_checkbox_options->remove_child(child); - child->queue_free(); - } - while (grid_select_options->get_child_count() > 0) { - Node *child = grid_select_options->get_child(0); - grid_select_options->remove_child(child); - child->queue_free(); - } - - selected_options.clear(); - - for (const EditorFileDialog::Option &opt : options) { - if (opt.values.is_empty()) { - CheckBox *cb = memnew(CheckBox); - cb->set_accessibility_name(opt.name); - cb->set_pressed(opt.default_idx); - cb->set_text(opt.name); - flow_checkbox_options->add_child(cb); - cb->connect(SceneStringName(toggled), callable_mp(this, &EditorFileDialog::_option_changed_checkbox_toggled).bind(opt.name)); - selected_options[opt.name] = (bool)opt.default_idx; - } else { - Label *lbl = memnew(Label); - lbl->set_text(opt.name); - grid_select_options->add_child(lbl); - - OptionButton *ob = memnew(OptionButton); - for (const String &val : opt.values) { - ob->add_item(val); - } - ob->set_accessibility_name(opt.name); - ob->select(opt.default_idx); - grid_select_options->add_child(ob); - ob->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_option_changed_item_selected).bind(opt.name)); - selected_options[opt.name] = opt.default_idx; - } - } -} - -Dictionary EditorFileDialog::get_selected_options() const { - return selected_options; -} - -String EditorFileDialog::get_option_name(int p_option) const { - ERR_FAIL_INDEX_V(p_option, options.size(), String()); - return options[p_option].name; -} - -Vector EditorFileDialog::get_option_values(int p_option) const { - ERR_FAIL_INDEX_V(p_option, options.size(), Vector()); - return options[p_option].values; -} - -int EditorFileDialog::get_option_default(int p_option) const { - ERR_FAIL_INDEX_V(p_option, options.size(), -1); - return options[p_option].default_idx; -} - -void EditorFileDialog::set_option_name(int p_option, const String &p_name) { - if (p_option < 0) { - p_option += get_option_count(); - } - ERR_FAIL_INDEX(p_option, options.size()); - options.write[p_option].name = p_name; - options_dirty = true; - if (is_visible()) { - _update_option_controls(); - } -} - -void EditorFileDialog::set_option_values(int p_option, const Vector &p_values) { - if (p_option < 0) { - p_option += get_option_count(); - } - ERR_FAIL_INDEX(p_option, options.size()); - options.write[p_option].values = p_values; - if (p_values.is_empty()) { - options.write[p_option].default_idx = CLAMP(options[p_option].default_idx, 0, 1); - } else { - options.write[p_option].default_idx = CLAMP(options[p_option].default_idx, 0, options[p_option].values.size() - 1); - } - options_dirty = true; - if (is_visible()) { - _update_option_controls(); - } -} - -void EditorFileDialog::set_option_default(int p_option, int p_index) { - if (p_option < 0) { - p_option += get_option_count(); - } - ERR_FAIL_INDEX(p_option, options.size()); - if (options[p_option].values.is_empty()) { - options.write[p_option].default_idx = CLAMP(p_index, 0, 1); - } else { - options.write[p_option].default_idx = CLAMP(p_index, 0, options[p_option].values.size() - 1); - } - options_dirty = true; - if (is_visible()) { - _update_option_controls(); - } -} - -void EditorFileDialog::add_option(const String &p_name, const Vector &p_values, int p_index) { - Option opt; - opt.name = p_name; - opt.values = p_values; - if (opt.values.is_empty()) { - opt.default_idx = CLAMP(p_index, 0, 1); - } else { - opt.default_idx = CLAMP(p_index, 0, opt.values.size() - 1); - } - options.push_back(opt); - options_dirty = true; - if (is_visible()) { - _update_option_controls(); - } -} - -void EditorFileDialog::set_option_count(int p_count) { - ERR_FAIL_COND(p_count < 0); - - if (options.size() == p_count) { - return; - } - options.resize(p_count); - - options_dirty = true; - notify_property_list_changed(); - if (is_visible()) { - _update_option_controls(); - } -} - -int EditorFileDialog::get_option_count() const { - return options.size(); -} - -void EditorFileDialog::_bind_methods() { - ClassDB::bind_method(D_METHOD("_cancel_pressed"), &EditorFileDialog::_cancel_pressed); - - ClassDB::bind_method(D_METHOD("clear_filters"), &EditorFileDialog::clear_filters); - ClassDB::bind_method(D_METHOD("add_filter", "filter", "description"), &EditorFileDialog::add_filter, DEFVAL("")); - ClassDB::bind_method(D_METHOD("set_filters", "filters"), &EditorFileDialog::set_filters); - ClassDB::bind_method(D_METHOD("get_filters"), &EditorFileDialog::get_filters); - ClassDB::bind_method(D_METHOD("get_option_name", "option"), &EditorFileDialog::get_option_name); - ClassDB::bind_method(D_METHOD("get_option_values", "option"), &EditorFileDialog::get_option_values); - ClassDB::bind_method(D_METHOD("get_option_default", "option"), &EditorFileDialog::get_option_default); - ClassDB::bind_method(D_METHOD("set_option_name", "option", "name"), &EditorFileDialog::set_option_name); - ClassDB::bind_method(D_METHOD("set_option_values", "option", "values"), &EditorFileDialog::set_option_values); - ClassDB::bind_method(D_METHOD("set_option_default", "option", "default_value_index"), &EditorFileDialog::set_option_default); - ClassDB::bind_method(D_METHOD("set_option_count", "count"), &EditorFileDialog::set_option_count); - ClassDB::bind_method(D_METHOD("get_option_count"), &EditorFileDialog::get_option_count); - ClassDB::bind_method(D_METHOD("add_option", "name", "values", "default_value_index"), &EditorFileDialog::add_option); - ClassDB::bind_method(D_METHOD("get_selected_options"), &EditorFileDialog::get_selected_options); - ClassDB::bind_method(D_METHOD("clear_filename_filter"), &EditorFileDialog::clear_search_filter); - ClassDB::bind_method(D_METHOD("set_filename_filter", "filter"), &EditorFileDialog::set_search_filter); - ClassDB::bind_method(D_METHOD("get_filename_filter"), &EditorFileDialog::get_search_filter); - ClassDB::bind_method(D_METHOD("get_current_dir"), &EditorFileDialog::get_current_dir); - ClassDB::bind_method(D_METHOD("get_current_file"), &EditorFileDialog::get_current_file); - ClassDB::bind_method(D_METHOD("get_current_path"), &EditorFileDialog::get_current_path); - ClassDB::bind_method(D_METHOD("set_current_dir", "dir"), &EditorFileDialog::set_current_dir); - ClassDB::bind_method(D_METHOD("set_current_file", "file"), &EditorFileDialog::set_current_file); - ClassDB::bind_method(D_METHOD("set_current_path", "path"), &EditorFileDialog::set_current_path); - ClassDB::bind_method(D_METHOD("set_file_mode", "mode"), &EditorFileDialog::set_file_mode); - ClassDB::bind_method(D_METHOD("get_file_mode"), &EditorFileDialog::get_file_mode); - ClassDB::bind_method(D_METHOD("get_vbox"), &EditorFileDialog::get_vbox); - ClassDB::bind_method(D_METHOD("get_line_edit"), &EditorFileDialog::get_line_edit); - ClassDB::bind_method(D_METHOD("set_access", "access"), &EditorFileDialog::set_access); - ClassDB::bind_method(D_METHOD("get_access"), &EditorFileDialog::get_access); - ClassDB::bind_method(D_METHOD("set_show_hidden_files", "show"), &EditorFileDialog::set_show_hidden_files); - ClassDB::bind_method(D_METHOD("is_showing_hidden_files"), &EditorFileDialog::is_showing_hidden_files); - ClassDB::bind_method(D_METHOD("set_display_mode", "mode"), &EditorFileDialog::set_display_mode); - ClassDB::bind_method(D_METHOD("get_display_mode"), &EditorFileDialog::get_display_mode); - ClassDB::bind_method(D_METHOD("set_disable_overwrite_warning", "disable"), &EditorFileDialog::set_disable_overwrite_warning); - ClassDB::bind_method(D_METHOD("is_overwrite_warning_disabled"), &EditorFileDialog::is_overwrite_warning_disabled); - ClassDB::bind_method(D_METHOD("popup_file_dialog"), &EditorFileDialog::popup_file_dialog); -#ifndef DISABLE_DEPRECATED - ClassDB::bind_method(D_METHOD("add_side_menu", "menu", "title"), &EditorFileDialog::add_side_menu, DEFVAL("")); -#endif - - ClassDB::bind_method(D_METHOD("invalidate"), &EditorFileDialog::invalidate); - - ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path"))); - ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths"))); - ADD_SIGNAL(MethodInfo("dir_selected", PropertyInfo(Variant::STRING, "dir"))); - ADD_SIGNAL(MethodInfo("filename_filter_changed", PropertyInfo(Variant::STRING, "filter"))); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"), "set_display_mode", "get_display_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "file_mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"), "set_file_mode", "get_file_mode"); - 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_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_path", "get_current_path"); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "filters"), "set_filters", "get_filters"); - ADD_ARRAY_COUNT("Options", "option_count", "set_option_count", "get_option_count", "option_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_overwrite_warning"), "set_disable_overwrite_warning", "is_overwrite_warning_disabled"); - - BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILE); - BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILES); - BIND_ENUM_CONSTANT(FILE_MODE_OPEN_DIR); - BIND_ENUM_CONSTANT(FILE_MODE_OPEN_ANY); - BIND_ENUM_CONSTANT(FILE_MODE_SAVE_FILE); - - BIND_ENUM_CONSTANT(ACCESS_RESOURCES); - BIND_ENUM_CONSTANT(ACCESS_USERDATA); - BIND_ENUM_CONSTANT(ACCESS_FILESYSTEM); - - BIND_ENUM_CONSTANT(DISPLAY_THUMBNAILS); - BIND_ENUM_CONSTANT(DISPLAY_LIST); - - Option defaults; - - base_property_helper.set_prefix("option_"); - base_property_helper.set_array_length_getter(&EditorFileDialog::get_option_count); - base_property_helper.register_property(PropertyInfo(Variant::STRING, "name"), defaults.name, &EditorFileDialog::set_option_name, &EditorFileDialog::get_option_name); - base_property_helper.register_property(PropertyInfo(Variant::PACKED_STRING_ARRAY, "values"), defaults.values, &EditorFileDialog::set_option_values, &EditorFileDialog::get_option_values); - base_property_helper.register_property(PropertyInfo(Variant::INT, "default"), defaults.default_idx, &EditorFileDialog::set_option_default, &EditorFileDialog::get_option_default); - PropertyListHelper::register_base_helper(&base_property_helper); -} - -void EditorFileDialog::set_show_hidden_files(bool p_show) { - if (p_show == show_hidden_files) { - return; - } - - EditorSettings::get_singleton()->set("filesystem/file_dialog/show_hidden_files", p_show); - show_hidden_files = p_show; - show_hidden->set_pressed(p_show); - invalidate(); -} - -void EditorFileDialog::set_show_search_filter(bool p_show) { - if (p_show == show_search_filter) { - return; - } - if (p_show) { - filter_box->grab_focus(); - } else { - search_string.clear(); - filter_box->clear(); - if (filter_box->has_focus()) { - item_list->call_deferred("grab_focus"); - } - } - show_search_filter = p_show; - update_search_filter_gui(); - invalidate(); -} - -bool EditorFileDialog::is_showing_hidden_files() const { - return show_hidden_files; -} - -void EditorFileDialog::set_default_show_hidden_files(bool p_show) { - default_show_hidden_files = p_show; -} - -void EditorFileDialog::set_default_display_mode(DisplayMode p_mode) { - default_display_mode = p_mode; -} - -void EditorFileDialog::_save_to_recent() { - String cur_dir = get_current_dir(); - Vector recent_new = EditorSettings::get_singleton()->get_recent_dirs(); - - const int max = 20; - int count = 0; - bool res = cur_dir.begins_with("res://"); - - for (int i = 0; i < recent_new.size(); i++) { - bool cres = recent_new[i].begins_with("res://"); - if (recent_new[i] == cur_dir || (res == cres && count > max)) { - recent_new.remove_at(i); - i--; - } else { - count++; - } - } - - recent_new.insert(0, cur_dir); - - EditorSettings::get_singleton()->set_recent_dirs(recent_new); -} - -void EditorFileDialog::set_disable_overwrite_warning(bool p_disable) { - disable_overwrite_warning = p_disable; -} - -bool EditorFileDialog::is_overwrite_warning_disabled() const { - return disable_overwrite_warning; -} - -void EditorFileDialog::set_previews_enabled(bool p_enabled) { - previews_enabled = p_enabled; -} - -bool EditorFileDialog::are_previews_enabled() { - return previews_enabled; -} - -EditorFileDialog::EditorFileDialog() { - show_hidden_files = default_show_hidden_files; - display_mode = default_display_mode; - - vbc = memnew(VBoxContainer); - add_child(vbc); - - set_title(TTRC("Save a File")); - - ED_SHORTCUT("file_dialog/go_back", TTRC("Go Back"), KeyModifierMask::ALT | Key::LEFT); - ED_SHORTCUT("file_dialog/go_forward", TTRC("Go Forward"), KeyModifierMask::ALT | Key::RIGHT); - ED_SHORTCUT("file_dialog/go_up", TTRC("Go Up"), KeyModifierMask::ALT | Key::UP); - ED_SHORTCUT("file_dialog/refresh", TTRC("Refresh"), Key::F5); - ED_SHORTCUT("file_dialog/toggle_hidden_files", TTRC("Toggle Hidden Files"), KeyModifierMask::CTRL | Key::H); - ED_SHORTCUT("file_dialog/toggle_favorite", TTRC("Toggle Favorite"), KeyModifierMask::ALT | Key::F); - ED_SHORTCUT("file_dialog/toggle_mode", TTRC("Toggle Mode"), KeyModifierMask::ALT | Key::V); - ED_SHORTCUT("file_dialog/create_folder", TTRC("Create Folder"), KeyModifierMask::CMD_OR_CTRL | Key::N); - ED_SHORTCUT("file_dialog/delete", TTRC("Delete"), Key::KEY_DELETE); - ED_SHORTCUT("file_dialog/focus_path", TTRC("Focus Path"), KeyModifierMask::CMD_OR_CTRL | Key::L); - // Allow both Cmd + L and Cmd + Shift + G to match Safari's and Finder's shortcuts respectively. - ED_SHORTCUT_OVERRIDE_ARRAY("file_dialog/focus_path", "macos", - { int32_t(KeyModifierMask::META | Key::L), int32_t(KeyModifierMask::META | KeyModifierMask::SHIFT | Key::G) }); - ED_SHORTCUT("file_dialog/focus_filter", TTRC("Focus Filter"), KeyModifierMask::CMD_OR_CTRL | Key::F); - ED_SHORTCUT("file_dialog/move_favorite_up", TTRC("Move Favorite Up"), KeyModifierMask::CMD_OR_CTRL | Key::UP); - ED_SHORTCUT("file_dialog/move_favorite_down", TTRC("Move Favorite Down"), KeyModifierMask::CMD_OR_CTRL | Key::DOWN); - - ED_SHORTCUT_OVERRIDE("file_dialog/toggle_hidden_files", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::PERIOD); - ED_SHORTCUT_OVERRIDE("file_dialog/toggle_favorite", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::F); - ED_SHORTCUT_OVERRIDE("file_dialog/toggle_mode", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::V); - - pathhb = memnew(HBoxContainer); - vbc->add_child(pathhb); - - dir_prev = memnew(Button); - dir_prev->set_theme_type_variation(SceneStringName(FlatButton)); - dir_prev->set_tooltip_text(TTRC("Go to previous folder.")); - dir_next = memnew(Button); - dir_next->set_theme_type_variation(SceneStringName(FlatButton)); - dir_next->set_tooltip_text(TTRC("Go to next folder.")); - dir_up = memnew(Button); - dir_up->set_theme_type_variation(SceneStringName(FlatButton)); - dir_up->set_tooltip_text(TTRC("Go to parent folder.")); - - pathhb->add_child(dir_prev); - pathhb->add_child(dir_next); - pathhb->add_child(dir_up); - - dir_prev->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::_go_back)); - dir_next->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::_go_forward)); - dir_up->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::_go_up)); - - Label *l = memnew(Label(TTRC("Path:"))); - l->set_theme_type_variation("HeaderSmall"); - pathhb->add_child(l); - - drives_container = memnew(HBoxContainer); - pathhb->add_child(drives_container); - - dir = memnew(LineEdit); - dir->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE); - dir->set_accessibility_name(TTRC("Path:")); - pathhb->add_child(dir); - - refresh = memnew(Button); - refresh->set_theme_type_variation(SceneStringName(FlatButton)); - refresh->set_tooltip_text(TTRC("Refresh files.")); - refresh->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::update_file_list)); - pathhb->add_child(refresh); - - favorite = memnew(Button); - favorite->set_theme_type_variation(SceneStringName(FlatButton)); - favorite->set_toggle_mode(true); - favorite->set_tooltip_text(TTRC("(Un)favorite current folder.")); - favorite->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::_favorite_pressed)); - pathhb->add_child(favorite); - - shortcuts_container = memnew(HBoxContainer); - pathhb->add_child(shortcuts_container); - - drives = memnew(OptionButton); - drives->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_select_drive)); - drives->set_accessibility_name(TTRC("Current Drive")); - pathhb->add_child(drives); - - makedir_sep = memnew(VSeparator); - pathhb->add_child(makedir_sep); - - makedir = memnew(Button); - makedir->set_theme_type_variation(SceneStringName(FlatButton)); - makedir->set_tooltip_text(TTRC("Create a new folder.")); - makedir->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::_make_dir)); - pathhb->add_child(makedir); - - dir->set_h_size_flags(Control::SIZE_EXPAND_FILL); - - body_hsplit = memnew(HSplitContainer); - body_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL); - vbc->add_child(body_hsplit); - - flow_checkbox_options = memnew(HFlowContainer); - flow_checkbox_options->set_h_size_flags(Control::SIZE_EXPAND_FILL); - flow_checkbox_options->set_alignment(FlowContainer::ALIGNMENT_CENTER); - vbc->add_child(flow_checkbox_options); - - grid_select_options = memnew(GridContainer); - grid_select_options->set_h_size_flags(Control::SIZE_SHRINK_CENTER); - grid_select_options->set_columns(2); - vbc->add_child(grid_select_options); - - list_hb = memnew(HSplitContainer); - list_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); - body_hsplit->add_child(list_hb); - - VSplitContainer *vsc = memnew(VSplitContainer); - list_hb->add_child(vsc); - - VBoxContainer *fav_vb = memnew(VBoxContainer); - vsc->add_child(fav_vb); - fav_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE); - fav_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL); - HBoxContainer *fav_hb = memnew(HBoxContainer); - fav_vb->add_child(fav_hb); - - l = memnew(Label(TTRC("Favorites:"))); - l->set_theme_type_variation("HeaderSmall"); - fav_hb->add_child(l); - - fav_hb->add_spacer(); - fav_up = memnew(Button); - fav_up->set_theme_type_variation(SceneStringName(FlatButton)); - fav_up->set_accessibility_name(TTRC("Move Up")); - fav_hb->add_child(fav_up); - fav_up->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::_favorite_move_up)); - fav_down = memnew(Button); - fav_down->set_theme_type_variation(SceneStringName(FlatButton)); - fav_down->set_accessibility_name(TTRC("Move Down")); - fav_hb->add_child(fav_down); - fav_down->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::_favorite_move_down)); - - favorites = memnew(ItemList); - favorites->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); - fav_vb->add_child(favorites); - favorites->set_v_size_flags(Control::SIZE_EXPAND_FILL); - favorites->set_theme_type_variation("ItemListSecondary"); - favorites->set_accessibility_name(TTRC("Favorites:")); - favorites->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_favorite_selected)); - - VBoxContainer *rec_vb = memnew(VBoxContainer); - vsc->add_child(rec_vb); - rec_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE); - rec_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL); - recent = memnew(ItemList); - recent->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); - recent->set_allow_reselect(true); - recent->set_theme_type_variation("ItemListSecondary"); - recent->set_accessibility_name(TTRC("Recent:")); - rec_vb->add_margin_child(TTRC("Recent:"), recent, true); - recent->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_recent_selected)); - - VBoxContainer *item_vb = memnew(VBoxContainer); - list_hb->add_child(item_vb); - item_vb->set_custom_minimum_size(Size2(320, 0) * EDSCALE); - - HBoxContainer *preview_hb = memnew(HBoxContainer); - preview_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL); - item_vb->add_child(preview_hb); - - VBoxContainer *list_vb = memnew(VBoxContainer); - list_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); - - HBoxContainer *lower_hb = memnew(HBoxContainer); - lower_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); - - l = memnew(Label(TTRC("Directories & Files:"))); - l->set_theme_type_variation("HeaderSmall"); - l->set_h_size_flags(Control::SIZE_EXPAND_FILL); - - lower_hb->add_child(l); - - show_hidden = memnew(Button); - show_hidden->set_theme_type_variation(SceneStringName(FlatButton)); - show_hidden->set_toggle_mode(true); - show_hidden->set_pressed(is_showing_hidden_files()); - show_hidden->set_tooltip_text(TTRC("Toggle the visibility of hidden files.")); - show_hidden->connect(SceneStringName(toggled), callable_mp(this, &EditorFileDialog::set_show_hidden_files)); - lower_hb->add_child(show_hidden); - - lower_hb->add_child(memnew(VSeparator)); - - Ref view_mode_group; - view_mode_group.instantiate(); - - mode_thumbnails = memnew(Button); - mode_thumbnails->set_theme_type_variation(SceneStringName(FlatButton)); - mode_thumbnails->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::set_display_mode).bind(DISPLAY_THUMBNAILS)); - mode_thumbnails->set_toggle_mode(true); - mode_thumbnails->set_pressed(display_mode == DISPLAY_THUMBNAILS); - mode_thumbnails->set_button_group(view_mode_group); - mode_thumbnails->set_tooltip_text(TTRC("View items as a grid of thumbnails.")); - lower_hb->add_child(mode_thumbnails); - - mode_list = memnew(Button); - mode_list->set_theme_type_variation(SceneStringName(FlatButton)); - mode_list->connect(SceneStringName(pressed), callable_mp(this, &EditorFileDialog::set_display_mode).bind(DISPLAY_LIST)); - mode_list->set_toggle_mode(true); - mode_list->set_pressed(display_mode == DISPLAY_LIST); - mode_list->set_button_group(view_mode_group); - mode_list->set_tooltip_text(TTRC("View items as a list.")); - lower_hb->add_child(mode_list); - - lower_hb->add_child(memnew(VSeparator)); - - file_sort_button = memnew(MenuButton); - file_sort_button->set_flat(false); - file_sort_button->set_theme_type_variation("FlatMenuButton"); - file_sort_button->set_tooltip_text(TTRC("Sort files")); - - show_search_filter_button = memnew(Button); - show_search_filter_button->set_theme_type_variation(SceneStringName(FlatButton)); - show_search_filter_button->set_toggle_mode(true); - show_search_filter_button->set_pressed(false); - show_search_filter_button->set_tooltip_text(TTRC("Toggle the visibility of the filter for file names.")); - show_search_filter_button->connect(SceneStringName(toggled), callable_mp(this, &EditorFileDialog::set_show_search_filter)); - lower_hb->add_child(show_search_filter_button); - - PopupMenu *p = file_sort_button->get_popup(); - p->connect(SceneStringName(id_pressed), callable_mp(this, &EditorFileDialog::_file_sort_popup)); - p->add_radio_check_item(TTRC("Sort by Name (Ascending)"), static_cast(FileSortOption::FILE_SORT_NAME)); - p->add_radio_check_item(TTRC("Sort by Name (Descending)"), static_cast(FileSortOption::FILE_SORT_NAME_REVERSE)); - p->add_radio_check_item(TTRC("Sort by Type (Ascending)"), static_cast(FileSortOption::FILE_SORT_TYPE)); - p->add_radio_check_item(TTRC("Sort by Type (Descending)"), static_cast(FileSortOption::FILE_SORT_TYPE_REVERSE)); - p->add_radio_check_item(TTRC("Sort by Last Modified"), static_cast(FileSortOption::FILE_SORT_MODIFIED_TIME)); - p->add_radio_check_item(TTRC("Sort by First Modified"), static_cast(FileSortOption::FILE_SORT_MODIFIED_TIME_REVERSE)); - p->set_item_checked(0, true); - lower_hb->add_child(file_sort_button); - - list_vb->add_child(lower_hb); - preview_hb->add_child(list_vb); - - // Item (files and folders) list with context menu. - - item_list = memnew(ItemList); - item_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); - item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); - item_list->connect("item_clicked", callable_mp(this, &EditorFileDialog::_item_list_item_rmb_clicked)); - item_list->connect("empty_clicked", callable_mp(this, &EditorFileDialog::_item_list_empty_clicked)); - item_list->set_allow_rmb_select(true); - item_list->set_accessibility_name(TTRC("Directories & Files:")); - - list_vb->add_child(item_list); - - item_menu = memnew(PopupMenu); - item_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorFileDialog::_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); - preview_vb->add_margin_child(TTRC("Preview:"), prev_cc); - preview = memnew(TextureRect); - prev_cc->add_child(preview); - preview_vb->hide(); - - filter_hb = memnew(HBoxContainer); - filter_hb->add_child(memnew(Label(RTR("Filter:")))); - filter_box = memnew(LineEdit); - filter_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); - filter_box->set_placeholder(TTRC("Filter")); - filter_box->set_accessibility_name(TTRC("Filename Filter:")); - filter_hb->add_child(filter_box); - filter_hb->set_visible(false); - item_vb->add_child(filter_hb); - - file_box = memnew(HBoxContainer); - - l = memnew(Label(TTRC("File:"))); - l->set_theme_type_variation("HeaderSmall"); - file_box->add_child(l); - - file = memnew(LineEdit); - file->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE); - file->set_stretch_ratio(4); - file->set_h_size_flags(Control::SIZE_EXPAND_FILL); - file->set_accessibility_name(TTRC("File:")); - file_box->add_child(file); - filter = memnew(OptionButton); - filter->set_stretch_ratio(3); - filter->set_h_size_flags(Control::SIZE_EXPAND_FILL); - filter->set_clip_text(true); // Too many extensions overflow it. - filter->set_accessibility_name(TTRC("File Type Filter")); - file_box->add_child(filter); - file_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); - item_vb->add_child(file_box); - - dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); - _update_drives(); - - connect(SceneStringName(confirmed), callable_mp(this, &EditorFileDialog::_action_pressed)); - item_list->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_item_selected), CONNECT_DEFERRED); - item_list->connect("multi_selected", callable_mp(this, &EditorFileDialog::_multi_selected), CONNECT_DEFERRED); - item_list->connect("item_activated", callable_mp(this, &EditorFileDialog::_item_dc_selected).bind()); - item_list->connect("empty_clicked", callable_mp(this, &EditorFileDialog::_items_clear_selection)); - dir->connect(SceneStringName(text_submitted), callable_mp(this, &EditorFileDialog::_dir_submitted)); - filter_box->connect(SceneStringName(text_changed), callable_mp(this, &EditorFileDialog::_filter_changed)); - filter_box->connect(SceneStringName(text_submitted), callable_mp(this, &EditorFileDialog::_search_filter_selected).unbind(1)); - file->connect(SceneStringName(text_submitted), callable_mp(this, &EditorFileDialog::_file_submitted)); - filter->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_filter_selected)); - - confirm_save = memnew(ConfirmationDialog); - add_child(confirm_save); - confirm_save->connect(SceneStringName(confirmed), callable_mp(this, &EditorFileDialog::_save_confirm_pressed)); - - dep_remove_dialog = memnew(DependencyRemoveDialog); - add_child(dep_remove_dialog); - - global_remove_dialog = memnew(ConfirmationDialog); - global_remove_dialog->set_text(TTRC("Remove the selected files? For safety only files and empty directories can be deleted from here. (Cannot be undone.)\nDepending on your filesystem configuration, the files will either be moved to the system trash or deleted permanently.")); - global_remove_dialog->connect(SceneStringName(confirmed), callable_mp(this, &EditorFileDialog::_delete_files_global)); - add_child(global_remove_dialog); - - makedialog = memnew(ConfirmationDialog); - makedialog->set_title(TTRC("Create Folder")); - VBoxContainer *makevb = memnew(VBoxContainer); - makedialog->add_child(makevb); - - makedirname = memnew(LineEdit); - makedirname->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE); - makedirname->set_accessibility_name(TTRC("Name:")); - makevb->add_margin_child(TTRC("Name:"), makedirname); - add_child(makedialog); - makedialog->register_text_enter(makedirname); - makedialog->connect(SceneStringName(confirmed), callable_mp(this, &EditorFileDialog::_make_dir_confirm)); - error_dialog = memnew(AcceptDialog); - add_child(error_dialog); - - update_filters(); - update_dir(); - - set_hide_on_ok(false); - vbox = vbc; - - if (register_func) { - register_func(this); - } - - property_helper.setup_for_instance(base_property_helper, this); -} - -EditorFileDialog::~EditorFileDialog() { - if (unregister_func) { - unregister_func(this); - } -} diff --git a/editor/gui/editor_file_dialog.h b/editor/gui/editor_file_dialog.h index ea7cf1edb67..b8e21974520 100644 --- a/editor/gui/editor_file_dialog.h +++ b/editor/gui/editor_file_dialog.h @@ -30,348 +30,30 @@ #pragma once -#include "core/io/dir_access.h" -#include "editor/file_system/file_info.h" -#include "scene/gui/dialogs.h" -#include "scene/property_list_helper.h" +#include "scene/gui/file_dialog.h" class DependencyRemoveDialog; -class GridContainer; -class HSplitContainer; -class HFlowContainer; -class ItemList; -class MenuButton; -class OptionButton; -class PopupMenu; -class TextureRect; -class VSeparator; -class EditorFileDialog : public ConfirmationDialog { - GDCLASS(EditorFileDialog, ConfirmationDialog); +class EditorFileDialog : public FileDialog { + GDCLASS(EditorFileDialog, FileDialog); -public: - enum DisplayMode { - DISPLAY_THUMBNAILS, - DISPLAY_LIST - }; - - enum Access { - ACCESS_RESOURCES, - ACCESS_USERDATA, - ACCESS_FILESYSTEM - }; - - enum FileMode { - FILE_MODE_OPEN_FILE, - FILE_MODE_OPEN_FILES, - FILE_MODE_OPEN_DIR, - FILE_MODE_OPEN_ANY, - FILE_MODE_SAVE_FILE - }; - - typedef Ref (*GetIconFunc)(const String &); - typedef void (*RegisterFunc)(EditorFileDialog *); - - static inline GetIconFunc get_icon_func = nullptr; - static inline GetIconFunc get_thumbnail_func = nullptr; - static inline RegisterFunc register_func = nullptr; - static inline RegisterFunc unregister_func = nullptr; - -private: - 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, - }; - - ConfirmationDialog *makedialog = nullptr; - LineEdit *makedirname = nullptr; - - VSeparator *makedir_sep = nullptr; - Button *makedir = nullptr; - Access access = ACCESS_RESOURCES; - - HFlowContainer *flow_checkbox_options = nullptr; - GridContainer *grid_select_options = nullptr; - VBoxContainer *vbox = nullptr; - FileMode mode = FILE_MODE_SAVE_FILE; - bool can_create_dir = false; - LineEdit *dir = nullptr; - - Button *dir_prev = nullptr; - Button *dir_next = nullptr; - Button *dir_up = nullptr; - - HBoxContainer *drives_container = nullptr; - HBoxContainer *shortcuts_container = nullptr; - OptionButton *drives = nullptr; - ItemList *item_list = nullptr; - PopupMenu *item_menu = nullptr; - TextureRect *preview = nullptr; - VBoxContainer *preview_vb = nullptr; - HSplitContainer *body_hsplit = nullptr; - HSplitContainer *list_hb = nullptr; - HBoxContainer *file_box = nullptr; - LineEdit *file = nullptr; - OptionButton *filter = nullptr; - AcceptDialog *error_dialog = nullptr; - Ref dir_access; - ConfirmationDialog *confirm_save = nullptr; - DependencyRemoveDialog *dep_remove_dialog = nullptr; - ConfirmationDialog *global_remove_dialog = nullptr; - VBoxContainer *vbc = nullptr; - HBoxContainer *pathhb = nullptr; - - Button *mode_thumbnails = nullptr; - Button *mode_list = nullptr; - - Button *refresh = nullptr; - Button *favorite = nullptr; - Button *show_hidden = nullptr; - Button *show_search_filter_button = nullptr; - - String search_string; - bool show_search_filter = false; - HBoxContainer *filter_hb = nullptr; - LineEdit *filter_box = nullptr; - FileSortOption file_sort = FileSortOption::FILE_SORT_NAME; - MenuButton *file_sort_button = nullptr; - - Button *fav_up = nullptr; - Button *fav_down = nullptr; - - ItemList *favorites = nullptr; - ItemList *recent = nullptr; - - Vector local_history; - int local_history_pos = 0; - void _push_history(); - - Vector filters; - Vector processed_filters; - - bool previews_enabled = true; - bool preview_waiting = false; - int preview_wheel_index = 0; - float preview_wheel_timeout = 0.0f; - - static inline bool default_show_hidden_files = false; - static inline DisplayMode default_display_mode = DISPLAY_THUMBNAILS; - bool show_hidden_files; - DisplayMode display_mode; - - bool disable_overwrite_warning = false; - bool is_invalidating = false; - - struct ThemeCache { - Ref parent_folder; - Ref forward_folder; - Ref back_folder; - Ref open_folder; - Ref reload; - Ref toggle_hidden; - Ref toggle_filename_filter; - Ref favorite; - Ref mode_thumbnails; - Ref mode_list; - Ref create_folder; - Ref favorites_up; - Ref favorites_down; - - Ref filter_box; - Ref file_sort_button; - - Ref file; - Ref folder; - Color folder_icon_color; - - Ref action_copy; - Ref action_delete; - Ref filesystem; - - Ref folder_medium_thumbnail; - Ref file_medium_thumbnail; - Ref folder_big_thumbnail; - Ref file_big_thumbnail; - - Ref progress[8]{}; - } theme_cache; - - struct Option { - String name; - Vector values; - int default_idx = 0; - }; - - static inline PropertyListHelper base_property_helper; - PropertyListHelper property_helper; - - Vector