summaryrefslogtreecommitdiff
path: root/Libraries/LibCore
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-09-15 21:33:37 +0200
committerAndreas Kling <kling@serenityos.org>2020-09-15 21:46:26 +0200
commite2f32b8f9d4ce0ce8f92d4817273fb9b32cd8ccd (patch)
treebcc49c2fd5a87ffbfc992d0b1406b4c64e47941b /Libraries/LibCore
parent1e96e46a8137e297d1d38271373145ac21f4da3e (diff)
downloadserenity-e2f32b8f9d4ce0ce8f92d4817273fb9b32cd8ccd.zip
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.
Diffstat (limited to 'Libraries/LibCore')
-rw-r--r--Libraries/LibCore/CMakeLists.txt1
-rw-r--r--Libraries/LibCore/Object.cpp41
-rw-r--r--Libraries/LibCore/Object.h110
-rw-r--r--Libraries/LibCore/Property.cpp42
-rw-r--r--Libraries/LibCore/Property.h58
5 files changed, 241 insertions, 11 deletions
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<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 << '}';
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 <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; \
+ });
}
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 <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()
+{
+}
+
+}
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 <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;
+};
+
+}