mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 23:07:35 +00:00
ThemeEditor: Give each theme property its own editing widget
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.
This commit is contained in:
parent
aadb35ff46
commit
423383e9aa
10 changed files with 514 additions and 322 deletions
18
Userland/Applications/ThemeEditor/AlignmentProperty.gml
Normal file
18
Userland/Applications/ThemeEditor/AlignmentProperty.gml
Normal file
|
@ -0,0 +1,18 @@
|
|||
@GUI::Frame {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
spacing: 4
|
||||
}
|
||||
shrink_to_fit: true
|
||||
|
||||
@GUI::Label {
|
||||
name: "name"
|
||||
text: "Some alignment"
|
||||
text_alignment: "CenterLeft"
|
||||
fixed_width: 200
|
||||
}
|
||||
|
||||
@GUI::ComboBox {
|
||||
name: "combo_box"
|
||||
model_only: true
|
||||
}
|
||||
}
|
|
@ -4,11 +4,21 @@ serenity_component(
|
|||
)
|
||||
|
||||
compile_gml(ThemeEditor.gml ThemeEditorGML.h theme_editor_gml)
|
||||
compile_gml(AlignmentProperty.gml AlignmentPropertyGML.h alignment_property_gml)
|
||||
compile_gml(ColorProperty.gml ColorPropertyGML.h color_property_gml)
|
||||
compile_gml(FlagProperty.gml FlagPropertyGML.h flag_property_gml)
|
||||
compile_gml(MetricProperty.gml MetricPropertyGML.h metric_property_gml)
|
||||
compile_gml(PathProperty.gml PathPropertyGML.h path_property_gml)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
MainWidget.cpp
|
||||
PreviewWidget.cpp
|
||||
AlignmentPropertyGML.h
|
||||
ColorPropertyGML.h
|
||||
FlagPropertyGML.h
|
||||
MetricPropertyGML.h
|
||||
PathPropertyGML.h
|
||||
ThemeEditorGML.h
|
||||
)
|
||||
|
||||
|
|
17
Userland/Applications/ThemeEditor/ColorProperty.gml
Normal file
17
Userland/Applications/ThemeEditor/ColorProperty.gml
Normal file
|
@ -0,0 +1,17 @@
|
|||
@GUI::Frame {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
spacing: 4
|
||||
}
|
||||
shrink_to_fit: true
|
||||
|
||||
@GUI::Label {
|
||||
name: "name"
|
||||
text: "Some color"
|
||||
text_alignment: "CenterLeft"
|
||||
fixed_width: 200
|
||||
}
|
||||
|
||||
@GUI::ColorInput {
|
||||
name: "color_input"
|
||||
}
|
||||
}
|
12
Userland/Applications/ThemeEditor/FlagProperty.gml
Normal file
12
Userland/Applications/ThemeEditor/FlagProperty.gml
Normal file
|
@ -0,0 +1,12 @@
|
|||
@GUI::Frame {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
spacing: 4
|
||||
}
|
||||
shrink_to_fit: true
|
||||
|
||||
@GUI::CheckBox {
|
||||
name: "checkbox"
|
||||
text: "Some flag"
|
||||
checkbox_position: "Right"
|
||||
}
|
||||
}
|
|
@ -9,87 +9,157 @@
|
|||
*/
|
||||
|
||||
#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/CheckBox.h>
|
||||
#include <LibGUI/ColorInput.h>
|
||||
#include <LibGUI/ComboBox.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/SpinBox.h>
|
||||
#include <LibGUI/TextBox.h>
|
||||
#include <LibGUI/ScrollableContainerWidget.h>
|
||||
#include <LibGfx/Filters/ColorBlindnessFilter.h>
|
||||
|
||||
namespace ThemeEditor {
|
||||
|
||||
template<typename T>
|
||||
class RoleModel final : public GUI::ItemListModel<T> {
|
||||
public:
|
||||
static ErrorOr<NonnullRefPtr<RoleModel>> try_create(Vector<T> const& data)
|
||||
static const PropertyTab window_tab {
|
||||
"Windows",
|
||||
{
|
||||
return adopt_nonnull_ref_or_enomem(new (nothrow) RoleModel<T>(data));
|
||||
}
|
||||
{ Gfx::FlagRole::IsDark },
|
||||
{ Gfx::AlignmentRole::TitleAlignment },
|
||||
{ Gfx::MetricRole::TitleHeight },
|
||||
{ Gfx::MetricRole::TitleButtonWidth },
|
||||
{ Gfx::MetricRole::TitleButtonHeight },
|
||||
{ Gfx::PathRole::TitleButtonIcons },
|
||||
{ Gfx::FlagRole::TitleButtonsIconOnly },
|
||||
|
||||
virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override
|
||||
{
|
||||
if (role == GUI::ModelRole::Display)
|
||||
return Gfx::to_string(this->m_data[index.row()]);
|
||||
if (role == GUI::ModelRole::Custom)
|
||||
return this->m_data[index.row()];
|
||||
{ Gfx::MetricRole::BorderThickness },
|
||||
{ Gfx::MetricRole::BorderRadius },
|
||||
|
||||
return GUI::ItemListModel<T>::data(index, role);
|
||||
}
|
||||
{ Gfx::ColorRole::ActiveWindowBorder1 },
|
||||
{ Gfx::ColorRole::ActiveWindowBorder2 },
|
||||
{ Gfx::ColorRole::ActiveWindowTitle },
|
||||
{ Gfx::ColorRole::ActiveWindowTitleShadow },
|
||||
{ Gfx::ColorRole::ActiveWindowTitleStripes },
|
||||
{ Gfx::PathRole::ActiveWindowShadow },
|
||||
|
||||
private:
|
||||
explicit RoleModel(Vector<T> const& data)
|
||||
: GUI::ItemListModel<T>(data)
|
||||
{
|
||||
{ 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 },
|
||||
}
|
||||
};
|
||||
|
||||
class AlignmentModel final : public GUI::Model {
|
||||
public:
|
||||
static ErrorOr<NonnullRefPtr<AlignmentModel>> try_create()
|
||||
static const PropertyTab widgets_tab {
|
||||
"Widgets",
|
||||
{
|
||||
return adopt_nonnull_ref_or_enomem(new (nothrow) AlignmentModel());
|
||||
{ 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 },
|
||||
}
|
||||
};
|
||||
|
||||
virtual ~AlignmentModel() = default;
|
||||
|
||||
virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return 3; }
|
||||
virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return 2; }
|
||||
|
||||
virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override
|
||||
static const PropertyTab syntax_highlighting_tab {
|
||||
"Syntax Highlighting",
|
||||
{
|
||||
if (role == GUI::ModelRole::Display)
|
||||
return m_alignments[index.row()].title;
|
||||
if (role == GUI::ModelRole::Custom)
|
||||
return m_alignments[index.row()].setting_value;
|
||||
|
||||
return {};
|
||||
{ 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 },
|
||||
}
|
||||
|
||||
private:
|
||||
AlignmentModel() = default;
|
||||
|
||||
struct AlignmentValue {
|
||||
String title;
|
||||
Gfx::TextAlignment setting_value;
|
||||
};
|
||||
Vector<AlignmentValue> m_alignments {
|
||||
{ "Center", Gfx::TextAlignment::Center },
|
||||
{ "Left", Gfx::TextAlignment::CenterLeft },
|
||||
{ "Right", Gfx::TextAlignment::CenterRight },
|
||||
};
|
||||
};
|
||||
|
||||
MainWidget::MainWidget(Optional<String> path, Gfx::Palette startup_preview_palette)
|
||||
|
@ -97,163 +167,21 @@ MainWidget::MainWidget(Optional<String> path, Gfx::Palette startup_preview_palet
|
|||
{
|
||||
load_from_gml(theme_editor_gml);
|
||||
|
||||
#define __ENUMERATE_COLOR_ROLE(role) m_color_roles.append(Gfx::ColorRole::role);
|
||||
ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE)
|
||||
#undef __ENUMERATE_COLOR_ROLE
|
||||
|
||||
#define __ENUMERATE_ALIGNMENT_ROLE(role) m_alignment_roles.append(Gfx::AlignmentRole::role);
|
||||
ENUMERATE_ALIGNMENT_ROLES(__ENUMERATE_ALIGNMENT_ROLE)
|
||||
#undef __ENUMERATE_ALIGNMENT_ROLE
|
||||
|
||||
#define __ENUMERATE_FLAG_ROLE(role) m_flag_roles.append(Gfx::FlagRole::role);
|
||||
ENUMERATE_FLAG_ROLES(__ENUMERATE_FLAG_ROLE)
|
||||
#undef __ENUMERATE_FLAG_ROLE
|
||||
|
||||
#define __ENUMERATE_METRIC_ROLE(role) m_metric_roles.append(Gfx::MetricRole::role);
|
||||
ENUMERATE_METRIC_ROLES(__ENUMERATE_METRIC_ROLE)
|
||||
#undef __ENUMERATE_METRIC_ROLE
|
||||
|
||||
#define __ENUMERATE_PATH_ROLE(role) m_path_roles.append(Gfx::PathRole::role);
|
||||
ENUMERATE_PATH_ROLES(__ENUMERATE_PATH_ROLE)
|
||||
#undef __ENUMERATE_PATH_ROLE
|
||||
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);
|
||||
auto& color_combo_box = *find_descendant_of_type_named<GUI::ComboBox>("color_combo_box");
|
||||
auto& color_input = *find_descendant_of_type_named<GUI::ColorInput>("color_input");
|
||||
|
||||
auto& alignment_combo_box = *find_descendant_of_type_named<GUI::ComboBox>("alignment_combo_box");
|
||||
auto& alignment_input = *find_descendant_of_type_named<GUI::ComboBox>("alignment_input");
|
||||
|
||||
auto& flag_combo_box = *find_descendant_of_type_named<GUI::ComboBox>("flag_combo_box");
|
||||
auto& flag_input = *find_descendant_of_type_named<GUI::CheckBox>("flag_input");
|
||||
|
||||
auto& metric_combo_box = *find_descendant_of_type_named<GUI::ComboBox>("metric_combo_box");
|
||||
auto& metric_input = *find_descendant_of_type_named<GUI::SpinBox>("metric_input");
|
||||
|
||||
auto& path_combo_box = *find_descendant_of_type_named<GUI::ComboBox>("path_combo_box");
|
||||
auto& path_input = *find_descendant_of_type_named<GUI::TextBox>("path_input");
|
||||
auto& path_picker_button = *find_descendant_of_type_named<GUI::Button>("path_picker_button");
|
||||
|
||||
color_combo_box.set_model(MUST(RoleModel<Gfx::ColorRole>::try_create(m_color_roles)));
|
||||
color_combo_box.on_change = [&](auto&, auto& index) {
|
||||
auto role = index.model()->data(index, GUI::ModelRole::Custom).to_color_role();
|
||||
color_input.set_color(m_preview_widget->preview_palette().color(role), GUI::AllowCallback::No);
|
||||
};
|
||||
color_combo_box.set_selected_index((size_t)Gfx::ColorRole::Window - 1);
|
||||
|
||||
color_input.on_change = [&] {
|
||||
auto role = color_combo_box.model()->index(color_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_color_role();
|
||||
auto preview_palette = m_preview_widget->preview_palette();
|
||||
preview_palette.set_color(role, color_input.color());
|
||||
m_preview_widget->set_preview_palette(preview_palette);
|
||||
};
|
||||
color_input.set_color(startup_preview_palette.color(Gfx::ColorRole::Window), GUI::AllowCallback::No);
|
||||
|
||||
alignment_combo_box.set_model(MUST(RoleModel<Gfx::AlignmentRole>::try_create(m_alignment_roles)));
|
||||
alignment_combo_box.on_change = [&](auto&, auto& index) {
|
||||
auto role = index.model()->data(index, GUI::ModelRole::Custom).to_alignment_role();
|
||||
alignment_input.set_selected_index((size_t)m_preview_widget->preview_palette().alignment(role), GUI::AllowCallback::No);
|
||||
};
|
||||
alignment_combo_box.set_selected_index((size_t)Gfx::AlignmentRole::TitleAlignment - 1);
|
||||
|
||||
alignment_input.set_only_allow_values_from_model(true);
|
||||
alignment_input.set_model(MUST(AlignmentModel::try_create()));
|
||||
alignment_input.set_selected_index((size_t)startup_preview_palette.alignment(Gfx::AlignmentRole::TitleAlignment), GUI::AllowCallback::No);
|
||||
alignment_input.on_change = [&](auto&, auto& index) {
|
||||
auto role = alignment_combo_box.model()->index(alignment_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_alignment_role();
|
||||
auto preview_palette = m_preview_widget->preview_palette();
|
||||
|
||||
preview_palette.set_alignment(role, index.data(GUI::ModelRole::Custom).to_text_alignment(Gfx::TextAlignment::CenterLeft));
|
||||
m_preview_widget->set_preview_palette(preview_palette);
|
||||
};
|
||||
|
||||
flag_combo_box.set_model(MUST(RoleModel<Gfx::FlagRole>::try_create(m_flag_roles)));
|
||||
flag_combo_box.on_change = [&](auto&, auto& index) {
|
||||
auto role = index.model()->data(index, GUI::ModelRole::Custom).to_flag_role();
|
||||
flag_input.set_checked(m_preview_widget->preview_palette().flag(role), GUI::AllowCallback::No);
|
||||
};
|
||||
flag_combo_box.set_selected_index((size_t)Gfx::FlagRole::IsDark - 1);
|
||||
|
||||
flag_input.on_checked = [&](bool checked) {
|
||||
auto role = flag_combo_box.model()->index(flag_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_flag_role();
|
||||
auto preview_palette = m_preview_widget->preview_palette();
|
||||
preview_palette.set_flag(role, checked);
|
||||
m_preview_widget->set_preview_palette(preview_palette);
|
||||
};
|
||||
flag_input.set_checked(startup_preview_palette.flag(Gfx::FlagRole::IsDark), GUI::AllowCallback::No);
|
||||
|
||||
metric_combo_box.set_model(MUST(RoleModel<Gfx::MetricRole>::try_create(m_metric_roles)));
|
||||
metric_combo_box.on_change = [&](auto&, auto& index) {
|
||||
auto role = index.model()->data(index, GUI::ModelRole::Custom).to_metric_role();
|
||||
metric_input.set_value(m_preview_widget->preview_palette().metric(role), GUI::AllowCallback::No);
|
||||
};
|
||||
metric_combo_box.set_selected_index((size_t)Gfx::MetricRole::TitleButtonHeight - 1);
|
||||
|
||||
metric_input.on_change = [&](int value) {
|
||||
auto role = metric_combo_box.model()->index(metric_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_metric_role();
|
||||
auto preview_palette = m_preview_widget->preview_palette();
|
||||
preview_palette.set_metric(role, value);
|
||||
m_preview_widget->set_preview_palette(preview_palette);
|
||||
};
|
||||
metric_input.set_value(startup_preview_palette.metric(Gfx::MetricRole::TitleButtonHeight), GUI::AllowCallback::No);
|
||||
|
||||
path_combo_box.set_model(MUST(RoleModel<Gfx::PathRole>::try_create(m_path_roles)));
|
||||
path_combo_box.on_change = [&](auto&, auto& index) {
|
||||
auto role = index.model()->data(index, GUI::ModelRole::Custom).to_path_role();
|
||||
path_input.set_text(m_preview_widget->preview_palette().path(role), GUI::AllowCallback::No);
|
||||
};
|
||||
path_combo_box.set_selected_index((size_t)Gfx::PathRole::TitleButtonIcons - 1);
|
||||
|
||||
path_input.on_change = [&] {
|
||||
auto role = path_combo_box.model()->index(path_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_path_role();
|
||||
auto preview_palette = m_preview_widget->preview_palette();
|
||||
preview_palette.set_path(role, path_input.text());
|
||||
m_preview_widget->set_preview_palette(preview_palette);
|
||||
};
|
||||
path_input.set_text(startup_preview_palette.path(Gfx::PathRole::TitleButtonIcons), GUI::AllowCallback::No);
|
||||
|
||||
path_picker_button.on_click = [&](auto) {
|
||||
auto role = path_combo_box.model()->index(path_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_path_role();
|
||||
bool open_folder = (role == Gfx::PathRole::TitleButtonIcons);
|
||||
auto window_title = String::formatted(open_folder ? "Select {} folder" : "Select {} file", path_combo_box.text());
|
||||
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);
|
||||
};
|
||||
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) {
|
||||
set_path(new_path);
|
||||
|
||||
auto selected_color_role = color_combo_box.model()->index(color_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_color_role();
|
||||
color_input.set_color(m_preview_widget->preview_palette().color(selected_color_role), GUI::AllowCallback::No);
|
||||
|
||||
auto selected_alignment_role = alignment_combo_box.model()->index(alignment_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_alignment_role();
|
||||
alignment_input.set_selected_index((size_t)(m_preview_widget->preview_palette().alignment(selected_alignment_role), GUI::AllowCallback::No));
|
||||
|
||||
auto selected_flag_role = flag_combo_box.model()->index(flag_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_flag_role();
|
||||
flag_input.set_checked(m_preview_widget->preview_palette().flag(selected_flag_role), GUI::AllowCallback::No);
|
||||
|
||||
auto selected_metric_role = metric_combo_box.model()->index(metric_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_metric_role();
|
||||
metric_input.set_value(m_preview_widget->preview_palette().metric(selected_metric_role), GUI::AllowCallback::No);
|
||||
|
||||
auto selected_path_role = path_combo_box.model()->index(path_combo_box.selected_index()).data(GUI::ModelRole::Custom).to_path_role();
|
||||
path_input.set_text(m_preview_widget->preview_palette().path(selected_path_role), GUI::AllowCallback::No);
|
||||
|
||||
m_last_modified_time = Time::now_monotonic();
|
||||
window()->set_modified(false);
|
||||
load_from_file(new_path);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -393,25 +321,25 @@ 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();
|
||||
|
||||
for (auto role : m_alignment_roles) {
|
||||
theme->write_entry("Alignments", to_string(role), to_string(m_preview_widget->preview_palette().alignment(role)));
|
||||
}
|
||||
#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
|
||||
|
||||
for (auto role : m_color_roles) {
|
||||
theme->write_entry("Colors", to_string(role), m_preview_widget->preview_palette().color(role).to_string());
|
||||
}
|
||||
#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
|
||||
|
||||
for (auto role : m_flag_roles) {
|
||||
theme->write_bool_entry("Flags", to_string(role), m_preview_widget->preview_palette().flag(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
|
||||
|
||||
for (auto role : m_metric_roles) {
|
||||
theme->write_num_entry("Metrics", to_string(role), m_preview_widget->preview_palette().metric(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
|
||||
|
||||
for (auto role : m_path_roles) {
|
||||
theme->write_entry("Paths", to_string(role), m_preview_widget->preview_palette().path(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()) {
|
||||
|
@ -423,4 +351,193 @@ void MainWidget::save_to_file(Core::File& file)
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,13 +7,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "PreviewWidget.h"
|
||||
#include <AK/FixedArray.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
#include <LibGUI/CheckBox.h>
|
||||
#include <LibGUI/ColorInput.h>
|
||||
#include <LibGUI/ComboBox.h>
|
||||
#include <LibGUI/SpinBox.h>
|
||||
#include <LibGUI/TabWidget.h>
|
||||
#include <LibGUI/TextBox.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibGfx/SystemTheme.h>
|
||||
|
||||
namespace ThemeEditor {
|
||||
|
||||
class AlignmentModel final : public GUI::Model {
|
||||
public:
|
||||
static ErrorOr<NonnullRefPtr<AlignmentModel>> try_create()
|
||||
{
|
||||
return adopt_nonnull_ref_or_enomem(new (nothrow) AlignmentModel());
|
||||
}
|
||||
|
||||
virtual ~AlignmentModel() = default;
|
||||
|
||||
virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return 3; }
|
||||
virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return 2; }
|
||||
|
||||
virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override
|
||||
{
|
||||
if (role == GUI::ModelRole::Display)
|
||||
return m_alignments[index.row()].title;
|
||||
if (role == GUI::ModelRole::Custom)
|
||||
return m_alignments[index.row()].setting_value;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t index_of(Gfx::TextAlignment alignment) const
|
||||
{
|
||||
auto match = m_alignments.find_if([&](auto& it) { return it.setting_value == alignment; });
|
||||
return match.index();
|
||||
}
|
||||
|
||||
private:
|
||||
AlignmentModel() = default;
|
||||
|
||||
struct AlignmentValue {
|
||||
String title;
|
||||
Gfx::TextAlignment setting_value;
|
||||
};
|
||||
Vector<AlignmentValue> m_alignments {
|
||||
{ "Center", Gfx::TextAlignment::Center },
|
||||
{ "Left", Gfx::TextAlignment::CenterLeft },
|
||||
{ "Right", Gfx::TextAlignment::CenterRight },
|
||||
};
|
||||
};
|
||||
|
||||
struct Property {
|
||||
Variant<Gfx::AlignmentRole, Gfx::ColorRole, Gfx::FlagRole, Gfx::MetricRole, Gfx::PathRole> role;
|
||||
};
|
||||
|
||||
struct PropertyTab {
|
||||
String title;
|
||||
Vector<Property> properties;
|
||||
};
|
||||
|
||||
class MainWidget final : public GUI::Widget {
|
||||
C_OBJECT(MainWidget);
|
||||
|
||||
|
@ -27,20 +84,37 @@ public:
|
|||
private:
|
||||
explicit MainWidget(Optional<String> path, Gfx::Palette startup_preview_palette);
|
||||
|
||||
void load_from_file(String const& path);
|
||||
void save_to_file(Core::File&);
|
||||
void set_path(String);
|
||||
|
||||
void add_property_tab(PropertyTab const&);
|
||||
void set_alignment(Gfx::AlignmentRole, Gfx::TextAlignment);
|
||||
void set_color(Gfx::ColorRole, Gfx::Color);
|
||||
void set_flag(Gfx::FlagRole, bool);
|
||||
void set_metric(Gfx::MetricRole, int);
|
||||
void set_path(Gfx::PathRole, String);
|
||||
|
||||
enum class PathPickerTarget {
|
||||
File,
|
||||
Folder,
|
||||
};
|
||||
void show_path_picker_dialog(StringView property_display_name, GUI::TextBox&, PathPickerTarget);
|
||||
|
||||
RefPtr<PreviewWidget> m_preview_widget;
|
||||
RefPtr<GUI::TabWidget> m_property_tabs;
|
||||
RefPtr<GUI::Action> m_save_action;
|
||||
|
||||
Optional<String> m_path;
|
||||
Time m_last_modified_time { Time::now_monotonic() };
|
||||
|
||||
Vector<Gfx::AlignmentRole> m_alignment_roles;
|
||||
Vector<Gfx::ColorRole> m_color_roles;
|
||||
Vector<Gfx::FlagRole> m_flag_roles;
|
||||
Vector<Gfx::MetricRole> m_metric_roles;
|
||||
Vector<Gfx::PathRole> m_path_roles;
|
||||
RefPtr<AlignmentModel> m_alignment_model;
|
||||
|
||||
Array<RefPtr<GUI::ComboBox>, to_underlying(Gfx::AlignmentRole::__Count)> m_alignment_inputs;
|
||||
Array<RefPtr<GUI::ColorInput>, to_underlying(Gfx::ColorRole::__Count)> m_color_inputs;
|
||||
Array<RefPtr<GUI::CheckBox>, to_underlying(Gfx::FlagRole::__Count)> m_flag_inputs;
|
||||
Array<RefPtr<GUI::SpinBox>, to_underlying(Gfx::MetricRole::__Count)> m_metric_inputs;
|
||||
Array<RefPtr<GUI::TextBox>, to_underlying(Gfx::PathRole::__Count)> m_path_inputs;
|
||||
|
||||
OwnPtr<GUI::ActionGroup> m_preview_type_action_group;
|
||||
};
|
||||
|
|
17
Userland/Applications/ThemeEditor/MetricProperty.gml
Normal file
17
Userland/Applications/ThemeEditor/MetricProperty.gml
Normal file
|
@ -0,0 +1,17 @@
|
|||
@GUI::Frame {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
spacing: 4
|
||||
}
|
||||
shrink_to_fit: true
|
||||
|
||||
@GUI::Label {
|
||||
name: "name"
|
||||
text: "Some metric"
|
||||
text_alignment: "CenterLeft"
|
||||
fixed_width: 200
|
||||
}
|
||||
|
||||
@GUI::SpinBox {
|
||||
name: "spin_box"
|
||||
}
|
||||
}
|
24
Userland/Applications/ThemeEditor/PathProperty.gml
Normal file
24
Userland/Applications/ThemeEditor/PathProperty.gml
Normal file
|
@ -0,0 +1,24 @@
|
|||
@GUI::Frame {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
spacing: 4
|
||||
}
|
||||
shrink_to_fit: true
|
||||
|
||||
@GUI::Label {
|
||||
name: "name"
|
||||
text: "Some path"
|
||||
text_alignment: "CenterLeft"
|
||||
fixed_width: 200
|
||||
}
|
||||
|
||||
@GUI::TextBox {
|
||||
name: "path_input"
|
||||
}
|
||||
|
||||
@GUI::Button {
|
||||
name: "path_picker_button"
|
||||
fixed_width: 20
|
||||
text: "..."
|
||||
tooltip: "Choose..."
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
@GUI::Widget {
|
||||
layout: @GUI::VerticalBoxLayout {}
|
||||
layout: @GUI::HorizontalBoxLayout {}
|
||||
fill_with_background_color: true
|
||||
|
||||
@GUI::Frame {
|
||||
|
@ -7,104 +7,7 @@
|
|||
name: "preview_frame"
|
||||
}
|
||||
|
||||
@GUI::GroupBox {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
margins: [4, 4, 4, 4]
|
||||
}
|
||||
shrink_to_fit: true
|
||||
title: "Colors"
|
||||
|
||||
@GUI::ComboBox {
|
||||
name: "color_combo_box"
|
||||
model_only: true
|
||||
fixed_width: 230
|
||||
}
|
||||
|
||||
@GUI::ColorInput {
|
||||
name: "color_input"
|
||||
}
|
||||
}
|
||||
|
||||
@GUI::GroupBox {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
margins: [4, 4, 4, 4]
|
||||
}
|
||||
shrink_to_fit: true
|
||||
title: "Alignments"
|
||||
|
||||
@GUI::ComboBox {
|
||||
name: "alignment_combo_box"
|
||||
model_only: true
|
||||
fixed_width: 230
|
||||
}
|
||||
|
||||
@GUI::ComboBox {
|
||||
name: "alignment_input"
|
||||
}
|
||||
}
|
||||
|
||||
@GUI::GroupBox {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
margins: [4, 4, 4, 4]
|
||||
}
|
||||
shrink_to_fit: true
|
||||
title: "Flags"
|
||||
|
||||
@GUI::ComboBox {
|
||||
name: "flag_combo_box"
|
||||
model_only: true
|
||||
fixed_width: 230
|
||||
}
|
||||
|
||||
@GUI::Widget {}
|
||||
|
||||
@GUI::CheckBox {
|
||||
name: "flag_input"
|
||||
fixed_width: 13
|
||||
}
|
||||
}
|
||||
|
||||
@GUI::GroupBox {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
margins: [4, 4, 4, 4]
|
||||
}
|
||||
shrink_to_fit: true
|
||||
title: "Metrics"
|
||||
|
||||
@GUI::ComboBox {
|
||||
name: "metric_combo_box"
|
||||
model_only: true
|
||||
fixed_width: 230
|
||||
}
|
||||
|
||||
@GUI::SpinBox {
|
||||
name: "metric_input"
|
||||
}
|
||||
}
|
||||
|
||||
@GUI::GroupBox {
|
||||
layout: @GUI::HorizontalBoxLayout {
|
||||
margins: [4, 4, 4, 4]
|
||||
}
|
||||
shrink_to_fit: true
|
||||
title: "Paths"
|
||||
|
||||
@GUI::ComboBox {
|
||||
name: "path_combo_box"
|
||||
model_only: true
|
||||
fixed_width: 230
|
||||
}
|
||||
|
||||
@GUI::TextBox {
|
||||
name: "path_input"
|
||||
mode: "Editable"
|
||||
}
|
||||
|
||||
@GUI::Button {
|
||||
name: "path_picker_button"
|
||||
fixed_width: 20
|
||||
text: "..."
|
||||
tooltip: "Choose..."
|
||||
}
|
||||
@GUI::TabWidget {
|
||||
name: "property_tabs"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
return main_widget->request_close();
|
||||
};
|
||||
|
||||
window->resize(480, 520);
|
||||
window->resize(820, 520);
|
||||
window->set_resizable(false);
|
||||
window->show();
|
||||
window->set_icon(app_icon.bitmap_for_size(16));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue