diff --git a/Libraries/LibCore/CMakeLists.txt b/Libraries/LibCore/CMakeLists.txt index bd64c35131..8b1dfd6059 100644 --- a/Libraries/LibCore/CMakeLists.txt +++ b/Libraries/LibCore/CMakeLists.txt @@ -19,6 +19,7 @@ set(SOURCES Notifier.cpp Object.cpp ProcessStatisticsReader.cpp + Property.cpp puff.cpp SocketAddress.cpp Socket.cpp diff --git a/Libraries/LibCore/Object.cpp b/Libraries/LibCore/Object.cpp index 2c4b87cece..44ab028de2 100644 --- a/Libraries/LibCore/Object.cpp +++ b/Libraries/LibCore/Object.cpp @@ -47,6 +47,17 @@ Object::Object(Object* parent, bool is_widget) all_objects().append(*this); if (m_parent) m_parent->add_child(*this); + + REGISTER_READONLY_STRING_PROPERTY("class_name", class_name); + REGISTER_STRING_PROPERTY("name", name, set_name); + + register_property( + "address", [this] { return FlatPtr(this); }, + [](auto&) { return false; }); + + register_property( + "parent", [this] { return FlatPtr(this->parent()); }, + [](auto&) { return false; }); } Object::~Object() @@ -173,19 +184,26 @@ void Object::deferred_invoke(Function invokee) void Object::save_to(JsonObject& json) { - json.set("class_name", class_name()); - json.set("address", (FlatPtr)this); - json.set("name", name()); - json.set("parent", (FlatPtr)parent()); + for (auto& it : m_properties) { + auto& property = it.value; + json.set(property->name(), property->get()); + } +} + +JsonValue Object::property(const StringView& name) +{ + auto it = m_properties.find(name); + if (it == m_properties.end()) + return JsonValue(); + return it->value->get(); } bool Object::set_property(const StringView& name, const JsonValue& value) { - if (name == "name") { - set_name(value.to_string()); - return true; - } - return false; + auto it = m_properties.find(name); + if (it == m_properties.end()) + return false; + return it->value->set(value); } bool Object::is_ancestor_of(const Object& other) const @@ -235,6 +253,11 @@ void Object::decrement_inspector_count(Badge) did_end_inspection(); } +void Object::register_property(const String& name, Function getter, Function setter) +{ + m_properties.set(name, make(name, move(getter), move(setter))); +} + const LogStream& operator<<(const LogStream& stream, const Object& object) { return stream << object.class_name() << '{' << &object << '}'; diff --git a/Libraries/LibCore/Object.h b/Libraries/LibCore/Object.h index e54e0d9920..24ef7a19cb 100644 --- a/Libraries/LibCore/Object.h +++ b/Libraries/LibCore/Object.h @@ -27,6 +27,7 @@ #pragma once #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include namespace Core { @@ -112,8 +114,10 @@ public: virtual bool is_action() const { return false; } virtual bool is_window() const { return false; } - virtual void save_to(AK::JsonObject&); - virtual bool set_property(const StringView& name, const JsonValue& value); + void save_to(AK::JsonObject&); + + bool set_property(const StringView& name, const JsonValue& value); + JsonValue property(const StringView& name); static IntrusiveList& all_objects(); @@ -143,6 +147,8 @@ public: protected: explicit Object(Object* parent = nullptr, bool is_widget = false); + void register_property(const String& name, Function getter, Function setter = nullptr); + virtual void timer_event(TimerEvent&); virtual void custom_event(CustomEvent&); @@ -158,6 +164,7 @@ private: int m_timer_id { 0 }; unsigned m_inspector_count { 0 }; bool m_widget { false }; + HashMap> m_properties; NonnullRefPtrVector m_children; }; @@ -176,4 +183,103 @@ inline void Object::for_each_child_of_type(Callback callback) const LogStream& operator<<(const LogStream&, const Object&); +#define REGISTER_INT_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, \ + [this] { return this->getter(); }, \ + [this](auto& value) { \ + this->setter(value.template to_number()); \ + return true; \ + }); + +#define REGISTER_BOOL_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, \ + [this] { return this->getter(); }, \ + [this](auto& value) { \ + this->setter(value.to_bool()); \ + return true; \ + }); + +#define REGISTER_STRING_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, \ + [this] { return this->getter(); }, \ + [this](auto& value) { \ + this->setter(value.to_string()); \ + return true; \ + }); + +#define REGISTER_READONLY_STRING_PROPERTY(property_name, getter) \ + register_property( \ + property_name, \ + [this] { return this->getter(); }, \ + nullptr); + +#define REGISTER_RECT_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, \ + [this] { \ + auto rect = this->getter(); \ + JsonObject rect_object; \ + rect_object.set("x", rect.x()); \ + rect_object.set("y", rect.y()); \ + rect_object.set("width", rect.width()); \ + rect_object.set("height", rect.height()); \ + return rect_object; \ + }, \ + [this](auto& value) { \ + if (!value.is_object()) \ + return false; \ + Gfx::IntRect rect; \ + rect.set_x(value.as_object().get("x").to_i32()); \ + rect.set_y(value.as_object().get("y").to_i32()); \ + rect.set_width(value.as_object().get("width").to_i32()); \ + rect.set_height(value.as_object().get("height").to_i32()); \ + setter(rect); \ + return true; \ + }); + +#define REGISTER_SIZE_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, \ + [this] { \ + auto size = this->getter(); \ + JsonObject size_object; \ + size_object.set("width", size.width()); \ + size_object.set("height", size.height()); \ + return size_object; \ + }, \ + [this](auto& value) { \ + if (!value.is_object()) \ + return false; \ + Gfx::IntSize size; \ + size.set_width(value.as_object().get("width").to_i32()); \ + size.set_height(value.as_object().get("height").to_i32()); \ + setter(size); \ + return true; \ + }); + +#define REGISTER_SIZE_POLICY_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, [this]() -> JsonValue { \ + auto policy = this->getter(); \ + if (policy == GUI::SizePolicy::Fill) \ + return "Fill"; \ + if (policy == GUI::SizePolicy::Fixed) \ + return "Fixed"; \ + return JsonValue(); }, \ + [this](auto& value) { \ + if (!value.is_string()) \ + return false; \ + if (value.as_string() == "Fill") { \ + setter(GUI::SizePolicy::Fill); \ + return true; \ + } \ + if (value.as_string() == "Fixed") { \ + setter(GUI::SizePolicy::Fixed); \ + return true; \ + } \ + return false; \ + }); } diff --git a/Libraries/LibCore/Property.cpp b/Libraries/LibCore/Property.cpp new file mode 100644 index 0000000000..8c6c2587c6 --- /dev/null +++ b/Libraries/LibCore/Property.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +namespace Core { + +Property::Property(String name, Function getter, Function setter) + : m_name(move(name)) + , m_getter(move(getter)) + , m_setter(move(setter)) +{ +} + +Property::~Property() +{ +} + +} diff --git a/Libraries/LibCore/Property.h b/Libraries/LibCore/Property.h new file mode 100644 index 0000000000..baf9e0812c --- /dev/null +++ b/Libraries/LibCore/Property.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +namespace Core { + +class Property { + AK_MAKE_NONCOPYABLE(Property); + +public: + Property(String name, Function getter, Function setter = nullptr); + ~Property(); + + bool set(const JsonValue& value) + { + if (!m_setter) + return false; + return m_setter(value); + } + + JsonValue get() const { return m_getter(); } + + const String& name() const { return m_name; } + +private: + String m_name; + Function m_getter; + Function m_setter; +}; + +} diff --git a/Libraries/LibGUI/AbstractButton.cpp b/Libraries/LibGUI/AbstractButton.cpp index a4114bc1b8..ae14dddc42 100644 --- a/Libraries/LibGUI/AbstractButton.cpp +++ b/Libraries/LibGUI/AbstractButton.cpp @@ -39,6 +39,11 @@ AbstractButton::AbstractButton(const StringView& text) m_auto_repeat_timer->on_timeout = [this] { click(); }; + + REGISTER_STRING_PROPERTY("text", text, set_text); + REGISTER_BOOL_PROPERTY("checked", is_checked, set_checked); + REGISTER_BOOL_PROPERTY("checkable", is_checkable, set_checkable); + REGISTER_BOOL_PROPERTY("exclusive", is_exclusive, set_exclusive); } AbstractButton::~AbstractButton() @@ -185,35 +190,4 @@ void AbstractButton::change_event(Event& event) Widget::change_event(event); } -void AbstractButton::save_to(JsonObject& json) -{ - json.set("text", m_text); - json.set("checked", m_checked); - json.set("checkable", m_checkable); - json.set("exclusive", m_exclusive); - Widget::save_to(json); -} - -bool AbstractButton::set_property(const StringView& name, const JsonValue& value) -{ - if (name == "text") { - set_text(value.to_string()); - return true; - } - if (name == "checked") { - set_checked(value.to_bool()); - return true; - } - if (name == "checkable") { - set_checkable(value.to_bool()); - return true; - } - if (name == "exclusive") { - set_exclusive(value.to_bool()); - return true; - } - - return Widget::set_property(name, value); -} - } diff --git a/Libraries/LibGUI/AbstractButton.h b/Libraries/LibGUI/AbstractButton.h index 7e62e5a9f1..c5f7562a18 100644 --- a/Libraries/LibGUI/AbstractButton.h +++ b/Libraries/LibGUI/AbstractButton.h @@ -70,9 +70,6 @@ protected: virtual void leave_event(Core::Event&) override; virtual void change_event(Event&) override; - virtual void save_to(JsonObject&) override; - virtual bool set_property(const StringView& name, const JsonValue& value) override; - void paint_text(Painter&, const Gfx::IntRect&, const Gfx::Font&, Gfx::TextAlignment); private: diff --git a/Libraries/LibGUI/BoxLayout.cpp b/Libraries/LibGUI/BoxLayout.cpp index ce49657a1f..f4b93b6f85 100644 --- a/Libraries/LibGUI/BoxLayout.cpp +++ b/Libraries/LibGUI/BoxLayout.cpp @@ -37,6 +37,8 @@ namespace GUI { BoxLayout::BoxLayout(Orientation orientation) : m_orientation(orientation) { + register_property( + "orientation", [this] { return m_orientation == Gfx::Orientation::Vertical ? "Vertical" : "Horizontal"; }, nullptr); } void BoxLayout::run(Widget& widget) @@ -174,11 +176,4 @@ void BoxLayout::run(Widget& widget) current_y += rect.height() + spacing(); } } - -void BoxLayout::save_to(JsonObject& json) -{ - Layout::save_to(json); - json.set("orientation", m_orientation == Gfx::Orientation::Vertical ? "Vertical" : "Horizontal"); -} - } diff --git a/Libraries/LibGUI/BoxLayout.h b/Libraries/LibGUI/BoxLayout.h index 57694e95d0..7ddbaffdd3 100644 --- a/Libraries/LibGUI/BoxLayout.h +++ b/Libraries/LibGUI/BoxLayout.h @@ -44,8 +44,6 @@ public: protected: explicit BoxLayout(Gfx::Orientation); - virtual void save_to(JsonObject &) override; - private: Gfx::Orientation m_orientation; }; diff --git a/Libraries/LibGUI/Layout.cpp b/Libraries/LibGUI/Layout.cpp index 71c37e9369..8616a99252 100644 --- a/Libraries/LibGUI/Layout.cpp +++ b/Libraries/LibGUI/Layout.cpp @@ -33,6 +33,45 @@ namespace GUI { Layout::Layout() { + REGISTER_INT_PROPERTY("spacing", spacing, set_spacing); + + register_property( + "margins", + [this] { + JsonObject margins_object; + margins_object.set("left", m_margins.left()); + margins_object.set("right", m_margins.right()); + margins_object.set("top", m_margins.top()); + margins_object.set("bottom", m_margins.bottom()); + return margins_object; + }, + [this](auto value) { + if (!value.is_array() || value.as_array().size() != 4) + return false; + int m[4]; + for (size_t i = 0; i < 4; ++i) + m[i] = value.as_array().at(i).to_i32(); + set_margins({ m[0], m[1], m[2], m[3] }); + return true; + }); + + register_property("entries", + [this] { + JsonArray entries_array; + for (auto& entry : m_entries) { + JsonObject entry_object; + if (entry.type == Entry::Type::Widget) { + entry_object.set("type", "Widget"); + entry_object.set("widget", (FlatPtr)entry.widget.ptr()); + } else if (entry.type == Entry::Type::Spacer) { + entry_object.set("type", "Spacer"); + } else { + ASSERT_NOT_REACHED(); + } + entries_array.append(move(entry_object)); + } + return entries_array; + }); } Layout::~Layout() @@ -121,32 +160,4 @@ void Layout::set_margins(const Margins& margins) m_owner->notify_layout_changed({}); } -void Layout::save_to(JsonObject& json) -{ - Core::Object::save_to(json); - json.set("spacing", m_spacing); - - JsonObject margins_object; - margins_object.set("left", m_margins.left()); - margins_object.set("right", m_margins.right()); - margins_object.set("top", m_margins.top()); - margins_object.set("bottom", m_margins.bottom()); - json.set("margins", move(margins_object)); - - JsonArray entries_array; - for (auto& entry : m_entries) { - JsonObject entry_object; - if (entry.type == Entry::Type::Widget) { - entry_object.set("type", "Widget"); - entry_object.set("widget", (FlatPtr)entry.widget.ptr()); - } else if (entry.type == Entry::Type::Spacer) { - entry_object.set("type", "Spacer"); - } else { - ASSERT_NOT_REACHED(); - } - entries_array.append(move(entry_object)); - } - json.set("entries", move(entries_array)); -} - } diff --git a/Libraries/LibGUI/Layout.h b/Libraries/LibGUI/Layout.h index 1d4bfc0d9d..3e586e3fd2 100644 --- a/Libraries/LibGUI/Layout.h +++ b/Libraries/LibGUI/Layout.h @@ -59,8 +59,6 @@ public: int spacing() const { return m_spacing; } void set_spacing(int); - virtual void save_to(JsonObject&) override; - protected: Layout(); diff --git a/Libraries/LibGUI/TabWidget.cpp b/Libraries/LibGUI/TabWidget.cpp index c17c86ae36..7386505678 100644 --- a/Libraries/LibGUI/TabWidget.cpp +++ b/Libraries/LibGUI/TabWidget.cpp @@ -40,6 +40,20 @@ namespace GUI { TabWidget::TabWidget() { + REGISTER_INT_PROPERTY("container_padding", container_padding, set_container_padding); + REGISTER_BOOL_PROPERTY("uniform_tabs", uniform_tabs, set_uniform_tabs); + + register_property( + "text_alignment", + [this] { return Gfx::to_string(text_alignment()); }, + [this](auto& value) { + auto alignment = Gfx::text_alignment_from_string(value.to_string()); + if (alignment.has_value()) { + set_text_alignment(alignment.value()); + return true; + } + return false; + }); } TabWidget::~TabWidget() @@ -396,26 +410,4 @@ void TabWidget::context_menu_event(ContextMenuEvent& context_menu_event) } } -bool TabWidget::set_property(const StringView& name, const JsonValue& value) -{ - if (name == "container_padding") { - set_container_padding(value.to_i32()); - return true; - } - - if (name == "uniform_tabs") { - set_uniform_tabs(value.to_bool()); - return true; - } - - if (name == "text_alignment") { - auto alignment = Gfx::text_alignment_from_string(value.to_string()); - if (alignment.has_value()) - set_text_alignment(alignment.value()); - return true; - } - - return Widget::set_property(name, value); -} - } diff --git a/Libraries/LibGUI/TabWidget.h b/Libraries/LibGUI/TabWidget.h index 9c85f599cb..9fa487f482 100644 --- a/Libraries/LibGUI/TabWidget.h +++ b/Libraries/LibGUI/TabWidget.h @@ -76,6 +76,7 @@ public: void set_text_alignment(Gfx::TextAlignment alignment) { m_text_alignment = alignment; } Gfx::TextAlignment text_alignment() const { return m_text_alignment; } + bool uniform_tabs() const { return m_uniform_tabs; } void set_uniform_tabs(bool uniform_tabs) { m_uniform_tabs = uniform_tabs; } int uniform_tab_width() const; @@ -86,8 +87,6 @@ public: Function on_middle_click; Function on_context_menu_request; - virtual bool set_property(const StringView& name, const JsonValue& value) override; - protected: TabWidget(); diff --git a/Libraries/LibGUI/Widget.cpp b/Libraries/LibGUI/Widget.cpp index 89b32a81fb..ffd893f9d1 100644 --- a/Libraries/LibGUI/Widget.cpp +++ b/Libraries/LibGUI/Widget.cpp @@ -110,6 +110,17 @@ Widget::Widget() , m_font(Gfx::Font::default_font()) , m_palette(Application::the()->palette().impl()) { + 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); + REGISTER_BOOL_PROPERTY("visible", is_visible, set_visible); + REGISTER_BOOL_PROPERTY("focused", is_focused, set_focus); + REGISTER_BOOL_PROPERTY("enabled", is_enabled, set_enabled); + REGISTER_STRING_PROPERTY("tooltip", tooltip, set_tooltip); + REGISTER_SIZE_PROPERTY("preferred_size", preferred_size, set_preferred_size); + REGISTER_INT_PROPERTY("preferred_width", preferred_width, set_preferred_width); + REGISTER_INT_PROPERTY("preferred_height", preferred_height, set_preferred_height); + REGISTER_SIZE_POLICY_PROPERTY("horizontal_size_policy", horizontal_size_policy, set_horizontal_size_policy); + REGISTER_SIZE_POLICY_PROPERTY("vertical_size_policy", vertical_size_policy, set_vertical_size_policy); } Widget::~Widget() @@ -774,79 +785,6 @@ void Widget::set_forecolor(const StringView& color_string) set_foreground_color(color.value()); } -void Widget::save_to(AK::JsonObject& json) -{ - json.set("relative_rect", relative_rect().to_string()); - json.set("fill_with_background_color", fill_with_background_color()); - json.set("tooltip", tooltip()); - json.set("visible", is_visible()); - json.set("focused", is_focused()); - json.set("enabled", is_enabled()); - json.set("background_color", background_color().to_string()); - json.set("foreground_color", foreground_color().to_string()); - json.set("preferred_size", preferred_size().to_string()); - json.set("size_policy", String::format("[%s,%s]", to_string(horizontal_size_policy()), to_string(vertical_size_policy()))); - Core::Object::save_to(json); -} - -bool Widget::set_property(const StringView& name, const JsonValue& value) -{ - if (name == "fill_with_background_color") { - set_fill_with_background_color(value.to_bool()); - return true; - } - if (name == "tooltip") { - set_tooltip(value.to_string()); - return true; - } - if (name == "enable") { - set_enabled(value.to_bool()); - return true; - } - if (name == "focused") { - set_focus(value.to_bool()); - return true; - } - if (name == "visible") { - set_visible(value.to_bool()); - return true; - } - - if (name == "horizontal_size_policy") { - auto string = value.to_string(); - if (string == "Fill") - set_size_policy(Gfx::Orientation::Horizontal, SizePolicy::Fill); - else if (string == "Fixed") - set_size_policy(Gfx::Orientation::Horizontal, SizePolicy::Fixed); - else - ASSERT_NOT_REACHED(); - return true; - } - - if (name == "vertical_size_policy") { - auto string = value.to_string(); - if (string == "Fill") - set_size_policy(Gfx::Orientation::Vertical, SizePolicy::Fill); - else if (string == "Fixed") - set_size_policy(Gfx::Orientation::Vertical, SizePolicy::Fixed); - else - ASSERT_NOT_REACHED(); - return true; - } - - if (name == "preferred_height") { - set_preferred_size(preferred_size().width(), value.to_i32()); - return true; - } - - if (name == "preferred_width") { - set_preferred_size(value.to_i32(), preferred_size().height()); - return true; - } - - return Core::Object::set_property(name, value); -} - Vector Widget::child_widgets() const { Vector widgets; @@ -975,22 +913,9 @@ bool Widget::load_from_json(const JsonObject& json) return false; } - auto spacing = layout.get("spacing"); - if (spacing.is_number()) - this->layout()->set_spacing(spacing.to_i32()); - - auto margins = layout.get("margins"); - if (margins.is_array()) { - if (margins.as_array().size() != 4) { - dbg() << "margins array needs 4 entries"; - return false; - } - int m[4]; - for (size_t i = 0; i < 4; ++i) - m[i] = margins.as_array().at(i).to_i32(); - dbg() << "setting margins " << m[0] << "," << m[1] << "," << m[2] << "," << m[3]; - this->layout()->set_margins({ m[0], m[1], m[2], m[3] }); - } + layout.for_each_member([&](auto& key, auto& value) { + this->layout()->set_property(key, value); + }); } auto children = json.get("children"); @@ -1045,5 +970,4 @@ Widget* Widget::find_descendant_by_name(const String& name) }); return found_widget; } - } diff --git a/Libraries/LibGUI/Widget.h b/Libraries/LibGUI/Widget.h index 901945c260..17bb3864d2 100644 --- a/Libraries/LibGUI/Widget.h +++ b/Libraries/LibGUI/Widget.h @@ -109,11 +109,18 @@ public: SizePolicy size_policy(Orientation orientation) { return orientation == Orientation::Horizontal ? m_horizontal_size_policy : m_vertical_size_policy; } void set_size_policy(SizePolicy horizontal_policy, SizePolicy vertical_policy); void set_size_policy(Orientation, SizePolicy); + void set_horizontal_size_policy(SizePolicy policy) { set_size_policy(policy, vertical_size_policy()); } + void set_vertical_size_policy(SizePolicy policy) { set_size_policy(horizontal_size_policy(), policy); } Gfx::IntSize preferred_size() const { return m_preferred_size; } void set_preferred_size(const Gfx::IntSize&); void set_preferred_size(int width, int height) { set_preferred_size({ width, height }); } + int preferred_width() const { return preferred_size().width(); } + int preferred_height() const { return preferred_size().height(); } + void set_preferred_width(int w) { set_preferred_size(w, preferred_height()); } + void set_preferred_height(int h) { set_preferred_size(preferred_width(), h); } + bool has_tooltip() const { return !m_tooltip.is_empty(); } String tooltip() const { return m_tooltip; } void set_tooltip(const StringView&); @@ -261,8 +268,6 @@ public: virtual bool is_radio_button() const { return false; } virtual bool is_abstract_button() const { return false; } - virtual void save_to(AK::JsonObject&) override; - void do_layout(); Gfx::Palette palette() const; @@ -317,8 +322,6 @@ protected: virtual void did_begin_inspection() override; virtual void did_end_inspection() override; - virtual bool set_property(const StringView& name, const JsonValue& value) override; - private: void handle_paint_event(PaintEvent&); void handle_resize_event(ResizeEvent&); diff --git a/Libraries/LibGUI/Window.cpp b/Libraries/LibGUI/Window.cpp index 4e15ca5eea..1618e891c3 100644 --- a/Libraries/LibGUI/Window.cpp +++ b/Libraries/LibGUI/Window.cpp @@ -65,6 +65,24 @@ Window::Window(Core::Object* parent) all_windows->set(this); m_rect_when_windowless = { -5000, -5000, 140, 140 }; m_title_when_windowless = "GUI::Window"; + + register_property( + "title", + [this] { return title(); }, + [this](auto& value) { + set_title(value.to_string()); + return true; + }); + + register_property("visible", [this] { return is_visible(); }); + register_property("active", [this] { return is_active(); }); + + REGISTER_BOOL_PROPERTY("minimizable", is_minimizable, set_minimizable); + REGISTER_BOOL_PROPERTY("resizable", is_resizable, set_resizable); + REGISTER_BOOL_PROPERTY("fullscreen", is_fullscreen, set_fullscreen); + REGISTER_RECT_PROPERTY("rect", rect, set_rect); + REGISTER_SIZE_PROPERTY("base_size", base_size, set_base_size); + REGISTER_SIZE_PROPERTY("size_increment", size_increment, set_size_increment); } Window::~Window() @@ -724,20 +742,6 @@ Vector Window::focusable_widgets() const return collected_widgets; } -void Window::save_to(AK::JsonObject& json) -{ - json.set("title", title()); - json.set("visible", is_visible()); - json.set("active", is_active()); - json.set("minimizable", is_minimizable()); - json.set("resizable", is_resizable()); - json.set("fullscreen", is_fullscreen()); - json.set("rect", rect().to_string()); - json.set("base_size", base_size().to_string()); - json.set("size_increment", size_increment().to_string()); - Core::Object::save_to(json); -} - void Window::set_fullscreen(bool fullscreen) { if (m_fullscreen == fullscreen) diff --git a/Libraries/LibGUI/Window.h b/Libraries/LibGUI/Window.h index 621693f57f..ce9ca93554 100644 --- a/Libraries/LibGUI/Window.h +++ b/Libraries/LibGUI/Window.h @@ -177,8 +177,6 @@ public: Vector focusable_widgets() const; - virtual void save_to(AK::JsonObject&) override; - void schedule_relayout(); static void for_each_window(Badge, Function); diff --git a/Libraries/LibGfx/TextAlignment.h b/Libraries/LibGfx/TextAlignment.h index a29846afdb..5c34b4cf26 100644 --- a/Libraries/LibGfx/TextAlignment.h +++ b/Libraries/LibGfx/TextAlignment.h @@ -31,13 +31,18 @@ namespace Gfx { +#define GFX_ENUMERATE_TEXT_ALIGNMENTS(M) \ + M(TopLeft) \ + M(CenterLeft) \ + M(Center) \ + M(CenterRight) \ + M(TopRight) \ + M(BottomRight) + enum class TextAlignment { - TopLeft, - CenterLeft, - Center, - CenterRight, - TopRight, - BottomRight, +#define __ENUMERATE(x) x, + GFX_ENUMERATE_TEXT_ALIGNMENTS(__ENUMERATE) +#undef __ENUMERATE }; inline bool is_right_text_alignment(TextAlignment alignment) @@ -54,18 +59,21 @@ inline bool is_right_text_alignment(TextAlignment alignment) inline Optional text_alignment_from_string(const StringView& string) { - if (string == "TopLeft") - return TextAlignment::TopLeft; - if (string == "CenterLeft") - return TextAlignment::CenterLeft; - if (string == "Center") - return TextAlignment::Center; - if (string == "CenterRight") - return TextAlignment::CenterRight; - if (string == "TopRight") - return TextAlignment::TopRight; - if (string == "BottomRight") - return TextAlignment::BottomRight; +#define __ENUMERATE(x) \ + if (string == #x) \ + return TextAlignment::x; + GFX_ENUMERATE_TEXT_ALIGNMENTS(__ENUMERATE) +#undef __ENUMERATE + return {}; +} + +inline const char* to_string(TextAlignment text_alignment) +{ +#define __ENUMERATE(x) \ + if (text_alignment == TextAlignment::x) \ + return #x; + GFX_ENUMERATE_TEXT_ALIGNMENTS(__ENUMERATE) +#undef __ENUMERATE return {}; } diff --git a/Libraries/LibLine/Editor.h b/Libraries/LibLine/Editor.h index fe32d5bfba..5f8109307f 100644 --- a/Libraries/LibLine/Editor.h +++ b/Libraries/LibLine/Editor.h @@ -274,8 +274,8 @@ private: Retry }; - // ^Core::Object - virtual void save_to(JsonObject&) override; + // FIXME: Port to Core::Property + void save_to(JsonObject&); struct KeyCallback { KeyCallback(Function cb) diff --git a/Libraries/LibWeb/Loader/ResourceLoader.cpp b/Libraries/LibWeb/Loader/ResourceLoader.cpp index 48eff645d7..1efaf3d52c 100644 --- a/Libraries/LibWeb/Loader/ResourceLoader.cpp +++ b/Libraries/LibWeb/Loader/ResourceLoader.cpp @@ -205,11 +205,4 @@ bool ResourceLoader::is_port_blocked(int port) return false; } -void ResourceLoader::save_to(JsonObject& object) -{ - Object::save_to(object); - object.set("pending_loads", m_pending_loads); - object.set("user_agent", m_user_agent); -} - } diff --git a/Libraries/LibWeb/Loader/ResourceLoader.h b/Libraries/LibWeb/Loader/ResourceLoader.h index bd3591e5d9..7f6c62d0f9 100644 --- a/Libraries/LibWeb/Loader/ResourceLoader.h +++ b/Libraries/LibWeb/Loader/ResourceLoader.h @@ -59,8 +59,6 @@ private: ResourceLoader(); static bool is_port_blocked(int port); - virtual void save_to(JsonObject&) override; - int m_pending_loads { 0 }; RefPtr m_protocol_client; diff --git a/Services/SystemServer/Service.h b/Services/SystemServer/Service.h index 1d293044fb..7b4bbed380 100644 --- a/Services/SystemServer/Service.h +++ b/Services/SystemServer/Service.h @@ -42,7 +42,8 @@ public: static Service* find_by_pid(pid_t); - void save_to(AK::JsonObject&) override; + // FIXME: Port to Core::Property + void save_to(AK::JsonObject&); private: Service(const Core::ConfigFile&, const StringView& name); diff --git a/Shell/Shell.h b/Shell/Shell.h index 02f974df6a..e4dbb195bc 100644 --- a/Shell/Shell.h +++ b/Shell/Shell.h @@ -194,8 +194,8 @@ private: Shell(); virtual ~Shell() override; - // ^Core::Object - virtual void save_to(JsonObject&) override; + // FIXME: Port to Core::Property + void save_to(JsonObject&); void bring_cursor_to_beginning_of_a_line() const; void cache_path();