diff --git a/Userland/Libraries/LibGUI/FilePicker.cpp b/Userland/Libraries/LibGUI/FilePicker.cpp index b05d9fc62f..d080187889 100644 --- a/Userland/Libraries/LibGUI/FilePicker.cpp +++ b/Userland/Libraries/LibGUI/FilePicker.cpp @@ -16,7 +16,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -32,9 +35,9 @@ namespace GUI { -Optional FilePicker::get_open_filepath(Window* parent_window, DeprecatedString const& window_title, StringView path, bool folder, ScreenPosition screen_position) +Optional FilePicker::get_open_filepath(Window* parent_window, DeprecatedString const& window_title, StringView path, bool folder, ScreenPosition screen_position, Optional> allowed_file_types) { - auto picker = FilePicker::construct(parent_window, folder ? Mode::OpenFolder : Mode::Open, ""sv, path, screen_position); + auto picker = FilePicker::construct(parent_window, folder ? Mode::OpenFolder : Mode::Open, ""sv, path, screen_position, move(allowed_file_types)); if (!window_title.is_null()) picker->set_title(window_title); @@ -65,9 +68,10 @@ Optional FilePicker::get_save_filepath(Window* parent_window, return {}; } -FilePicker::FilePicker(Window* parent_window, Mode mode, StringView filename, StringView path, ScreenPosition screen_position) +FilePicker::FilePicker(Window* parent_window, Mode mode, StringView filename, StringView path, ScreenPosition screen_position, Optional> allowed_file_types) : Dialog(parent_window, screen_position) , m_model(FileSystemModel::create(path)) + , m_allowed_file_types(move(allowed_file_types)) , m_mode(mode) { switch (m_mode) { @@ -112,6 +116,36 @@ FilePicker::FilePicker(Window* parent_window, Mode mode, StringView filename, St set_path(m_location_textbox->text()); }; + auto* file_types_filters_combo = widget->find_descendant_of_type_named("allowed_file_type_filters_combo"); + + if (m_allowed_file_types.has_value()) { + for (auto& filter : *m_allowed_file_types) { + if (!filter.extensions.has_value()) { + m_allowed_file_types_names.append(filter.name); + continue; + } + + StringBuilder extension_list; + extension_list.join("; "sv, *filter.extensions); + m_allowed_file_types_names.append(DeprecatedString::formatted("{} ({})", filter.name, extension_list.to_deprecated_string())); + } + + file_types_filters_combo->set_model(*GUI::ItemListModel>::create(m_allowed_file_types_names)); + file_types_filters_combo->on_change = [this](DeprecatedString const&, GUI::ModelIndex const& index) { + m_model->set_allowed_file_extensions((*m_allowed_file_types)[index.row()].extensions); + }; + file_types_filters_combo->set_selected_index(0); + } else { + auto* file_types_filter_label = widget->find_descendant_of_type_named("allowed_file_types_label"); + auto& spacer = file_types_filter_label->parent_widget()->add(); + spacer.set_fixed_height(22); + file_types_filter_label->remove_from_parent(); + + file_types_filters_combo->parent_widget()->insert_child_before(GUI::Widget::construct(), *file_types_filters_combo); + + file_types_filters_combo->remove_from_parent(); + } + auto open_parent_directory_action = Action::create( "Open parent directory", { Mod_Alt, Key_Up }, Gfx::Bitmap::load_from_file("/res/icons/16x16/open-parent-directory.png"sv).release_value_but_fixme_should_propagate_errors(), [this](Action const&) { set_path(DeprecatedString::formatted("{}/..", m_model->root_path())); diff --git a/Userland/Libraries/LibGUI/FilePicker.h b/Userland/Libraries/LibGUI/FilePicker.h index 20b8ff89e0..8611aac51b 100644 --- a/Userland/Libraries/LibGUI/FilePicker.h +++ b/Userland/Libraries/LibGUI/FilePicker.h @@ -8,8 +8,11 @@ #include #include +#include #include +#include #include +#include #include #include @@ -28,7 +31,7 @@ public: Save }; - static Optional get_open_filepath(Window* parent_window, DeprecatedString const& window_title = {}, StringView path = Core::StandardPaths::home_directory(), bool folder = false, ScreenPosition screen_position = Dialog::ScreenPosition::CenterWithinParent); + static Optional get_open_filepath(Window* parent_window, DeprecatedString const& window_title = {}, StringView path = Core::StandardPaths::home_directory(), bool folder = false, ScreenPosition screen_position = Dialog::ScreenPosition::CenterWithinParent, Optional> allowed_file_types = {}); static Optional get_save_filepath(Window* parent_window, DeprecatedString const& title, DeprecatedString const& extension, StringView path = Core::StandardPaths::home_directory(), ScreenPosition screen_position = Dialog::ScreenPosition::CenterWithinParent); virtual ~FilePicker() override; @@ -43,7 +46,7 @@ private: // ^GUI::ModelClient virtual void model_did_update(unsigned) override; - FilePicker(Window* parent_window, Mode type = Mode::Open, StringView filename = "Untitled"sv, StringView path = Core::StandardPaths::home_directory(), ScreenPosition screen_position = Dialog::ScreenPosition::CenterWithinParent); + FilePicker(Window* parent_window, Mode type = Mode::Open, StringView filename = "Untitled"sv, StringView path = Core::StandardPaths::home_directory(), ScreenPosition screen_position = Dialog::ScreenPosition::CenterWithinParent, Optional> allowed_file_types = {}); static DeprecatedString ok_button_name(Mode mode) { @@ -68,6 +71,9 @@ private: NonnullRefPtr m_model; DeprecatedString m_selected_file; + Vector m_allowed_file_types_names; + Optional> m_allowed_file_types; + RefPtr m_error_label; RefPtr m_filename_textbox; diff --git a/Userland/Libraries/LibGUI/FilePickerDialog.gml b/Userland/Libraries/LibGUI/FilePickerDialog.gml index d285d2ebf5..79760da794 100644 --- a/Userland/Libraries/LibGUI/FilePickerDialog.gml +++ b/Userland/Libraries/LibGUI/FilePickerDialog.gml @@ -25,11 +25,14 @@ @GUI::Label { text: "Filename:" text_alignment: "CenterRight" - fixed_height: 24 + fixed_height: 22 } - @GUI::Widget { - fixed_height: 20 + @GUI::Label { + name: "allowed_file_types_label" + text: "Files of Type:" + text_alignment: "CenterRight" + fixed_height: 22 } } @@ -68,10 +71,6 @@ name: "filename_textbox" } - @GUI::Widget { - fixed_width: 20 - } - @GUI::DialogButton { name: "ok_button" text: "OK" @@ -82,7 +81,10 @@ fixed_height: 22 layout: @GUI::HorizontalBoxLayout {} - @GUI::Layout::Spacer {} + @GUI::ComboBox { + name: "allowed_file_type_filters_combo" + model_only: true + } @GUI::DialogButton { name: "cancel_button" diff --git a/Userland/Libraries/LibGUI/FileTypeFilter.h b/Userland/Libraries/LibGUI/FileTypeFilter.h new file mode 100644 index 0000000000..144ed765a3 --- /dev/null +++ b/Userland/Libraries/LibGUI/FileTypeFilter.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023, Marcus Nilsson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace GUI { + +struct FileTypeFilter { + DeprecatedString name; + Optional> extensions; + + static FileTypeFilter all_files() + { + return FileTypeFilter { "All Files", {} }; + } + + static FileTypeFilter image_files() + { + return FileTypeFilter { "Image Files", Vector { "png", "gif", "bmp", "dip", "pbm", "pgm", "ppm", "ico", "jpeg", "jpg", "dds", "qoi" } }; + } +}; + +}