mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-20 10:02:51 +00:00
This is quite a radical change. The previous UI was very easy to add new properties to (just add to the FooRole enum and it automatically works), but not so nice to use: A ComboBox for selecting a property, and then a box to edit that property's value. This makes it difficult to compare different properties or edit multiple together without a lot of back-and-forth. This new design gives each property its own editing widgets, with those categorized into several tabs. To try and avoid increasing the maintenance burden for this, the UI is generated from the `windows_tab`, `widgets_tab` and `syntax_highlighting_tab` variables, which are basically just lists of which properties go in that tab. One of the `FooProperty.gml` files is loaded to create each property's widgets.
543 lines
23 KiB
C++
543 lines
23 KiB
C++
/*
|
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
|
* Copyright (c) 2021, Jakob-Niklas See <git@nwex.de>
|
|
* Copyright (c) 2021-2022, 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 <LibFileSystemAccessClient/Client.h>
|
|
#include <LibGUI/ActionGroup.h>
|
|
#include <LibGUI/Application.h>
|
|
#include <LibGUI/BoxLayout.h>
|
|
#include <LibGUI/Button.h>
|
|
#include <LibGUI/FilePicker.h>
|
|
#include <LibGUI/Frame.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 <LibGfx/Filters/ColorBlindnessFilter.h>
|
|
|
|
namespace ThemeEditor {
|
|
|
|
static const PropertyTab window_tab {
|
|
"Windows",
|
|
{
|
|
{ Gfx::FlagRole::IsDark },
|
|
{ Gfx::AlignmentRole::TitleAlignment },
|
|
{ Gfx::MetricRole::TitleHeight },
|
|
{ Gfx::MetricRole::TitleButtonWidth },
|
|
{ Gfx::MetricRole::TitleButtonHeight },
|
|
{ Gfx::PathRole::TitleButtonIcons },
|
|
{ Gfx::FlagRole::TitleButtonsIconOnly },
|
|
|
|
{ Gfx::MetricRole::BorderThickness },
|
|
{ Gfx::MetricRole::BorderRadius },
|
|
|
|
{ Gfx::ColorRole::ActiveWindowBorder1 },
|
|
{ Gfx::ColorRole::ActiveWindowBorder2 },
|
|
{ Gfx::ColorRole::ActiveWindowTitle },
|
|
{ Gfx::ColorRole::ActiveWindowTitleShadow },
|
|
{ Gfx::ColorRole::ActiveWindowTitleStripes },
|
|
{ Gfx::PathRole::ActiveWindowShadow },
|
|
|
|
{ Gfx::ColorRole::InactiveWindowBorder1 },
|
|
{ Gfx::ColorRole::InactiveWindowBorder2 },
|
|
{ Gfx::ColorRole::InactiveWindowTitle },
|
|
{ Gfx::ColorRole::InactiveWindowTitleShadow },
|
|
{ Gfx::ColorRole::InactiveWindowTitleStripes },
|
|
{ Gfx::PathRole::InactiveWindowShadow },
|
|
|
|
{ Gfx::ColorRole::HighlightWindowBorder1 },
|
|
{ Gfx::ColorRole::HighlightWindowBorder2 },
|
|
{ Gfx::ColorRole::HighlightWindowTitle },
|
|
{ Gfx::ColorRole::HighlightWindowTitleShadow },
|
|
{ Gfx::ColorRole::HighlightWindowTitleStripes },
|
|
|
|
{ Gfx::ColorRole::MovingWindowBorder1 },
|
|
{ Gfx::ColorRole::MovingWindowBorder2 },
|
|
{ Gfx::ColorRole::MovingWindowTitle },
|
|
{ Gfx::ColorRole::MovingWindowTitleShadow },
|
|
{ Gfx::ColorRole::MovingWindowTitleStripes },
|
|
|
|
{ Gfx::ColorRole::Window },
|
|
{ Gfx::ColorRole::WindowText },
|
|
|
|
{ Gfx::ColorRole::DesktopBackground },
|
|
{ Gfx::PathRole::TaskbarShadow },
|
|
}
|
|
};
|
|
|
|
static const PropertyTab widgets_tab {
|
|
"Widgets",
|
|
{
|
|
{ Gfx::ColorRole::Accent },
|
|
{ Gfx::ColorRole::Base },
|
|
{ Gfx::ColorRole::ThreedHighlight },
|
|
{ Gfx::ColorRole::ThreedShadow1 },
|
|
{ Gfx::ColorRole::ThreedShadow2 },
|
|
{ Gfx::ColorRole::HoverHighlight },
|
|
|
|
{ Gfx::ColorRole::BaseText },
|
|
{ Gfx::ColorRole::DisabledTextFront },
|
|
{ Gfx::ColorRole::DisabledTextBack },
|
|
{ Gfx::ColorRole::PlaceholderText },
|
|
|
|
{ Gfx::ColorRole::Link },
|
|
{ Gfx::ColorRole::ActiveLink },
|
|
{ Gfx::ColorRole::VisitedLink },
|
|
|
|
{ Gfx::ColorRole::Button },
|
|
{ Gfx::ColorRole::ButtonText },
|
|
|
|
{ Gfx::ColorRole::Tooltip },
|
|
{ Gfx::ColorRole::TooltipText },
|
|
{ Gfx::PathRole::TooltipShadow },
|
|
|
|
{ Gfx::ColorRole::Tray },
|
|
{ Gfx::ColorRole::TrayText },
|
|
|
|
{ Gfx::ColorRole::Ruler },
|
|
{ Gfx::ColorRole::RulerBorder },
|
|
{ Gfx::ColorRole::RulerActiveText },
|
|
{ Gfx::ColorRole::RulerInactiveText },
|
|
|
|
{ Gfx::ColorRole::Gutter },
|
|
{ Gfx::ColorRole::GutterBorder },
|
|
|
|
{ Gfx::ColorRole::RubberBandBorder },
|
|
{ Gfx::ColorRole::RubberBandFill },
|
|
|
|
{ Gfx::ColorRole::MenuBase },
|
|
{ Gfx::ColorRole::MenuBaseText },
|
|
{ Gfx::ColorRole::MenuSelection },
|
|
{ Gfx::ColorRole::MenuSelectionText },
|
|
{ Gfx::ColorRole::MenuStripe },
|
|
{ Gfx::PathRole::MenuShadow },
|
|
|
|
{ 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 const PropertyTab syntax_highlighting_tab {
|
|
"Syntax Highlighting",
|
|
{
|
|
{ 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 },
|
|
}
|
|
};
|
|
|
|
MainWidget::MainWidget(Optional<String> path, Gfx::Palette startup_preview_palette)
|
|
: m_path(path)
|
|
{
|
|
load_from_gml(theme_editor_gml);
|
|
|
|
m_alignment_model = MUST(AlignmentModel::try_create());
|
|
|
|
m_preview_widget = find_descendant_of_type_named<GUI::Frame>("preview_frame")
|
|
->add<ThemeEditor::PreviewWidget>(startup_preview_palette);
|
|
m_property_tabs = find_descendant_of_type_named<GUI::TabWidget>("property_tabs");
|
|
add_property_tab(window_tab);
|
|
add_property_tab(widgets_tab);
|
|
add_property_tab(syntax_highlighting_tab);
|
|
|
|
m_preview_widget->on_palette_change = [&] {
|
|
window()->set_modified(true);
|
|
};
|
|
|
|
m_preview_widget->on_theme_load_from_file = [&](String const& new_path) {
|
|
load_from_file(new_path);
|
|
};
|
|
}
|
|
|
|
ErrorOr<void> MainWidget::initialize_menubar(GUI::Window& window)
|
|
{
|
|
auto file_menu = TRY(window.try_add_menu("&File"));
|
|
TRY(file_menu->try_add_action(GUI::CommonActions::make_open_action([&](auto&) {
|
|
auto response = FileSystemAccessClient::Client::the().try_open_file(&window, "Select theme file", "/res/themes");
|
|
if (response.is_error())
|
|
return;
|
|
m_preview_widget->set_theme_from_file(*response.value());
|
|
})));
|
|
|
|
m_save_action = GUI::CommonActions::make_save_action([&](auto&) {
|
|
if (m_path.has_value()) {
|
|
auto result = FileSystemAccessClient::Client::the().try_request_file(&window, *m_path, Core::OpenMode::ReadWrite | Core::OpenMode::Truncate);
|
|
if (result.is_error())
|
|
return;
|
|
save_to_file(result.value());
|
|
} else {
|
|
auto result = FileSystemAccessClient::Client::the().try_save_file(&window, "Theme", "ini", Core::OpenMode::ReadWrite | Core::OpenMode::Truncate);
|
|
if (result.is_error())
|
|
return;
|
|
save_to_file(result.value());
|
|
}
|
|
});
|
|
TRY(file_menu->try_add_action(*m_save_action));
|
|
|
|
TRY(file_menu->try_add_action(GUI::CommonActions::make_save_as_action([&](auto&) {
|
|
auto result = FileSystemAccessClient::Client::the().try_save_file(&window, "Theme", "ini", Core::OpenMode::ReadWrite | Core::OpenMode::Truncate);
|
|
if (result.is_error())
|
|
return;
|
|
save_to_file(result.value());
|
|
})));
|
|
|
|
TRY(file_menu->try_add_separator());
|
|
TRY(file_menu->try_add_action(GUI::CommonActions::make_quit_action([&](auto&) { GUI::Application::the()->quit(); })));
|
|
|
|
auto accessibility_menu = TRY(window.try_add_menu("&Accessibility"));
|
|
|
|
auto default_accessibility_action = GUI::Action::create_checkable("Default - non-impaired", { Mod_AltGr, Key_1 }, [&](auto&) {
|
|
m_preview_widget->set_color_filter(nullptr);
|
|
});
|
|
default_accessibility_action->set_checked(true);
|
|
|
|
auto pratanopia_accessibility_action = GUI::Action::create_checkable("Protanopia", { Mod_AltGr, Key_2 }, [&](auto&) {
|
|
m_preview_widget->set_color_filter(Gfx::ColorBlindnessFilter::create_protanopia());
|
|
});
|
|
|
|
auto pratanomaly_accessibility_action = GUI::Action::create_checkable("Protanomaly", { Mod_AltGr, Key_3 }, [&](auto&) {
|
|
m_preview_widget->set_color_filter(Gfx::ColorBlindnessFilter::create_protanomaly());
|
|
});
|
|
|
|
auto tritanopia_accessibility_action = GUI::Action::create_checkable("Tritanopia", { Mod_AltGr, Key_4 }, [&](auto&) {
|
|
m_preview_widget->set_color_filter(Gfx::ColorBlindnessFilter::create_tritanopia());
|
|
});
|
|
|
|
auto tritanomaly_accessibility_action = GUI::Action::create_checkable("Tritanomaly", { Mod_AltGr, Key_5 }, [&](auto&) {
|
|
m_preview_widget->set_color_filter(Gfx::ColorBlindnessFilter::create_tritanomaly());
|
|
});
|
|
|
|
auto deuteranopia_accessibility_action = GUI::Action::create_checkable("Deuteranopia", { Mod_AltGr, Key_6 }, [&](auto&) {
|
|
m_preview_widget->set_color_filter(Gfx::ColorBlindnessFilter::create_deuteranopia());
|
|
});
|
|
|
|
auto deuteranomaly_accessibility_action = GUI::Action::create_checkable("Deuteranomaly", { Mod_AltGr, Key_7 }, [&](auto&) {
|
|
m_preview_widget->set_color_filter(Gfx::ColorBlindnessFilter::create_deuteranomaly());
|
|
});
|
|
|
|
auto achromatopsia_accessibility_action = GUI::Action::create_checkable("Achromatopsia", { Mod_AltGr, Key_8 }, [&](auto&) {
|
|
m_preview_widget->set_color_filter(Gfx::ColorBlindnessFilter::create_achromatopsia());
|
|
});
|
|
|
|
auto achromatomaly_accessibility_action = GUI::Action::create_checkable("Achromatomaly", { Mod_AltGr, Key_9 }, [&](auto&) {
|
|
m_preview_widget->set_color_filter(Gfx::ColorBlindnessFilter::create_achromatomaly());
|
|
});
|
|
|
|
m_preview_type_action_group = make<GUI::ActionGroup>();
|
|
m_preview_type_action_group->set_exclusive(true);
|
|
m_preview_type_action_group->add_action(*default_accessibility_action);
|
|
m_preview_type_action_group->add_action(*pratanopia_accessibility_action);
|
|
m_preview_type_action_group->add_action(*pratanomaly_accessibility_action);
|
|
m_preview_type_action_group->add_action(*tritanopia_accessibility_action);
|
|
m_preview_type_action_group->add_action(*tritanomaly_accessibility_action);
|
|
m_preview_type_action_group->add_action(*deuteranopia_accessibility_action);
|
|
m_preview_type_action_group->add_action(*deuteranomaly_accessibility_action);
|
|
m_preview_type_action_group->add_action(*achromatopsia_accessibility_action);
|
|
m_preview_type_action_group->add_action(*achromatomaly_accessibility_action);
|
|
|
|
TRY(accessibility_menu->try_add_action(default_accessibility_action));
|
|
TRY(accessibility_menu->try_add_action(pratanopia_accessibility_action));
|
|
TRY(accessibility_menu->try_add_action(pratanomaly_accessibility_action));
|
|
TRY(accessibility_menu->try_add_action(tritanopia_accessibility_action));
|
|
TRY(accessibility_menu->try_add_action(tritanomaly_accessibility_action));
|
|
TRY(accessibility_menu->try_add_action(deuteranopia_accessibility_action));
|
|
TRY(accessibility_menu->try_add_action(deuteranomaly_accessibility_action));
|
|
TRY(accessibility_menu->try_add_action(achromatopsia_accessibility_action));
|
|
TRY(accessibility_menu->try_add_action(achromatomaly_accessibility_action));
|
|
|
|
auto help_menu = TRY(window.try_add_menu("&Help"));
|
|
TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("Theme Editor", GUI::Icon::default_icon("app-theme-editor"), &window)));
|
|
|
|
return {};
|
|
}
|
|
|
|
void MainWidget::update_title()
|
|
{
|
|
window()->set_title(String::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(String path)
|
|
{
|
|
m_path = path;
|
|
update_title();
|
|
}
|
|
|
|
void MainWidget::save_to_file(Core::File& file)
|
|
{
|
|
auto theme = Core::ConfigFile::open(file.filename(), file.leak_fd()).release_value_but_fixme_should_propagate_errors();
|
|
|
|
#define __ENUMERATE_ALIGNMENT_ROLE(role) theme->write_entry("Alignments", to_string(Gfx::AlignmentRole::role), to_string(m_preview_widget->preview_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_preview_widget->preview_palette().color(Gfx::ColorRole::role).to_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_preview_widget->preview_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_preview_widget->preview_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_preview_widget->preview_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(), String::formatted("Failed to save theme file: {}", sync_result.error()));
|
|
} else {
|
|
m_last_modified_time = Time::now_monotonic();
|
|
set_path(file.filename());
|
|
window()->set_modified(false);
|
|
}
|
|
}
|
|
|
|
void MainWidget::add_property_tab(PropertyTab const& property_tab)
|
|
{
|
|
auto& scrollable_container = m_property_tabs->add_tab<GUI::ScrollableContainerWidget>(property_tab.title);
|
|
scrollable_container.set_should_hide_unnecessary_scrollbars(true);
|
|
|
|
auto properties_list = GUI::Widget::construct();
|
|
scrollable_container.set_widget(properties_list);
|
|
properties_list->set_layout<GUI::VerticalBoxLayout>();
|
|
properties_list->layout()->set_spacing(12);
|
|
properties_list->layout()->set_margins({ 8 });
|
|
|
|
for (auto const& property : property_tab.properties) {
|
|
NonnullRefPtr<GUI::Widget> row_widget = properties_list->add<GUI::Widget>();
|
|
row_widget->set_fixed_height(24);
|
|
|
|
property.role.visit(
|
|
[&](Gfx::AlignmentRole role) {
|
|
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(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_preview_widget->preview_palette().alignment(role)), GUI::AllowCallback::No);
|
|
|
|
VERIFY(m_alignment_inputs[to_underlying(role)].is_null());
|
|
m_alignment_inputs[to_underlying(role)] = alignment_picker;
|
|
},
|
|
[&](Gfx::ColorRole role) {
|
|
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(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_preview_widget->preview_palette().color(role), GUI::AllowCallback::No);
|
|
|
|
VERIFY(m_color_inputs[to_underlying(role)].is_null());
|
|
m_color_inputs[to_underlying(role)] = color_input;
|
|
},
|
|
[&](Gfx::FlagRole role) {
|
|
row_widget->load_from_gml(flag_property_gml);
|
|
|
|
auto& checkbox = *row_widget->find_descendant_of_type_named<GUI::CheckBox>("checkbox");
|
|
checkbox.set_text(to_string(role));
|
|
checkbox.on_checked = [&, role](bool checked) {
|
|
set_flag(role, checked);
|
|
};
|
|
checkbox.set_checked(m_preview_widget->preview_palette().flag(role), GUI::AllowCallback::No);
|
|
|
|
VERIFY(m_flag_inputs[to_underlying(role)].is_null());
|
|
m_flag_inputs[to_underlying(role)] = checkbox;
|
|
},
|
|
[&](Gfx::MetricRole role) {
|
|
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(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_preview_widget->preview_palette().metric(role), GUI::AllowCallback::No);
|
|
|
|
VERIFY(m_metric_inputs[to_underlying(role)].is_null());
|
|
m_metric_inputs[to_underlying(role)] = spin_box;
|
|
},
|
|
[&](Gfx::PathRole role) {
|
|
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(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_preview_widget->preview_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;
|
|
});
|
|
}
|
|
}
|
|
|
|
void MainWidget::set_alignment(Gfx::AlignmentRole role, Gfx::TextAlignment value)
|
|
{
|
|
auto preview_palette = m_preview_widget->preview_palette();
|
|
preview_palette.set_alignment(role, value);
|
|
m_preview_widget->set_preview_palette(preview_palette);
|
|
}
|
|
|
|
void MainWidget::set_color(Gfx::ColorRole role, Gfx::Color value)
|
|
{
|
|
auto preview_palette = m_preview_widget->preview_palette();
|
|
preview_palette.set_color(role, value);
|
|
m_preview_widget->set_preview_palette(preview_palette);
|
|
}
|
|
|
|
void MainWidget::set_flag(Gfx::FlagRole role, bool value)
|
|
{
|
|
auto preview_palette = m_preview_widget->preview_palette();
|
|
preview_palette.set_flag(role, value);
|
|
m_preview_widget->set_preview_palette(preview_palette);
|
|
}
|
|
|
|
void MainWidget::set_metric(Gfx::MetricRole role, int value)
|
|
{
|
|
auto preview_palette = m_preview_widget->preview_palette();
|
|
preview_palette.set_metric(role, value);
|
|
m_preview_widget->set_preview_palette(preview_palette);
|
|
}
|
|
|
|
void MainWidget::set_path(Gfx::PathRole role, String value)
|
|
{
|
|
auto preview_palette = m_preview_widget->preview_palette();
|
|
preview_palette.set_path(role, value);
|
|
m_preview_widget->set_preview_palette(preview_palette);
|
|
}
|
|
|
|
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 = String::formatted(open_folder ? "Select {} folder" : "Select {} file", property_display_name);
|
|
auto target_path = path_input.text();
|
|
if (Core::File::exists(target_path)) {
|
|
if (!Core::File::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);
|
|
}
|
|
|
|
void MainWidget::load_from_file(String const& new_path)
|
|
{
|
|
set_path(new_path);
|
|
|
|
#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_preview_widget->preview_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_preview_widget->preview_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_preview_widget->preview_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_preview_widget->preview_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_preview_widget->preview_palette().path(Gfx::PathRole::role), GUI::AllowCallback::No);
|
|
ENUMERATE_PATH_ROLES(__ENUMERATE_PATH_ROLE)
|
|
#undef __ENUMERATE_PATH_ROLE
|
|
|
|
m_last_modified_time = Time::now_monotonic();
|
|
window()->set_modified(false);
|
|
}
|
|
|
|
}
|