123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- /*
- * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
- * Copyright (c) 2021-2022, networkException <networkexception@serenityos.org>
- * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
- * Copyright (c) 2021, Antonio Di Stefano <tonio9681@gmail.com>
- * Copyright (c) 2022, Filiph Sandström <filiph.sandstrom@filfatstudios.com>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include "MainWidget.h"
- #include <Applications/ThemeEditor/AlignmentPropertyGML.h>
- #include <Applications/ThemeEditor/ColorPropertyGML.h>
- #include <Applications/ThemeEditor/FlagPropertyGML.h>
- #include <Applications/ThemeEditor/MetricPropertyGML.h>
- #include <Applications/ThemeEditor/PathPropertyGML.h>
- #include <Applications/ThemeEditor/ThemeEditorGML.h>
- #include <LibFileSystem/FileSystem.h>
- #include <LibFileSystemAccessClient/Client.h>
- #include <LibGUI/ActionGroup.h>
- #include <LibGUI/BoxLayout.h>
- #include <LibGUI/Button.h>
- #include <LibGUI/ConnectionToWindowServer.h>
- #include <LibGUI/FilePicker.h>
- #include <LibGUI/Frame.h>
- #include <LibGUI/GroupBox.h>
- #include <LibGUI/Icon.h>
- #include <LibGUI/ItemListModel.h>
- #include <LibGUI/Label.h>
- #include <LibGUI/Menu.h>
- #include <LibGUI/Menubar.h>
- #include <LibGUI/MessageBox.h>
- #include <LibGUI/ScrollableContainerWidget.h>
- #include <LibGUI/Statusbar.h>
- namespace ThemeEditor {
- static PropertyTab const window_tab {
- "Windows"sv,
- {
- { "General",
- { { Gfx::FlagRole::IsDark },
- { Gfx::AlignmentRole::TitleAlignment },
- { Gfx::MetricRole::TitleHeight },
- { Gfx::MetricRole::TitleButtonWidth },
- { Gfx::MetricRole::TitleButtonHeight },
- { Gfx::PathRole::TitleButtonIcons },
- { Gfx::FlagRole::TitleButtonsIconOnly } } },
- { "Border",
- { { Gfx::MetricRole::BorderThickness },
- { Gfx::MetricRole::BorderRadius } } },
- { "Active Window",
- { { Gfx::ColorRole::ActiveWindowBorder1 },
- { Gfx::ColorRole::ActiveWindowBorder2 },
- { Gfx::ColorRole::ActiveWindowTitle },
- { Gfx::ColorRole::ActiveWindowTitleShadow },
- { Gfx::ColorRole::ActiveWindowTitleStripes },
- { Gfx::PathRole::ActiveWindowShadow } } },
- { "Inactive Window",
- { { Gfx::ColorRole::InactiveWindowBorder1 },
- { Gfx::ColorRole::InactiveWindowBorder2 },
- { Gfx::ColorRole::InactiveWindowTitle },
- { Gfx::ColorRole::InactiveWindowTitleShadow },
- { Gfx::ColorRole::InactiveWindowTitleStripes },
- { Gfx::PathRole::InactiveWindowShadow } } },
- { "Highlighted Window",
- { { Gfx::ColorRole::HighlightWindowBorder1 },
- { Gfx::ColorRole::HighlightWindowBorder2 },
- { Gfx::ColorRole::HighlightWindowTitle },
- { Gfx::ColorRole::HighlightWindowTitleShadow },
- { Gfx::ColorRole::HighlightWindowTitleStripes } } },
- { "Moving Window",
- { { Gfx::ColorRole::MovingWindowBorder1 },
- { Gfx::ColorRole::MovingWindowBorder2 },
- { Gfx::ColorRole::MovingWindowTitle },
- { Gfx::ColorRole::MovingWindowTitleShadow },
- { Gfx::ColorRole::MovingWindowTitleStripes } } },
- { "Contents",
- { { Gfx::ColorRole::Window },
- { Gfx::ColorRole::WindowText } } },
- { "Desktop",
- { { Gfx::ColorRole::DesktopBackground },
- { Gfx::PathRole::TaskbarShadow } } },
- }
- };
- static PropertyTab const widgets_tab {
- "Widgets"sv,
- {
- { "General",
- { { Gfx::ColorRole::Accent },
- { Gfx::ColorRole::Base },
- { Gfx::ColorRole::ThreedHighlight },
- { Gfx::ColorRole::ThreedShadow1 },
- { Gfx::ColorRole::ThreedShadow2 },
- { Gfx::ColorRole::HoverHighlight } } },
- { "Text",
- { { Gfx::ColorRole::BaseText },
- { Gfx::ColorRole::DisabledTextFront },
- { Gfx::ColorRole::DisabledTextBack },
- { Gfx::ColorRole::PlaceholderText } } },
- { "Links",
- { { Gfx::ColorRole::Link },
- { Gfx::ColorRole::ActiveLink },
- { Gfx::ColorRole::VisitedLink } } },
- { "Buttons",
- { { Gfx::ColorRole::Button },
- { Gfx::ColorRole::ButtonText } } },
- { "Tooltips",
- { { Gfx::ColorRole::Tooltip },
- { Gfx::ColorRole::TooltipText },
- { Gfx::PathRole::TooltipShadow } } },
- { "Trays",
- { { Gfx::ColorRole::Tray },
- { Gfx::ColorRole::TrayText } } },
- { "Ruler",
- { { Gfx::ColorRole::Ruler },
- { Gfx::ColorRole::RulerBorder },
- { Gfx::ColorRole::RulerActiveText },
- { Gfx::ColorRole::RulerInactiveText } } },
- { "Gutter",
- { { Gfx::ColorRole::Gutter },
- { Gfx::ColorRole::GutterBorder } } },
- { "Rubber Band",
- { { Gfx::ColorRole::RubberBandBorder },
- { Gfx::ColorRole::RubberBandFill } } },
- { "Menus",
- { { Gfx::ColorRole::MenuBase },
- { Gfx::ColorRole::MenuBaseText },
- { Gfx::ColorRole::MenuSelection },
- { Gfx::ColorRole::MenuSelectionText },
- { Gfx::ColorRole::MenuStripe },
- { Gfx::PathRole::MenuShadow } } },
- { "Selection",
- { { Gfx::ColorRole::FocusOutline },
- { Gfx::ColorRole::TextCursor },
- { Gfx::ColorRole::Selection },
- { Gfx::ColorRole::SelectionText },
- { Gfx::ColorRole::InactiveSelection },
- { Gfx::ColorRole::InactiveSelectionText },
- { Gfx::ColorRole::HighlightSearching },
- { Gfx::ColorRole::HighlightSearchingText } } },
- }
- };
- static PropertyTab const syntax_highlighting_tab {
- "Syntax Highlighting"sv,
- {
- { "General",
- { { Gfx::ColorRole::SyntaxComment },
- { Gfx::ColorRole::SyntaxControlKeyword },
- { Gfx::ColorRole::SyntaxIdentifier },
- { Gfx::ColorRole::SyntaxKeyword },
- { Gfx::ColorRole::SyntaxNumber },
- { Gfx::ColorRole::SyntaxOperator },
- { Gfx::ColorRole::SyntaxPreprocessorStatement },
- { Gfx::ColorRole::SyntaxPreprocessorValue },
- { Gfx::ColorRole::SyntaxPunctuation },
- { Gfx::ColorRole::SyntaxString },
- { Gfx::ColorRole::SyntaxType },
- { Gfx::ColorRole::SyntaxFunction },
- { Gfx::ColorRole::SyntaxVariable },
- { Gfx::ColorRole::SyntaxCustomType },
- { Gfx::ColorRole::SyntaxNamespace },
- { Gfx::ColorRole::SyntaxMember },
- { Gfx::ColorRole::SyntaxParameter } } },
- }
- };
- static PropertyTab const color_scheme_tab {
- "Color Scheme"sv,
- {
- { "General",
- { { Gfx::FlagRole::BoldTextAsBright },
- { Gfx::ColorRole::Black },
- { Gfx::ColorRole::Red },
- { Gfx::ColorRole::Green },
- { Gfx::ColorRole::Yellow },
- { Gfx::ColorRole::Blue },
- { Gfx::ColorRole::Magenta },
- { Gfx::ColorRole::ColorSchemeBackground },
- { Gfx::ColorRole::ColorSchemeForeground },
- { Gfx::ColorRole::Cyan },
- { Gfx::ColorRole::White },
- { Gfx::ColorRole::BrightBlack },
- { Gfx::ColorRole::BrightRed },
- { Gfx::ColorRole::BrightGreen },
- { Gfx::ColorRole::BrightYellow },
- { Gfx::ColorRole::BrightBlue },
- { Gfx::ColorRole::BrightMagenta },
- { Gfx::ColorRole::BrightCyan },
- { Gfx::ColorRole::BrightWhite } } },
- }
- };
- ErrorOr<NonnullRefPtr<MainWidget>> MainWidget::try_create()
- {
- auto alignment_model = TRY(AlignmentModel::try_create());
- auto main_widget = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) MainWidget(move(alignment_model))));
- TRY(main_widget->load_from_gml(theme_editor_gml));
- main_widget->m_preview_widget = main_widget->find_descendant_of_type_named<ThemeEditor::PreviewWidget>("preview_widget");
- main_widget->m_property_tabs = main_widget->find_descendant_of_type_named<GUI::TabWidget>("property_tabs");
- main_widget->m_statusbar = main_widget->find_descendant_of_type_named<GUI::Statusbar>("statusbar");
- TRY(main_widget->add_property_tab(window_tab));
- TRY(main_widget->add_property_tab(widgets_tab));
- TRY(main_widget->add_property_tab(syntax_highlighting_tab));
- TRY(main_widget->add_property_tab(color_scheme_tab));
- main_widget->build_override_controls();
- return main_widget;
- }
- MainWidget::MainWidget(NonnullRefPtr<AlignmentModel> alignment_model)
- : m_current_palette(GUI::Application::the()->palette())
- , m_alignment_model(move(alignment_model))
- {
- GUI::Application::the()->on_action_enter = [this](GUI::Action& action) {
- m_statusbar->set_override_text(action.status_tip());
- };
- GUI::Application::the()->on_action_leave = [this](GUI::Action&) {
- m_statusbar->set_override_text({});
- };
- }
- ErrorOr<void> MainWidget::initialize_menubar(GUI::Window& window)
- {
- auto file_menu = window.add_menu("&File"_string);
- file_menu->add_action(GUI::CommonActions::make_open_action([&](auto&) {
- if (request_close() == GUI::Window::CloseRequestDecision::StayOpen)
- return;
- FileSystemAccessClient::OpenFileOptions options {
- .window_title = "Select Theme"sv,
- .path = "/res/themes"sv,
- .allowed_file_types = Vector { { "Theme Files", { { "ini" } } }, GUI::FileTypeFilter::all_files() },
- };
- auto response = FileSystemAccessClient::Client::the().open_file(&window, options);
- if (response.is_error())
- return;
- auto load_from_file_result = load_from_file(response.value().filename(), response.value().release_stream());
- if (load_from_file_result.is_error()) {
- GUI::MessageBox::show_error(&window, DeprecatedString::formatted("Can't open file named {}: {}", response.value().filename(), load_from_file_result.error()));
- return;
- }
- }));
- m_save_action = GUI::CommonActions::make_save_action([&](auto&) {
- if (m_path.has_value()) {
- auto result = FileSystemAccessClient::Client::the().request_file(&window, *m_path, Core::File::OpenMode::ReadWrite | Core::File::OpenMode::Truncate);
- if (result.is_error())
- return;
- save_to_file(result.value().filename(), result.value().release_stream());
- } else {
- auto result = FileSystemAccessClient::Client::the().save_file(&window, "Theme", "ini", Core::File::OpenMode::ReadWrite | Core::File::OpenMode::Truncate);
- if (result.is_error())
- return;
- save_to_file(result.value().filename(), result.value().release_stream());
- }
- });
- file_menu->add_action(*m_save_action);
- file_menu->add_action(GUI::CommonActions::make_save_as_action([&](auto&) {
- auto result = FileSystemAccessClient::Client::the().save_file(&window, "Theme", "ini", Core::File::OpenMode::ReadWrite | Core::File::OpenMode::Truncate);
- if (result.is_error())
- return;
- save_to_file(result.value().filename(), result.value().release_stream());
- }));
- file_menu->add_separator();
- file_menu->add_recent_files_list([&](auto& action) {
- if (request_close() == GUI::Window::CloseRequestDecision::StayOpen)
- return;
- auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(&window, action.text());
- if (response.is_error())
- return;
- auto load_from_file_result = load_from_file(response.value().filename(), response.value().release_stream());
- if (load_from_file_result.is_error()) {
- GUI::MessageBox::show_error(&window, DeprecatedString::formatted("Can't open file named {}: {}", response.value().filename(), load_from_file_result.error()));
- return;
- }
- });
- file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) {
- if (request_close() == GUI::Window::CloseRequestDecision::Close)
- GUI::Application::the()->quit();
- }));
- window.add_menu(GUI::CommonMenus::make_accessibility_menu(*m_preview_widget));
- auto help_menu = window.add_menu("&Help"_string);
- help_menu->add_action(GUI::CommonActions::make_command_palette_action(&window));
- help_menu->add_action(GUI::CommonActions::make_about_action("Theme Editor"_string, GUI::Icon::default_icon("app-theme-editor"sv), &window));
- return {};
- }
- void MainWidget::update_title()
- {
- window()->set_title(DeprecatedString::formatted("{}[*] - Theme Editor", m_path.value_or("Untitled")));
- }
- GUI::Window::CloseRequestDecision MainWidget::request_close()
- {
- if (!window()->is_modified())
- return GUI::Window::CloseRequestDecision::Close;
- auto result = GUI::MessageBox::ask_about_unsaved_changes(window(), m_path.value_or(""), m_last_modified_time);
- if (result == GUI::MessageBox::ExecResult::Yes) {
- m_save_action->activate();
- if (window()->is_modified())
- return GUI::Window::CloseRequestDecision::StayOpen;
- return GUI::Window::CloseRequestDecision::Close;
- }
- if (result == GUI::MessageBox::ExecResult::No)
- return GUI::Window::CloseRequestDecision::Close;
- return GUI::Window::CloseRequestDecision::StayOpen;
- }
- void MainWidget::set_path(DeprecatedString path)
- {
- m_path = path;
- update_title();
- }
- void MainWidget::save_to_file(String const& filename, NonnullOwnPtr<Core::File> file)
- {
- auto theme = Core::ConfigFile::open(filename.to_deprecated_string(), move(file)).release_value_but_fixme_should_propagate_errors();
- #define __ENUMERATE_ALIGNMENT_ROLE(role) theme->write_entry("Alignments", to_string(Gfx::AlignmentRole::role), to_string(m_current_palette.alignment(Gfx::AlignmentRole::role)));
- ENUMERATE_ALIGNMENT_ROLES(__ENUMERATE_ALIGNMENT_ROLE)
- #undef __ENUMERATE_ALIGNMENT_ROLE
- #define __ENUMERATE_COLOR_ROLE(role) theme->write_entry("Colors", to_string(Gfx::ColorRole::role), m_current_palette.color(Gfx::ColorRole::role).to_deprecated_string());
- ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE)
- #undef __ENUMERATE_COLOR_ROLE
- #define __ENUMERATE_FLAG_ROLE(role) theme->write_bool_entry("Flags", to_string(Gfx::FlagRole::role), m_current_palette.flag(Gfx::FlagRole::role));
- ENUMERATE_FLAG_ROLES(__ENUMERATE_FLAG_ROLE)
- #undef __ENUMERATE_FLAG_ROLE
- #define __ENUMERATE_METRIC_ROLE(role) theme->write_num_entry("Metrics", to_string(Gfx::MetricRole::role), m_current_palette.metric(Gfx::MetricRole::role));
- ENUMERATE_METRIC_ROLES(__ENUMERATE_METRIC_ROLE)
- #undef __ENUMERATE_METRIC_ROLE
- #define __ENUMERATE_PATH_ROLE(role) theme->write_entry("Paths", to_string(Gfx::PathRole::role), m_current_palette.path(Gfx::PathRole::role));
- ENUMERATE_PATH_ROLES(__ENUMERATE_PATH_ROLE)
- #undef __ENUMERATE_PATH_ROLE
- auto sync_result = theme->sync();
- if (sync_result.is_error()) {
- GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Failed to save theme file: {}", sync_result.error()));
- } else {
- m_last_modified_time = MonotonicTime::now();
- set_path(filename.to_deprecated_string());
- window()->set_modified(false);
- GUI::Application::the()->set_most_recently_open_file(filename);
- }
- }
- ErrorOr<Core::AnonymousBuffer> MainWidget::encode()
- {
- auto buffer = TRY(Core::AnonymousBuffer::create_with_size(sizeof(Gfx::SystemTheme)));
- auto* data = buffer.data<Gfx::SystemTheme>();
- #define __ENUMERATE_ALIGNMENT_ROLE(role) \
- data->alignment[(int)Gfx::AlignmentRole::role] = m_current_palette.alignment(Gfx::AlignmentRole::role);
- ENUMERATE_ALIGNMENT_ROLES(__ENUMERATE_ALIGNMENT_ROLE)
- #undef __ENUMERATE_ALIGNMENT_ROLE
- #define __ENUMERATE_COLOR_ROLE(role) \
- data->color[(int)Gfx::ColorRole::role] = m_current_palette.color(Gfx::ColorRole::role).value();
- ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE)
- #undef __ENUMERATE_COLOR_ROLE
- #define __ENUMERATE_FLAG_ROLE(role) \
- data->flag[(int)Gfx::FlagRole::role] = m_current_palette.flag(Gfx::FlagRole::role);
- ENUMERATE_FLAG_ROLES(__ENUMERATE_FLAG_ROLE)
- #undef __ENUMERATE_FLAG_ROLE
- #define __ENUMERATE_METRIC_ROLE(role) \
- data->metric[(int)Gfx::MetricRole::role] = m_current_palette.metric(Gfx::MetricRole::role);
- ENUMERATE_METRIC_ROLES(__ENUMERATE_METRIC_ROLE)
- #undef __ENUMERATE_METRIC_ROLE
- #define ENCODE_PATH(role, allow_empty) \
- do { \
- auto path = m_current_palette.path(Gfx::PathRole::role); \
- char const* characters; \
- if (path.is_empty()) { \
- switch (Gfx::PathRole::role) { \
- case Gfx::PathRole::TitleButtonIcons: \
- characters = "/res/icons/16x16/"; \
- break; \
- default: \
- characters = allow_empty ? "" : "/res/"; \
- } \
- } \
- characters = path.characters(); \
- memcpy(data->path[(int)Gfx::PathRole::role], characters, min(strlen(characters) + 1, sizeof(data->path[(int)Gfx::PathRole::role]))); \
- data->path[(int)Gfx::PathRole::role][sizeof(data->path[(int)Gfx::PathRole::role]) - 1] = '\0'; \
- } while (0)
- ENCODE_PATH(TitleButtonIcons, false);
- ENCODE_PATH(ActiveWindowShadow, true);
- ENCODE_PATH(InactiveWindowShadow, true);
- ENCODE_PATH(TaskbarShadow, true);
- ENCODE_PATH(MenuShadow, true);
- ENCODE_PATH(TooltipShadow, true);
- return buffer;
- }
- void MainWidget::build_override_controls()
- {
- auto* theme_override_controls = find_descendant_of_type_named<GUI::Widget>("theme_override_controls");
- m_theme_override_apply = theme_override_controls->find_child_of_type_named<GUI::DialogButton>("apply_button");
- m_theme_override_reset = theme_override_controls->find_child_of_type_named<GUI::DialogButton>("reset_button");
- m_theme_override_apply->on_click = [&](auto) {
- auto encoded = encode();
- if (encoded.is_error())
- return;
- // Empty the color scheme path to signal that it exists only in memory.
- m_current_palette.path(Gfx::PathRole::ColorScheme) = "";
- GUI::ConnectionToWindowServer::the().async_set_system_theme_override(encoded.value());
- };
- m_theme_override_reset->on_click = [&](auto) {
- GUI::ConnectionToWindowServer::the().async_clear_system_theme_override();
- };
- GUI::Application::the()->on_theme_change = [&]() {
- auto override_active = GUI::ConnectionToWindowServer::the().is_system_theme_overridden();
- m_theme_override_apply->set_enabled(!override_active && window()->is_modified());
- m_theme_override_reset->set_enabled(override_active);
- };
- }
- ErrorOr<void> MainWidget::add_property_tab(PropertyTab const& property_tab)
- {
- auto scrollable_container = TRY(m_property_tabs->try_add_tab<GUI::ScrollableContainerWidget>(TRY(String::from_utf8(property_tab.title))));
- scrollable_container->set_should_hide_unnecessary_scrollbars(true);
- auto properties_list = TRY(GUI::Widget::try_create());
- scrollable_container->set_widget(properties_list);
- properties_list->set_layout<GUI::VerticalBoxLayout>(GUI::Margins { 8 }, 12);
- for (auto const& group : property_tab.property_groups) {
- NonnullRefPtr<GUI::GroupBox> group_box = TRY(properties_list->try_add<GUI::GroupBox>(group.title));
- // 1px less on the left makes the text line up with the group title.
- group_box->set_layout<GUI::VerticalBoxLayout>(GUI::Margins { 8, 8, 8, 7 }, 12);
- group_box->set_preferred_height(GUI::SpecialDimension::Fit);
- for (auto const& property : group.properties) {
- NonnullRefPtr<GUI::Widget> row_widget = TRY(group_box->try_add<GUI::Widget>());
- row_widget->set_fixed_height(22);
- TRY(property.role.visit(
- [&](Gfx::AlignmentRole role) -> ErrorOr<void> {
- TRY(row_widget->load_from_gml(alignment_property_gml));
- auto& name_label = *row_widget->find_descendant_of_type_named<GUI::Label>("name");
- name_label.set_text(TRY(String::from_utf8(to_string(role))));
- auto& alignment_picker = *row_widget->find_descendant_of_type_named<GUI::ComboBox>("combo_box");
- alignment_picker.set_model(*m_alignment_model);
- alignment_picker.on_change = [&, role](auto&, auto& index) {
- set_alignment(role, index.data(GUI::ModelRole::Custom).to_text_alignment(Gfx::TextAlignment::CenterLeft));
- };
- alignment_picker.set_selected_index(m_alignment_model->index_of(m_current_palette.alignment(role)), GUI::AllowCallback::No);
- VERIFY(m_alignment_inputs[to_underlying(role)].is_null());
- m_alignment_inputs[to_underlying(role)] = alignment_picker;
- return {};
- },
- [&](Gfx::ColorRole role) -> ErrorOr<void> {
- TRY(row_widget->load_from_gml(color_property_gml));
- auto& name_label = *row_widget->find_descendant_of_type_named<GUI::Label>("name");
- name_label.set_text(TRY(String::from_utf8(to_string(role))));
- auto& color_input = *row_widget->find_descendant_of_type_named<GUI::ColorInput>("color_input");
- color_input.on_change = [&, role] {
- set_color(role, color_input.color());
- };
- color_input.set_color(m_current_palette.color(role), GUI::AllowCallback::No);
- VERIFY(m_color_inputs[to_underlying(role)].is_null());
- m_color_inputs[to_underlying(role)] = color_input;
- return {};
- },
- [&](Gfx::FlagRole role) -> ErrorOr<void> {
- TRY(row_widget->load_from_gml(flag_property_gml));
- auto& checkbox = *row_widget->find_descendant_of_type_named<GUI::CheckBox>("checkbox");
- checkbox.set_text(TRY(String::from_utf8(to_string(role))));
- checkbox.on_checked = [&, role](bool checked) {
- set_flag(role, checked);
- };
- checkbox.set_checked(m_current_palette.flag(role), GUI::AllowCallback::No);
- VERIFY(m_flag_inputs[to_underlying(role)].is_null());
- m_flag_inputs[to_underlying(role)] = checkbox;
- return {};
- },
- [&](Gfx::MetricRole role) -> ErrorOr<void> {
- TRY(row_widget->load_from_gml(metric_property_gml));
- auto& name_label = *row_widget->find_descendant_of_type_named<GUI::Label>("name");
- name_label.set_text(TRY(String::from_utf8(to_string(role))));
- auto& spin_box = *row_widget->find_descendant_of_type_named<GUI::SpinBox>("spin_box");
- spin_box.on_change = [&, role](int value) {
- set_metric(role, value);
- };
- spin_box.set_value(m_current_palette.metric(role), GUI::AllowCallback::No);
- VERIFY(m_metric_inputs[to_underlying(role)].is_null());
- m_metric_inputs[to_underlying(role)] = spin_box;
- return {};
- },
- [&](Gfx::PathRole role) -> ErrorOr<void> {
- TRY(row_widget->load_from_gml(path_property_gml));
- auto& name_label = *row_widget->find_descendant_of_type_named<GUI::Label>("name");
- name_label.set_text(TRY(String::from_utf8(to_string(role))));
- auto& path_input = *row_widget->find_descendant_of_type_named<GUI::TextBox>("path_input");
- path_input.on_change = [&, role] {
- set_path(role, path_input.text());
- };
- path_input.set_text(m_current_palette.path(role), GUI::AllowCallback::No);
- auto& path_picker_button = *row_widget->find_descendant_of_type_named<GUI::Button>("path_picker_button");
- auto picker_target = (role == Gfx::PathRole::TitleButtonIcons) ? PathPickerTarget::Folder : PathPickerTarget::File;
- path_picker_button.on_click = [&, role, picker_target](auto) {
- show_path_picker_dialog(to_string(role), path_input, picker_target);
- };
- VERIFY(m_path_inputs[to_underlying(role)].is_null());
- m_path_inputs[to_underlying(role)] = path_input;
- return {};
- }));
- }
- }
- return {};
- }
- void MainWidget::set_alignment(Gfx::AlignmentRole role, Gfx::TextAlignment value)
- {
- auto preview_palette = m_current_palette;
- preview_palette.set_alignment(role, value);
- set_palette(preview_palette);
- }
- void MainWidget::set_color(Gfx::ColorRole role, Gfx::Color value)
- {
- auto preview_palette = m_current_palette;
- preview_palette.set_color(role, value);
- set_palette(preview_palette);
- }
- void MainWidget::set_flag(Gfx::FlagRole role, bool value)
- {
- auto preview_palette = m_current_palette;
- preview_palette.set_flag(role, value);
- set_palette(preview_palette);
- }
- void MainWidget::set_metric(Gfx::MetricRole role, int value)
- {
- auto preview_palette = m_current_palette;
- preview_palette.set_metric(role, value);
- set_palette(preview_palette);
- }
- void MainWidget::set_path(Gfx::PathRole role, DeprecatedString value)
- {
- auto preview_palette = m_current_palette;
- preview_palette.set_path(role, value);
- set_palette(preview_palette);
- }
- void MainWidget::set_palette(Gfx::Palette palette)
- {
- m_current_palette = move(palette);
- m_preview_widget->set_preview_palette(m_current_palette);
- m_theme_override_apply->set_enabled(true);
- window()->set_modified(true);
- }
- void MainWidget::show_path_picker_dialog(StringView property_display_name, GUI::TextBox& path_input, PathPickerTarget path_picker_target)
- {
- bool open_folder = path_picker_target == PathPickerTarget::Folder;
- auto window_title = DeprecatedString::formatted(open_folder ? "Select {} folder"sv : "Select {} file"sv, property_display_name);
- auto target_path = path_input.text();
- if (FileSystem::exists(target_path)) {
- if (!FileSystem::is_directory(target_path))
- target_path = LexicalPath::dirname(target_path);
- } else {
- target_path = "/res/icons";
- }
- auto result = GUI::FilePicker::get_open_filepath(window(), window_title, target_path, open_folder);
- if (!result.has_value())
- return;
- path_input.set_text(*result);
- }
- ErrorOr<void> MainWidget::load_from_file(String const& filename, NonnullOwnPtr<Core::File> file)
- {
- auto config_file = TRY(Core::ConfigFile::open(filename.to_deprecated_string(), move(file)));
- auto theme = TRY(Gfx::load_system_theme(config_file));
- VERIFY(theme.is_valid());
- auto new_palette = Gfx::Palette(Gfx::PaletteImpl::create_with_anonymous_buffer(theme));
- set_palette(move(new_palette));
- set_path(filename.to_deprecated_string());
- #define __ENUMERATE_ALIGNMENT_ROLE(role) \
- if (auto alignment_input = m_alignment_inputs[to_underlying(Gfx::AlignmentRole::role)]) \
- alignment_input->set_selected_index(m_alignment_model->index_of(m_current_palette.alignment(Gfx::AlignmentRole::role)), GUI::AllowCallback::No);
- ENUMERATE_ALIGNMENT_ROLES(__ENUMERATE_ALIGNMENT_ROLE)
- #undef __ENUMERATE_ALIGNMENT_ROLE
- #define __ENUMERATE_COLOR_ROLE(role) \
- if (auto color_input = m_color_inputs[to_underlying(Gfx::ColorRole::role)]) \
- color_input->set_color(m_current_palette.color(Gfx::ColorRole::role), GUI::AllowCallback::No);
- ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE)
- #undef __ENUMERATE_COLOR_ROLE
- #define __ENUMERATE_FLAG_ROLE(role) \
- if (auto flag_input = m_flag_inputs[to_underlying(Gfx::FlagRole::role)]) \
- flag_input->set_checked(m_current_palette.flag(Gfx::FlagRole::role), GUI::AllowCallback::No);
- ENUMERATE_FLAG_ROLES(__ENUMERATE_FLAG_ROLE)
- #undef __ENUMERATE_FLAG_ROLE
- #define __ENUMERATE_METRIC_ROLE(role) \
- if (auto metric_input = m_metric_inputs[to_underlying(Gfx::MetricRole::role)]) \
- metric_input->set_value(m_current_palette.metric(Gfx::MetricRole::role), GUI::AllowCallback::No);
- ENUMERATE_METRIC_ROLES(__ENUMERATE_METRIC_ROLE)
- #undef __ENUMERATE_METRIC_ROLE
- #define __ENUMERATE_PATH_ROLE(role) \
- if (auto path_input = m_path_inputs[to_underlying(Gfx::PathRole::role)]) \
- path_input->set_text(m_current_palette.path(Gfx::PathRole::role), GUI::AllowCallback::No);
- ENUMERATE_PATH_ROLES(__ENUMERATE_PATH_ROLE)
- #undef __ENUMERATE_PATH_ROLE
- m_last_modified_time = MonotonicTime::now();
- window()->set_modified(false);
- GUI::Application::the()->set_most_recently_open_file(filename);
- return {};
- }
- void MainWidget::drag_enter_event(GUI::DragEvent& event)
- {
- auto const& mime_types = event.mime_types();
- if (mime_types.contains_slow("text/uri-list"))
- event.accept();
- }
- void MainWidget::drop_event(GUI::DropEvent& event)
- {
- event.accept();
- window()->move_to_front();
- if (event.mime_data().has_urls()) {
- auto urls = event.mime_data().urls();
- if (urls.is_empty())
- return;
- if (urls.size() > 1) {
- GUI::MessageBox::show(window(), "ThemeEditor can only open one file at a time!"sv, "One at a time please!"sv, GUI::MessageBox::Type::Error);
- return;
- }
- if (request_close() == GUI::Window::CloseRequestDecision::StayOpen)
- return;
- auto response = FileSystemAccessClient::Client::the().request_file(window(), urls.first().serialize_path(), Core::File::OpenMode::Read);
- if (response.is_error())
- return;
- auto load_from_file_result = load_from_file(response.value().filename(), response.value().release_stream());
- if (load_from_file_result.is_error())
- GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Can't open file named {}: {}", response.value().filename(), load_from_file_result.error()));
- }
- }
- }
|