1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 13:48: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:
Andreas Kling 2020-09-15 21:33:37 +02:00
parent 1e96e46a81
commit e2f32b8f9d
23 changed files with 373 additions and 250 deletions

View file

@ -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; \
});
}