diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index b51341dc24cb..ef09164bacea 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -19,6 +19,15 @@ 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. + + @@ -40,6 +49,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 +89,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 +140,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 b7f3ec9963d2..026643d30d3c 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -627,6 +627,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. + Base speed for increasing/decreasing float values by dragging them in the inspector. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 24bfba3844a5..1650c26432b9 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2025,7 +2025,9 @@ void EditorNode::_dialog_action(String p_file) { case FILE_EXPORT_MESH_LIBRARY: { Ref ml; - if (file_export_lib_merge->is_pressed() && FileAccess::exists(p_file)) { + const Dictionary &fd_options = file_export_lib->get_selected_options(); + bool file_export_lib_merge = (bool)fd_options.get(TTR("Merge With Existing"), true); + if (file_export_lib_merge && FileAccess::exists(p_file)) { ml = ResourceLoader::load(p_file, "MeshLibrary"); if (ml.is_null()) { @@ -2038,7 +2040,8 @@ void EditorNode::_dialog_action(String p_file) { ml = Ref(memnew(MeshLibrary)); } - MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true, file_export_lib_apply_xforms->is_pressed()); + bool file_export_lib_apply_xforms = (bool)fd_options.get(TTR("Apply MeshInstance Transforms"), false); + MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true, file_export_lib_apply_xforms); Error err = ResourceSaver::save(ml, p_file); if (err) { @@ -2660,8 +2663,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; @@ -3092,8 +3095,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); @@ -7188,16 +7191,9 @@ 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 f1dea0c11e43..bcfffacc28a7 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -392,8 +392,6 @@ class EditorNode : public Node { 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 25510122f40d..008bd67d4a4b 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -413,6 +413,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_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/editor/custom_display_scale", 1.0, "0.5,3,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 63bd87e6cc00..b18a12bcb52c 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -999,10 +999,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 = (bool)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); } } @@ -1084,7 +1087,11 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) { exporting = true; platform->clear_messages(); - 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 = (bool)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)) { @@ -1485,17 +1492,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 1a359b08dabd..b80df3e7cf23 100644 --- a/editor/export/project_export.h +++ b/editor/export/project_export.h @@ -152,8 +152,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 4d1fd1f7b7e2..6edcaa408e23 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,89 @@ EditorFileDialog::GetIconFunc EditorFileDialog::get_thumbnail_func = nullptr; EditorFileDialog::RegisterFunc EditorFileDialog::register_func = nullptr; EditorFileDialog::RegisterFunc EditorFileDialog::unregister_func = nullptr; +void EditorFileDialog::popup(const Rect2i &p_rect) { + _update_option_controls(); + + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG) && (bool(EDITOR_GET("interface/editor/use_native_file_dialogs")) || OS::get_singleton()->is_sandboxed())) { + 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)); + } else { + ConfirmationDialog::popup(p_rect); + } +} + +void EditorFileDialog::set_visible(bool p_visible) { + _update_option_controls(); + + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG) && (bool(EDITOR_GET("interface/editor/use_native_file_dialogs")) || OS::get_singleton()->is_sandboxed())) { + if (p_visible) { + 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)); + } + } 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) { + if (p_files.size() > 0) { + 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); + } + } else { + file->set_text(""); + emit_signal(SNAME("canceled")); + } +} + void EditorFileDialog::popup_file_dialog() { popup_centered_clamped(Size2(1050, 700) * EDSCALE, 0.8); _focus_file_text(); @@ -1621,6 +1706,212 @@ 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(false) > 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); + int prev_size = options.size(); + + if (prev_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(); +} + +bool EditorFileDialog::_set(const StringName &p_name, const Variant &p_value) { + Vector components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("option_") && components[0].trim_prefix("option_").is_valid_int()) { + int item_index = components[0].trim_prefix("option_").to_int(); + String property = components[1]; + if (property == "name") { + set_option_name(item_index, p_value); + return true; + } else if (property == "values") { + set_option_values(item_index, p_value); + return true; + } else if (property == "default") { + set_option_default(item_index, p_value); + return true; + } + } + return false; +} + +bool EditorFileDialog::_get(const StringName &p_name, Variant &r_ret) const { + Vector components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("option_") && components[0].trim_prefix("option_").is_valid_int()) { + int item_index = components[0].trim_prefix("option_").to_int(); + String property = components[1]; + if (property == "name") { + r_ret = get_option_name(item_index); + return true; + } else if (property == "values") { + r_ret = get_option_values(item_index); + return true; + } else if (property == "default") { + r_ret = get_option_default(item_index); + return true; + } + } + return false; +} + +void EditorFileDialog::_get_property_list(List *p_list) const { + for (int i = 0; i < options.size(); i++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("option_%d/name", i))); + p_list->push_back(PropertyInfo(Variant::PACKED_STRING_ARRAY, vformat("option_%d/values", i))); + p_list->push_back(PropertyInfo(Variant::INT, vformat("option_%d/default", i))); + } +} + void EditorFileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_cancel_pressed"), &EditorFileDialog::_cancel_pressed); @@ -1628,6 +1919,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", "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", "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); @@ -1663,6 +1964,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"); @@ -1880,6 +2182,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); diff --git a/editor/gui/editor_file_dialog.h b/editor/gui/editor_file_dialog.h index e0f53ace969a..8b9c4b4904d8 100644 --- a/editor/gui/editor_file_dialog.h +++ b/editor/gui/editor_file_dialog.h @@ -34,6 +34,7 @@ #include "core/io/dir_access.h" #include "scene/gui/dialogs.h" +class GridContainer; class DependencyRemoveDialog; class HSplitContainer; class ItemList; @@ -88,6 +89,7 @@ class EditorFileDialog : public ConfirmationDialog { Access access = ACCESS_RESOURCES; VBoxContainer *vbox = nullptr; + GridContainer *grid_options = nullptr; FileMode mode = FILE_MODE_SAVE_FILE; bool can_create_dir = false; LineEdit *dir = nullptr; @@ -173,6 +175,15 @@ class EditorFileDialog : public ConfirmationDialog { Ref progress[8]{}; } theme_cache; + struct Option { + String name; + Vector values; + int default_idx = 0; + }; + Vector