mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 15:47:44 +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
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;
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue