diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml
index b51341dc24c..4befcf5e697 100644
--- a/doc/classes/EditorFileDialog.xml
+++ b/doc/classes/EditorFileDialog.xml
@@ -19,6 +19,16 @@
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).
+
+
@@ -40,6 +50,33 @@
[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.
+
+
@@ -53,6 +90,30 @@
Notify the [EditorFileDialog] that its view of the data is no longer accurate. Updates the view contents on next view update.
+
+
+
+
+
+ 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].
+
+
@@ -80,6 +141,9 @@
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].
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 48ed191db17..00d0e3cb9c4 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -714,6 +714,9 @@
If [code]true[/code], editor main menu is using embedded [MenuBar] instead of system global menu.
Specific to the macOS platform.
+
+ If [code]true[/code], editor UI uses OS native file/directory selection dialogs.
+
Sets the V-Sync mode for the editor. Does not affect the project when run from the editor (this is controlled by [member ProjectSettings.display/window/vsync/vsync_mode]).
Depending on the platform and used renderer, the engine will fall back to [b]Enabled[/b] if the desired mode is not supported.
diff --git a/doc/classes/FileDialog.xml b/doc/classes/FileDialog.xml
index 9065adc0e06..dec3160ffed 100644
--- a/doc/classes/FileDialog.xml
+++ b/doc/classes/FileDialog.xml
@@ -23,9 +23,10 @@
-
+
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).
@@ -90,7 +91,7 @@
-
+
Sets the default value index of the [OptionButton] or [CheckBox] with index [param option].
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index edb6fee75c9..b7a0045dfd8 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2066,8 +2066,9 @@ void EditorNode::_dialog_action(String p_file) {
} break;
case FILE_EXPORT_MESH_LIBRARY: {
- bool merge_with_existing_library = file_export_lib_merge->is_pressed();
- bool apply_mesh_instance_transforms = file_export_lib_apply_xforms->is_pressed();
+ const Dictionary &fd_options = file_export_lib->get_selected_options();
+ bool merge_with_existing_library = fd_options.get(TTR("Merge With Existing"), true);
+ bool apply_mesh_instance_transforms = fd_options.get(TTR("Apply MeshInstance Transforms"), false);
Ref ml;
if (merge_with_existing_library && FileAccess::exists(p_file)) {
@@ -2723,8 +2724,8 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
root_name = EditorNode::adjust_scene_name_casing(root_name);
file->set_current_path(root_name + "." + extensions.front()->get().to_lower());
}
- file->popup_file_dialog();
file->set_title(TTR("Save Scene As..."));
+ file->popup_file_dialog();
} break;
@@ -3238,8 +3239,8 @@ void EditorNode::_export_as_menu_option(int p_idx) {
file_export_lib->add_filter("*." + E);
}
- file_export_lib->popup_file_dialog();
file_export_lib->set_title(TTR("Export Mesh Library"));
+ file_export_lib->popup_file_dialog();
} else { // Custom menu options added by plugins
if (export_as_menu->get_item_submenu(p_idx).is_empty()) { // If not a submenu
Callable callback = export_as_menu->get_item_metadata(p_idx);
@@ -7128,16 +7129,8 @@ EditorNode::EditorNode() {
file_export_lib->set_title(TTR("Export Library"));
file_export_lib->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
file_export_lib->connect("file_selected", callable_mp(this, &EditorNode::_dialog_action));
- file_export_lib_merge = memnew(CheckBox);
- file_export_lib_merge->set_text(TTR("Merge With Existing"));
- file_export_lib_merge->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
- file_export_lib_merge->set_pressed(true);
- file_export_lib->get_vbox()->add_child(file_export_lib_merge);
- file_export_lib_apply_xforms = memnew(CheckBox);
- file_export_lib_apply_xforms->set_text(TTR("Apply MeshInstance Transforms"));
- file_export_lib_apply_xforms->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
- file_export_lib_apply_xforms->set_pressed(false);
- file_export_lib->get_vbox()->add_child(file_export_lib_apply_xforms);
+ file_export_lib->add_option(TTR("Merge With Existing"), Vector(), true);
+ file_export_lib->add_option(TTR("Apply MeshInstance Transforms"), Vector(), false);
gui_base->add_child(file_export_lib);
file_script = memnew(EditorFileDialog);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index b4796266486..9acd19a15ce 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -399,8 +399,6 @@ private:
EditorFileDialog *file_export_lib = nullptr;
EditorFileDialog *file_script = nullptr;
EditorFileDialog *file_android_build_source = nullptr;
- CheckBox *file_export_lib_merge = nullptr;
- CheckBox *file_export_lib_apply_xforms = nullptr;
String current_path;
MenuButton *update_spinner = nullptr;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index ee33e171e3e..01e3ee41178 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -417,6 +417,7 @@ void EditorSettings::_load_defaults(Ref p_extra_config) {
set_restart_if_changed("interface/editor/debug/enable_pseudolocalization", true);
// Use pseudolocalization in editor.
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/use_embedded_menu", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+ EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/use_native_file_dialogs", false, "", PROPERTY_USAGE_DEFAULT)
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/expand_to_title", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/editor/main_font_size", 14, "8,48,1")
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 536e7a0f046..038e357ce27 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -1035,10 +1035,13 @@ void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) {
Ref platform = current->get_platform();
ERR_FAIL_COND(platform.is_null());
+ const Dictionary &fd_option = export_pck_zip->get_selected_options();
+ bool export_debug = fd_option.get(TTR("Export With Debug"), true);
+
if (p_path.ends_with(".zip")) {
- platform->export_zip(current, export_pck_zip_debug->is_pressed(), p_path);
+ platform->export_zip(current, export_debug, p_path);
} else if (p_path.ends_with(".pck")) {
- platform->export_pack(current, export_pck_zip_debug->is_pressed(), p_path);
+ platform->export_pack(current, export_debug, p_path);
} else {
ERR_FAIL_MSG("Path must end with .pck or .zip");
}
@@ -1123,7 +1126,10 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) {
platform->clear_messages();
current->update_value_overrides();
- Error err = platform->export_project(current, export_debug->is_pressed(), current->get_export_path(), 0);
+ Dictionary fd_option = export_project->get_selected_options();
+ bool export_debug = fd_option.get(TTR("Export With Debug"), true);
+
+ Error err = platform->export_project(current, export_debug, current->get_export_path(), 0);
result_dialog_log->clear();
if (err != ERR_SKIP) {
if (platform->fill_log_messages(result_dialog_log, err)) {
@@ -1552,17 +1558,8 @@ ProjectExportDialog::ProjectExportDialog() {
export_project->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_project_to_path));
export_project->get_line_edit()->connect("text_changed", callable_mp(this, &ProjectExportDialog::_validate_export_path));
- export_debug = memnew(CheckBox);
- export_debug->set_text(TTR("Export With Debug"));
- export_debug->set_pressed(true);
- export_debug->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
- export_project->get_vbox()->add_child(export_debug);
-
- export_pck_zip_debug = memnew(CheckBox);
- export_pck_zip_debug->set_text(TTR("Export With Debug"));
- export_pck_zip_debug->set_pressed(true);
- export_pck_zip_debug->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
- export_pck_zip->get_vbox()->add_child(export_pck_zip_debug);
+ export_project->add_option(TTR("Export With Debug"), Vector(), true);
+ export_pck_zip->add_option(TTR("Export With Debug"), Vector(), true);
set_hide_on_ok(false);
diff --git a/editor/export/project_export.h b/editor/export/project_export.h
index bcab05cebb9..c3499177f31 100644
--- a/editor/export/project_export.h
+++ b/editor/export/project_export.h
@@ -154,8 +154,6 @@ class ProjectExportDialog : public ConfirmationDialog {
EditorFileDialog *export_pck_zip = nullptr;
EditorFileDialog *export_project = nullptr;
- CheckBox *export_debug = nullptr;
- CheckBox *export_pck_zip_debug = nullptr;
CheckButton *enc_pck = nullptr;
CheckButton *enc_directory = nullptr;
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index df1f026f78f..25c8610ff48 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -42,6 +42,8 @@
#include "editor/filesystem_dock.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/center_container.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/grid_container.h"
#include "scene/gui/label.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/option_button.h"
@@ -56,6 +58,98 @@ EditorFileDialog::GetIconFunc EditorFileDialog::get_thumbnail_func = nullptr;
EditorFileDialog::RegisterFunc EditorFileDialog::register_func = nullptr;
EditorFileDialog::RegisterFunc EditorFileDialog::unregister_func = nullptr;
+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();
+ }
+ DisplayServer::get_singleton()->file_dialog_with_options_show(get_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &EditorFileDialog::_native_dialog_cb));
+}
+
+void EditorFileDialog::popup(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 (!side_vbox && use_native) {
+ _native_popup();
+ } else {
+ // Show custom file dialog (full dialog or side menu only).
+ _update_side_menu_visibility(use_native);
+ ConfirmationDialog::popup(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 (!side_vbox && use_native) {
+ _native_popup();
+ } else {
+ // Show custom file dialog (full dialog or side menu only).
+ _update_side_menu_visibility(use_native);
+ ConfirmationDialog::set_visible(p_visible);
+ }
+ } 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);
+ }
+ }
+ String f = files[0];
+ if (mode == FILE_MODE_OPEN_FILES) {
+ emit_signal(SNAME("files_selected"), files);
+ } else {
+ if (mode == FILE_MODE_SAVE_FILE) {
+ if (p_filter >= 0 && p_filter < filters.size()) {
+ bool valid = false;
+ String flt = filters[p_filter].get_slice(";", 0);
+ int filter_slice_count = flt.get_slice_count(",");
+ for (int j = 0; j < filter_slice_count; j++) {
+ String str = (flt.get_slice(",", j).strip_edges());
+ if (f.match(str)) {
+ valid = true;
+ break;
+ }
+ }
+
+ if (!valid && filter_slice_count > 0) {
+ String str = (flt.get_slice(",", 0).strip_edges());
+ f += str.substr(1, str.length() - 1);
+ }
+ }
+ 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);
+ }
+ }
+ file->set_text(f);
+ dir->set_text(f.get_base_dir());
+ selected_options = p_selected_options;
+ filter->select(p_filter);
+}
+
void EditorFileDialog::popup_file_dialog() {
popup_centered_clamped(Size2(1050, 700) * EDSCALE, 0.8);
_focus_file_text();
@@ -385,6 +479,15 @@ void EditorFileDialog::_request_single_thumbnail(const String &p_path) {
}
void EditorFileDialog::_action_pressed() {
+ // Accept side menu properties and show native dialog.
+ if (side_vbox && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (bool(EDITOR_GET("interface/editor/use_native_file_dialogs")) || OS::get_singleton()->is_sandboxed())) {
+ hide();
+ _native_popup();
+
+ return;
+ }
+
+ // Accept selection in the custom dialog.
if (mode == FILE_MODE_OPEN_FILES) {
String fbase = dir_access->get_current_dir();
@@ -1627,6 +1730,165 @@ 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 (grid_options->get_child_count() > 0) {
+ Node *child = grid_options->get_child(0);
+ grid_options->remove_child(child);
+ child->queue_free();
+ }
+ selected_options.clear();
+
+ for (const EditorFileDialog::Option &opt : options) {
+ Label *lbl = memnew(Label);
+ lbl->set_text(opt.name);
+ grid_options->add_child(lbl);
+ if (opt.values.is_empty()) {
+ CheckBox *cb = memnew(CheckBox);
+ cb->set_pressed(opt.default_idx);
+ grid_options->add_child(cb);
+ cb->connect("toggled", callable_mp(this, &EditorFileDialog::_option_changed_checkbox_toggled).bind(opt.name));
+ selected_options[opt.name] = (bool)opt.default_idx;
+ } else {
+ OptionButton *ob = memnew(OptionButton);
+ for (const String &val : opt.values) {
+ ob->add_item(val);
+ }
+ ob->select(opt.default_idx);
+ grid_options->add_child(ob);
+ ob->connect("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);
@@ -1634,6 +1896,16 @@ void EditorFileDialog::_bind_methods() {
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("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);
@@ -1669,6 +1941,7 @@ void EditorFileDialog::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file", PROPERTY_HINT_FILE, "*", 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");
@@ -1684,6 +1957,13 @@ void EditorFileDialog::_bind_methods() {
BIND_ENUM_CONSTANT(DISPLAY_THUMBNAILS);
BIND_ENUM_CONSTANT(DISPLAY_LIST);
+
+ Option defaults;
+
+ base_property_helper.set_prefix("option_");
+ 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);
}
void EditorFileDialog::set_show_hidden_files(bool p_show) {
@@ -1752,7 +2032,7 @@ void EditorFileDialog::add_side_menu(Control *p_menu, const String &p_title) {
// HSplitContainer has 3 children at maximum capacity, 1 of them is the SplitContainerDragger.
ERR_FAIL_COND_MSG(body_hsplit->get_child_count() > 2, "EditorFileDialog: Only one side menu can be added.");
// Everything for the side menu goes inside of a VBoxContainer.
- VBoxContainer *side_vbox = memnew(VBoxContainer);
+ side_vbox = memnew(VBoxContainer);
side_vbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
side_vbox->set_stretch_ratio(0.5);
body_hsplit->add_child(side_vbox);
@@ -1767,10 +2047,23 @@ void EditorFileDialog::add_side_menu(Control *p_menu, const String &p_title) {
side_vbox->add_child(p_menu);
}
+void EditorFileDialog::_update_side_menu_visibility(bool p_native_dlg) {
+ if (p_native_dlg) {
+ pathhb->set_visible(false);
+ grid_options->set_visible(false);
+ list_hb->set_visible(false);
+ } else {
+ pathhb->set_visible(true);
+ grid_options->set_visible(true);
+ list_hb->set_visible(true);
+ }
+}
+
EditorFileDialog::EditorFileDialog() {
show_hidden_files = default_show_hidden_files;
display_mode = default_display_mode;
- VBoxContainer *vbc = memnew(VBoxContainer);
+
+ vbc = memnew(VBoxContainer);
add_child(vbc);
set_title(TTR("Save a File"));
@@ -1797,7 +2090,7 @@ EditorFileDialog::EditorFileDialog() {
ED_SHORTCUT_OVERRIDE("file_dialog/toggle_mode", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::V);
}
- HBoxContainer *pathhb = memnew(HBoxContainer);
+ pathhb = memnew(HBoxContainer);
vbc->add_child(pathhb);
dir_prev = memnew(Button);
@@ -1893,6 +2186,11 @@ EditorFileDialog::EditorFileDialog() {
body_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vbc->add_child(body_hsplit);
+ grid_options = memnew(GridContainer);
+ grid_options->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
+ grid_options->set_columns(2);
+ vbc->add_child(grid_options);
+
list_hb = memnew(HSplitContainer);
list_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
body_hsplit->add_child(list_hb);
@@ -2044,6 +2342,8 @@ EditorFileDialog::EditorFileDialog() {
if (register_func) {
register_func(this);
}
+
+ property_helper.setup_for_instance(base_property_helper, this);
}
EditorFileDialog::~EditorFileDialog() {
diff --git a/editor/gui/editor_file_dialog.h b/editor/gui/editor_file_dialog.h
index 1e1c99bc760..7d6fa19a443 100644
--- a/editor/gui/editor_file_dialog.h
+++ b/editor/gui/editor_file_dialog.h
@@ -33,7 +33,9 @@
#include "core/io/dir_access.h"
#include "scene/gui/dialogs.h"
+#include "scene/property_list_helper.h"
+class GridContainer;
class DependencyRemoveDialog;
class HSplitContainer;
class ItemList;
@@ -87,6 +89,7 @@ private:
Button *makedir = nullptr;
Access access = ACCESS_RESOURCES;
+ GridContainer *grid_options = nullptr;
VBoxContainer *vbox = nullptr;
FileMode mode = FILE_MODE_SAVE_FILE;
bool can_create_dir = false;
@@ -113,6 +116,9 @@ private:
ConfirmationDialog *confirm_save = nullptr;
DependencyRemoveDialog *dep_remove_dialog = nullptr;
ConfirmationDialog *global_remove_dialog = nullptr;
+ VBoxContainer *side_vbox = nullptr;
+ VBoxContainer *vbc = nullptr;
+ HBoxContainer *pathhb = nullptr;
Button *mode_thumbnails = nullptr;
Button *mode_list = nullptr;
@@ -174,6 +180,19 @@ private:
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