From 1f4b15dcaa31f4fb6bafd2c30e94c28dfc8000ad Mon Sep 17 00:00:00 2001 From: Dan Klishch Date: Sun, 5 Nov 2023 19:29:02 -0500 Subject: [PATCH] LibGUI: Port rest of the classes to property deserializers --- Userland/Libraries/LibGUI/BoxLayout.cpp | 2 +- Userland/Libraries/LibGUI/Layout.cpp | 6 +- Userland/Libraries/LibGUI/Margins.h | 39 ++--- .../Libraries/LibGUI/PropertyDeserializer.cpp | 72 ++++++++ Userland/Libraries/LibGUI/TabWidget.cpp | 13 +- Userland/Libraries/LibGUI/UIDimensions.h | 66 +++---- Userland/Libraries/LibGUI/Widget.cpp | 162 +++++------------- Userland/Libraries/LibGUI/Widget.h | 1 - Userland/Libraries/LibGUI/Window.cpp | 14 +- 9 files changed, 164 insertions(+), 211 deletions(-) diff --git a/Userland/Libraries/LibGUI/BoxLayout.cpp b/Userland/Libraries/LibGUI/BoxLayout.cpp index 963a9b8a7e..d857684b32 100644 --- a/Userland/Libraries/LibGUI/BoxLayout.cpp +++ b/Userland/Libraries/LibGUI/BoxLayout.cpp @@ -22,7 +22,7 @@ BoxLayout::BoxLayout(Orientation orientation, Margins margins, int spacing) , m_orientation(orientation) { register_property( - "orientation", [this] { return m_orientation == Gfx::Orientation::Vertical ? "Vertical" : "Horizontal"; }, nullptr); + "orientation"sv, [this] { return m_orientation == Gfx::Orientation::Vertical ? "Vertical" : "Horizontal"; }, nullptr, nullptr); } UISize BoxLayout::preferred_size() const diff --git a/Userland/Libraries/LibGUI/Layout.cpp b/Userland/Libraries/LibGUI/Layout.cpp index bed40e62e9..d81697e275 100644 --- a/Userland/Libraries/LibGUI/Layout.cpp +++ b/Userland/Libraries/LibGUI/Layout.cpp @@ -21,7 +21,8 @@ Layout::Layout(Margins initial_margins, int spacing) REGISTER_INT_PROPERTY("spacing", spacing, set_spacing); REGISTER_MARGINS_PROPERTY("margins", margins, set_margins); - register_property("entries", + register_property( + "entries"sv, [this] { JsonArray entries_array; for (auto& entry : m_entries) { @@ -37,7 +38,8 @@ Layout::Layout(Margins initial_margins, int spacing) entries_array.must_append(move(entry_object)); } return entries_array; - }); + }, + nullptr, nullptr); } Layout::~Layout() = default; diff --git a/Userland/Libraries/LibGUI/Margins.h b/Userland/Libraries/LibGUI/Margins.h index 3083d558fe..3b8d60afab 100644 --- a/Userland/Libraries/LibGUI/Margins.h +++ b/Userland/Libraries/LibGUI/Margins.h @@ -139,30 +139,15 @@ private: #define REGISTER_MARGINS_PROPERTY(property_name, getter, setter) \ register_property( \ - property_name, [this]() { \ - auto m = getter(); \ - JsonObject margins_object; \ - margins_object.set("left", m.left()); \ - margins_object.set("right", m.right()); \ - margins_object.set("top", m.top()); \ - margins_object.set("bottom", m.bottom()); \ - return margins_object; }, \ - [this](auto& value) { \ - if (!value.is_array()) \ - return false; \ - auto size = value.as_array().size(); \ - if (size == 0 || size > 4) \ - return false; \ - int m[4]; \ - for (size_t i = 0; i < size; ++i) \ - m[i] = value.as_array().at(i).to_i32(); \ - if (size == 1) \ - setter({ m[0] }); \ - else if (size == 2) \ - setter({ m[0], m[1] }); \ - else if (size == 3) \ - setter({ m[0], m[1], m[2] }); \ - else \ - setter({ m[0], m[1], m[2], m[3] }); \ - return true; \ - }); + property_name##sv, \ + [this]() { \ + auto m = getter(); \ + JsonObject margins_object; \ + margins_object.set("left", m.left()); \ + margins_object.set("right", m.right()); \ + margins_object.set("top", m.top()); \ + margins_object.set("bottom", m.bottom()); \ + return margins_object; \ + }, \ + ::GUI::PropertyDeserializer<::GUI::Margins> {}, \ + [this](auto const& value) { return setter(value); }); diff --git a/Userland/Libraries/LibGUI/PropertyDeserializer.cpp b/Userland/Libraries/LibGUI/PropertyDeserializer.cpp index 381184e6c4..514fb6a97a 100644 --- a/Userland/Libraries/LibGUI/PropertyDeserializer.cpp +++ b/Userland/Libraries/LibGUI/PropertyDeserializer.cpp @@ -7,6 +7,9 @@ #include "PropertyDeserializer.h" #include #include +#include +#include +#include #include namespace GUI { @@ -114,4 +117,73 @@ ErrorOr PropertyDeserializer::operator()(JsonValue c return size; } +template<> +ErrorOr PropertyDeserializer::operator()(JsonValue const& value) const +{ + if (!value.is_array() || value.as_array().size() < 1 || value.as_array().size() > 4) + return Error::from_string_literal("Expected non-empty array with up to 4 integers"); + + auto const& array = value.as_array(); + auto size = array.size(); + + int m[4]; + + for (size_t i = 0; i < size; ++i) { + auto const& margin = array[i]; + if (!margin.is_integer()) + return Error::from_string_literal("Margin value should be an integer"); + m[i] = margin.to_i32(); + } + + if (size == 1) + return GUI::Margins { m[0] }; + else if (size == 2) + return GUI::Margins { m[0], m[1] }; + else if (size == 3) + return GUI::Margins { m[0], m[1], m[2] }; + else + return GUI::Margins { m[0], m[1], m[2], m[3] }; +} + +template<> +ErrorOr PropertyDeserializer::operator()(JsonValue const& value) const +{ + auto result = UIDimension::construct_from_json_value(value); + if (result.has_value()) + return result.release_value(); + return Error::from_string_literal("Value is not a valid UIDimension"); +} + +template<> +ErrorOr PropertyDeserializer::operator()(JsonValue const& value) const +{ + if (!value.is_object() || !value.as_object().has("width"sv) || !value.as_object().has("height"sv)) + return Error::from_string_literal("Object with keys \"width\" and \"height\" is expected"); + + auto const& object = value.as_object(); + + auto const& width = object.get("width"sv).value(); + auto result_width = GUI::UIDimension::construct_from_json_value(width); + if (!result_width.has_value()) + return Error::from_string_literal("width is not a valid UIDimension"); + + auto const& height = object.get("height"sv).value(); + auto result_height = GUI::UIDimension::construct_from_json_value(height); + if (!result_height.has_value()) + return Error::from_string_literal("height is not a valid UIDimension"); + + return UISize { result_width.value(), result_height.value() }; +} + +template<> +ErrorOr PropertyDeserializer::operator()(JsonValue const& value) const +{ + if (value.is_string()) { + auto c = Color::from_string(value.as_string()); + if (c.has_value()) + return c.release_value(); + } + return Error::from_string_literal("Color is expected"); +} + }; diff --git a/Userland/Libraries/LibGUI/TabWidget.cpp b/Userland/Libraries/LibGUI/TabWidget.cpp index 7d6d37a5c7..6d98279cc2 100644 --- a/Userland/Libraries/LibGUI/TabWidget.cpp +++ b/Userland/Libraries/LibGUI/TabWidget.cpp @@ -37,18 +37,7 @@ TabWidget::TabWidget() { TabPosition::Bottom, "Bottom" }, { TabPosition::Left, "Left" }, { TabPosition::Right, "Right" }, ); - - register_property( - "text_alignment", - [this] { return Gfx::to_string(text_alignment()); }, - [this](auto& value) { - auto alignment = Gfx::text_alignment_from_string(value.to_byte_string()); - if (alignment.has_value()) { - set_text_alignment(alignment.value()); - return true; - } - return false; - }); + REGISTER_TEXT_ALIGNMENT_PROPERTY("text_alignment", text_alignment, set_text_alignment); } ErrorOr TabWidget::try_add_widget(Widget& widget) diff --git a/Userland/Libraries/LibGUI/UIDimensions.h b/Userland/Libraries/LibGUI/UIDimensions.h index 9a9d6fce42..a96bbd6352 100644 --- a/Userland/Libraries/LibGUI/UIDimensions.h +++ b/Userland/Libraries/LibGUI/UIDimensions.h @@ -296,58 +296,44 @@ inline auto clamp(GUI::UIDimension const& input, GUI::UIDimens } -#define REGISTER_UI_DIMENSION_PROPERTY(property_name, getter, setter) \ - register_property( \ - property_name, \ - [this] { \ - return this->getter().as_json_value(); \ - }, \ - [this](auto& value) { \ - auto result = GUI::UIDimension::construct_from_json_value(value); \ - if (result.has_value()) \ - this->setter(result.value()); \ - return result.has_value(); \ - }); +#define REGISTER_UI_DIMENSION_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name##sv, \ + [this] { \ + return this->getter().as_json_value(); \ + }, \ + ::GUI::PropertyDeserializer<::GUI::UIDimension> {}, \ + [this](auto const& value) { return setter(value); }); #define REGISTER_READONLY_UI_DIMENSION_PROPERTY(property_name, getter) \ register_property( \ - property_name, \ + property_name##sv, \ [this] { \ return this->getter().as_json_value(); \ - }); + }, \ + nullptr, nullptr); -#define REGISTER_UI_SIZE_PROPERTY(property_name, getter, setter) \ - register_property( \ - property_name, \ - [this] { \ - auto size = this->getter(); \ - JsonObject size_object; \ - size_object.set("width"sv, size.width().as_json_value()); \ - size_object.set("height"sv, size.height().as_json_value()); \ - return size_object; \ - }, \ - [this](auto& value) { \ - if (!value.is_object()) \ - return false; \ - auto result_width = GUI::UIDimension::construct_from_json_value( \ - value.as_object().get("width"sv).value_or({})); \ - auto result_height = GUI::UIDimension::construct_from_json_value( \ - value.as_object().get("height"sv).value_or({})); \ - if (result_width.has_value() && result_height.has_value()) { \ - GUI::UISize size(result_width.value(), result_height.value()); \ - setter(size); \ - return true; \ - } \ - return false; \ - }); +#define REGISTER_UI_SIZE_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name##sv, \ + [this] { \ + auto size = this->getter(); \ + JsonObject size_object; \ + size_object.set("width"sv, size.width().as_json_value()); \ + size_object.set("height"sv, size.height().as_json_value()); \ + return size_object; \ + }, \ + ::GUI::PropertyDeserializer<::GUI::UISize> {}, \ + [this](auto const& value) { return setter(value); }); #define REGISTER_READONLY_UI_SIZE_PROPERTY(property_name, getter) \ register_property( \ - property_name, \ + property_name##sv, \ [this] { \ auto size = this->getter(); \ JsonObject size_object; \ size_object.set("width", size.width().as_json_value()); \ size_object.set("height", size.height().as_json_value()); \ return size_object; \ - }); + }, \ + nullptr, nullptr); diff --git a/Userland/Libraries/LibGUI/Widget.cpp b/Userland/Libraries/LibGUI/Widget.cpp index 32a72a3d79..2a079cd3e1 100644 --- a/Userland/Libraries/LibGUI/Widget.cpp +++ b/Userland/Libraries/LibGUI/Widget.cpp @@ -45,12 +45,12 @@ Widget::Widget() REGISTER_DEPRECATED_STRING_PROPERTY("name", name, set_name); register_property( - "address", [this] { return FlatPtr(this); }, - [](auto&) { return false; }); + "address"sv, [this] { return FlatPtr(this); }, + nullptr, nullptr); register_property( - "parent", [this] { return FlatPtr(this->parent()); }, - [](auto&) { return false; }); + "parent"sv, [this] { return FlatPtr(this->parent()); }, + nullptr, nullptr); REGISTER_RECT_PROPERTY("relative_rect", relative_rect, set_relative_rect); REGISTER_BOOL_PROPERTY("fill_with_background_color", fill_with_background_color, set_fill_with_background_color); @@ -90,119 +90,52 @@ Widget::Widget() REGISTER_BOOL_PROPERTY("font_fixed_width", is_font_fixed_width, set_font_fixed_width) register_property( - "font_type", [this] { return m_font->is_fixed_width() ? "FixedWidth" : "Normal"; }, - [this](auto& value) { - if (value.to_byte_string() == "FixedWidth") { - set_font_fixed_width(true); - return true; + "font_type"sv, [this] { return m_font->is_fixed_width() ? "FixedWidth" : "Normal"; }, + [](JsonValue const& value) -> ErrorOr { + if (value.is_string()) { + auto string = value.as_string(); + if (string == "FixedWidth") + return true; + if (string == "Normal") + return false; } - if (value.to_byte_string() == "Normal") { - set_font_fixed_width(false); - return true; - } - return false; + return Error::from_string_literal("\"FixedWidth\" or \"Normal\" is expected"); + }, + [this](auto const& value) { return set_font_fixed_width(value); }); + + REGISTER_ENUM_PROPERTY("focus_policy", focus_policy, set_focus_policy, GUI::FocusPolicy, + { GUI::FocusPolicy::ClickFocus, "ClickFocus" }, + { GUI::FocusPolicy::NoFocus, "NoFocus" }, + { GUI::FocusPolicy::TabFocus, "TabFocus" }, + { GUI::FocusPolicy::StrongFocus, "StrongFocus" }); + + register_property( + "foreground_color"sv, + [this]() { return palette().color(foreground_role()).to_byte_string(); }, + ::GUI::PropertyDeserializer {}, + [this](Gfx::Color const& color) { + auto _palette = palette(); + _palette.set_color(foreground_role(), color); + set_palette(_palette); }); register_property( - "focus_policy", [this]() -> JsonValue { - auto policy = focus_policy(); - if (policy == GUI::FocusPolicy::ClickFocus) - return "ClickFocus"; - if (policy == GUI::FocusPolicy::NoFocus) - return "NoFocus"; - if (policy == GUI::FocusPolicy::TabFocus) - return "TabFocus"; - if (policy == GUI::FocusPolicy::StrongFocus) - return "StrongFocus"; - return JsonValue(); }, - [this](auto& value) { - if (!value.is_string()) - return false; - if (value.as_string() == "ClickFocus") { - set_focus_policy(GUI::FocusPolicy::ClickFocus); - return true; - } - if (value.as_string() == "NoFocus") { - set_focus_policy(GUI::FocusPolicy::NoFocus); - return true; - } - if (value.as_string() == "TabFocus") { - set_focus_policy(GUI::FocusPolicy::TabFocus); - return true; - } - if (value.as_string() == "StrongFocus") { - set_focus_policy(GUI::FocusPolicy::StrongFocus); - return true; - } - return false; + "background_color"sv, + [this]() { return palette().color(background_role()).to_byte_string(); }, + ::GUI::PropertyDeserializer {}, + [this](Gfx::Color const& color) { + set_background_color(color); }); - register_property( - "foreground_color", [this]() -> JsonValue { return palette().color(foreground_role()).to_byte_string(); }, - [this](auto& value) { - auto c = Color::from_string(value.to_byte_string()); - if (c.has_value()) { - auto _palette = palette(); - _palette.set_color(foreground_role(), c.value()); - set_palette(_palette); - return true; - } - return false; - }); - - register_property( - "background_color", [this]() -> JsonValue { return palette().color(background_role()).to_byte_string(); }, - [this](JsonValue const& value) { - auto color_str = String::from_byte_string(value.to_byte_string()); - if (color_str.is_error()) - return false; - - return set_background_color(color_str.release_value()); - }); - - register_property( - "foreground_role", [this]() -> JsonValue { return Gfx::to_string(foreground_role()); }, - [this](auto& value) { - if (!value.is_string()) - return false; - auto str = value.as_string(); - if (str == "NoRole") { - set_foreground_role(Gfx::ColorRole::NoRole); - return true; - } +#define __ENUMERATE_COLOR_ROLE(role) \ + { Gfx::ColorRole::role, #role }, + REGISTER_ENUM_PROPERTY("foreground_role", foreground_role, set_foreground_role, Gfx::ColorRole, + { Gfx::ColorRole::NoRole, "NoRole" }, + ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE)); + REGISTER_ENUM_PROPERTY("background_role", background_role, set_background_role, Gfx::ColorRole, + { Gfx::ColorRole::NoRole, "NoRole" }, + ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE)); #undef __ENUMERATE_COLOR_ROLE -#define __ENUMERATE_COLOR_ROLE(role) \ - else if (str == #role) \ - { \ - set_foreground_role(Gfx::ColorRole::role); \ - return true; \ - } - ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE) -#undef __ENUMERATE_COLOR_ROLE - return false; - }); - - register_property( - "background_role", [this]() -> JsonValue { return Gfx::to_string(background_role()); }, - [this](auto& value) { - if (!value.is_string()) - return false; - auto str = value.as_string(); - if (str == "NoRole") { - set_background_role(Gfx::ColorRole::NoRole); - return true; - } -#undef __ENUMERATE_COLOR_ROLE -#define __ENUMERATE_COLOR_ROLE(role) \ - else if (str == #role) \ - { \ - set_background_role(Gfx::ColorRole::role); \ - return true; \ - } - ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE) -#undef __ENUMERATE_COLOR_ROLE - return false; - }); } Widget::~Widget() = default; @@ -1082,15 +1015,6 @@ void Widget::set_foreground_role(ColorRole role) update(); } -bool Widget::set_background_color(String color_str) -{ - auto color = Color::from_string(color_str.to_byte_string()); - if (!color.has_value()) - return false; - set_background_color(color.release_value()); - return true; -} - void Widget::set_background_color(Gfx::Color color) { auto _palette = palette(); diff --git a/Userland/Libraries/LibGUI/Widget.h b/Userland/Libraries/LibGUI/Widget.h index f3f492ec45..642128c9b4 100644 --- a/Userland/Libraries/LibGUI/Widget.h +++ b/Userland/Libraries/LibGUI/Widget.h @@ -254,7 +254,6 @@ public: Gfx::ColorRole foreground_role() const { return m_foreground_role; } void set_foreground_role(Gfx::ColorRole); - bool set_background_color(String); void set_background_color(Gfx::Color); void set_autofill(bool b) { set_fill_with_background_color(b); } diff --git a/Userland/Libraries/LibGUI/Window.cpp b/Userland/Libraries/LibGUI/Window.cpp index 020c84789a..035ff9a6cf 100644 --- a/Userland/Libraries/LibGUI/Window.cpp +++ b/Userland/Libraries/LibGUI/Window.cpp @@ -85,16 +85,12 @@ Window::Window(Core::EventReceiver* parent) m_floating_rect = { -5000, -5000, 0, 0 }; m_title_when_windowless = "GUI::Window"; - register_property( - "title", - [this] { return title(); }, - [this](auto& value) { - set_title(value.to_byte_string()); - return true; - }); + REGISTER_DEPRECATED_STRING_PROPERTY("title", title, set_title) - register_property("visible", [this] { return is_visible(); }); - register_property("active", [this] { return is_active(); }); + register_property( + "visible"sv, [this] { return is_visible(); }, nullptr, nullptr); + register_property( + "active"sv, [this] { return is_active(); }, nullptr, nullptr); REGISTER_BOOL_PROPERTY("minimizable", is_minimizable, set_minimizable); REGISTER_BOOL_PROPERTY("resizable", is_resizable, set_resizable);