diff options
author | Andreas Kling <kling@serenityos.org> | 2020-09-15 21:33:37 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-09-15 21:46:26 +0200 |
commit | e2f32b8f9d4ce0ce8f92d4817273fb9b32cd8ccd (patch) | |
tree | bcc49c2fd5a87ffbfc992d0b1406b4c64e47941b /Libraries/LibCore | |
parent | 1e96e46a8137e297d1d38271373145ac21f4da3e (diff) | |
download | serenity-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.txt | 1 | ||||
-rw-r--r-- | Libraries/LibCore/Object.cpp | 41 | ||||
-rw-r--r-- | Libraries/LibCore/Object.h | 110 | ||||
-rw-r--r-- | Libraries/LibCore/Property.cpp | 42 | ||||
-rw-r--r-- | Libraries/LibCore/Property.h | 58 |
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; +}; + +} |