mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 19:45:12 +00:00
LibCore: Make Core::Object properties more dynamic
Instead of everyone overriding save_to() and set_property() and doing a pretty asymmetric job of implementing the various properties, let's add a bit of structure here. Object properties are now represented by a Core::Property. Properties are registered with a getter and setter (optional) in constructors. I've added some convenience macros for creating and registering properties, but this does still feel a bit bulky. We'll have to iterate on this and see where it goes.
This commit is contained in:
parent
1e96e46a81
commit
e2f32b8f9d
Notes:
sideshowbarker
2024-07-19 02:23:40 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/e2f32b8f9d4
23 changed files with 373 additions and 250 deletions
|
@ -19,6 +19,7 @@ set(SOURCES
|
|||
Notifier.cpp
|
||||
Object.cpp
|
||||
ProcessStatisticsReader.cpp
|
||||
Property.cpp
|
||||
puff.cpp
|
||||
SocketAddress.cpp
|
||||
Socket.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<void(Object&)> 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<RPCClient>)
|
|||
did_end_inspection();
|
||||
}
|
||||
|
||||
void Object::register_property(const String& name, Function<JsonValue()> getter, Function<bool(const JsonValue&)> setter)
|
||||
{
|
||||
m_properties.set(name, make<Property>(name, move(getter), move(setter)));
|
||||
}
|
||||
|
||||
const LogStream& operator<<(const LogStream& stream, const Object& object)
|
||||
{
|
||||
return stream << object.class_name() << '{' << &object << '}';
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/IntrusiveList.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/NonnullRefPtrVector.h>
|
||||
|
@ -34,6 +35,7 @@
|
|||
#include <AK/TypeCasts.h>
|
||||
#include <AK/Weakable.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibCore/Property.h>
|
||||
|
||||
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<Object, &Object::m_all_objects_list_node>& all_objects();
|
||||
|
||||
|
@ -143,6 +147,8 @@ public:
|
|||
protected:
|
||||
explicit Object(Object* parent = nullptr, bool is_widget = false);
|
||||
|
||||
void register_property(const String& name, Function<JsonValue()> getter, Function<bool(const JsonValue&)> 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<String, NonnullOwnPtr<Property>> m_properties;
|
||||
NonnullRefPtrVector<Object> 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<int>()); \
|
||||
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; \
|
||||
});
|
||||
}
|
||||
|
|
42
Libraries/LibCore/Property.cpp
Normal file
42
Libraries/LibCore/Property.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <LibCore/Property.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
Property::Property(String name, Function<JsonValue()> getter, Function<bool(const JsonValue&)> setter)
|
||||
: m_name(move(name))
|
||||
, m_getter(move(getter))
|
||||
, m_setter(move(setter))
|
||||
{
|
||||
}
|
||||
|
||||
Property::~Property()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
58
Libraries/LibCore/Property.h
Normal file
58
Libraries/LibCore/Property.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* 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 <AK/Function.h>
|
||||
#include <AK/JsonValue.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Property {
|
||||
AK_MAKE_NONCOPYABLE(Property);
|
||||
|
||||
public:
|
||||
Property(String name, Function<JsonValue()> getter, Function<bool(const JsonValue&)> 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<JsonValue()> m_getter;
|
||||
Function<bool(const JsonValue&)> m_setter;
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,8 +44,6 @@ public:
|
|||
protected:
|
||||
explicit BoxLayout(Gfx::Orientation);
|
||||
|
||||
virtual void save_to(JsonObject &) override;
|
||||
|
||||
private:
|
||||
Gfx::Orientation m_orientation;
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,8 +59,6 @@ public:
|
|||
int spacing() const { return m_spacing; }
|
||||
void set_spacing(int);
|
||||
|
||||
virtual void save_to(JsonObject&) override;
|
||||
|
||||
protected:
|
||||
Layout();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<void(Widget&)> on_middle_click;
|
||||
Function<void(Widget&, const ContextMenuEvent&)> on_context_menu_request;
|
||||
|
||||
virtual bool set_property(const StringView& name, const JsonValue& value) override;
|
||||
|
||||
protected:
|
||||
TabWidget();
|
||||
|
||||
|
|
|
@ -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*> Widget::child_widgets() const
|
||||
{
|
||||
Vector<Widget*> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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<Widget*> 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)
|
||||
|
|
|
@ -177,8 +177,6 @@ public:
|
|||
|
||||
Vector<Widget*> focusable_widgets() const;
|
||||
|
||||
virtual void save_to(AK::JsonObject&) override;
|
||||
|
||||
void schedule_relayout();
|
||||
|
||||
static void for_each_window(Badge<WindowServerConnection>, Function<void(Window&)>);
|
||||
|
|
|
@ -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<TextAlignment> 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 {};
|
||||
}
|
||||
|
||||
|
|
|
@ -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<bool(Editor&)> cb)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Protocol::Client> m_protocol_client;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue