diff options
Diffstat (limited to 'editor/gui/editor_file_dialog.cpp')
-rw-r--r-- | editor/gui/editor_file_dialog.cpp | 312 |
1 files changed, 307 insertions, 5 deletions
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index df1f026f78..a7cdb65145 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<String> &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<String> 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(); @@ -865,8 +968,10 @@ void EditorFileDialog::update_file_list() { } } else if (!dir_access->current_is_hidden()) { String full_path = cdir == "res://" ? item : dir_access->get_current_dir() + "/" + item; - if (dir_access->current_is_dir() && (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path))) { - dirs.push_back(item); + if (dir_access->current_is_dir()) { + if (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path)) { + dirs.push_back(item); + } } else { files.push_back(item); } @@ -1627,6 +1732,165 @@ EditorFileDialog::DisplayMode EditorFileDialog::get_display_mode() const { return display_mode; } +TypedArray<Dictionary> EditorFileDialog::_get_options() const { + TypedArray<Dictionary> 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<String> EditorFileDialog::get_option_values(int p_option) const { + ERR_FAIL_INDEX_V(p_option, options.size(), Vector<String>()); + 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<String> &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<String> &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 +1898,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 +1943,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 +1959,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 +2034,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 +2049,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 +2092,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 +2188,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 +2344,8 @@ EditorFileDialog::EditorFileDialog() { if (register_func) { register_func(this); } + + property_helper.setup_for_instance(base_property_helper, this); } EditorFileDialog::~EditorFileDialog() { |