From 405187993ad8363da315f2bb1979345c8234d95d Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 6 Aug 2023 15:14:59 +0200 Subject: [PATCH] LibGUI+LibCore: Move GML property system from LibCore to LibGUI Since Core::Object properties are really only used by GML now that the Inspector is long gone, there's no need for these to pollute Core::Object. This patch adds a new GUI::Object class to hold properties, and makes it the new base class of GUI::Window, GUI::Widget and GUI::Layout. The "instantiate an object by name" mechanism that GML uses is also hoisted into GUI::Object as well. --- Userland/Libraries/LibCore/CMakeLists.txt | 1 - Userland/Libraries/LibCore/Object.cpp | 66 ----- Userland/Libraries/LibCore/Object.h | 227 +--------------- Userland/Libraries/LibGUI/CMakeLists.txt | 2 + Userland/Libraries/LibGUI/Forward.h | 1 + .../LibGUI/GML/AutocompleteProvider.cpp | 12 +- Userland/Libraries/LibGUI/Layout.cpp | 2 +- Userland/Libraries/LibGUI/Layout.h | 16 +- Userland/Libraries/LibGUI/Object.cpp | 74 ++++++ Userland/Libraries/LibGUI/Object.h | 249 ++++++++++++++++++ .../{LibCore => LibGUI}/Property.cpp | 4 +- .../Libraries/{LibCore => LibGUI}/Property.h | 2 +- .../LibGUI/ScrollableContainerWidget.cpp | 2 +- Userland/Libraries/LibGUI/Widget.cpp | 24 +- Userland/Libraries/LibGUI/Widget.h | 17 +- Userland/Libraries/LibGUI/Window.cpp | 2 +- Userland/Libraries/LibGUI/Window.h | 4 +- Userland/Libraries/LibThreading/Thread.cpp | 5 - Userland/Utilities/js.cpp | 1 + 19 files changed, 376 insertions(+), 335 deletions(-) create mode 100644 Userland/Libraries/LibGUI/Object.cpp create mode 100644 Userland/Libraries/LibGUI/Object.h rename Userland/Libraries/{LibCore => LibGUI}/Property.cpp (88%) rename Userland/Libraries/{LibCore => LibGUI}/Property.h (98%) diff --git a/Userland/Libraries/LibCore/CMakeLists.txt b/Userland/Libraries/LibCore/CMakeLists.txt index c4f530dcac..9de449528a 100644 --- a/Userland/Libraries/LibCore/CMakeLists.txt +++ b/Userland/Libraries/LibCore/CMakeLists.txt @@ -21,7 +21,6 @@ set(SOURCES Object.cpp Process.cpp ProcessStatisticsReader.cpp - Property.cpp SecretString.cpp SessionManagement.cpp Socket.cpp diff --git a/Userland/Libraries/LibCore/Object.cpp b/Userland/Libraries/LibCore/Object.cpp index 068315a9a2..684645830c 100644 --- a/Userland/Libraries/LibCore/Object.cpp +++ b/Userland/Libraries/LibCore/Object.cpp @@ -27,17 +27,6 @@ Object::Object(Object* parent) all_objects().append(*this); if (m_parent) m_parent->add_child(*this); - - REGISTER_READONLY_STRING_PROPERTY("class_name", class_name); - REGISTER_DEPRECATED_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() @@ -176,22 +165,6 @@ void Object::deferred_invoke(Function invokee) Core::deferred_invoke([invokee = move(invokee), strong_this = NonnullRefPtr(*this)] { invokee(); }); } -JsonValue Object::property(DeprecatedString const& name) const -{ - auto it = m_properties.find(name); - if (it == m_properties.end()) - return JsonValue(); - return it->value->get(); -} - -bool Object::set_property(DeprecatedString const& name, JsonValue const& value) -{ - auto it = m_properties.find(name); - if (it == m_properties.end()) - return false; - return it->value->set(value); -} - bool Object::is_ancestor_of(Object const& other) const { if (&other == this) @@ -241,48 +214,9 @@ void Object::decrement_inspector_count(Badge) did_end_inspection(); } -void Object::register_property(DeprecatedString const& name, Function getter, Function setter) -{ - m_properties.set(name, make(name, move(getter), move(setter))); -} - void Object::set_event_filter(Function filter) { m_event_filter = move(filter); } -static HashMap& object_classes() -{ - static HashMap s_map; - return s_map; -} - -ObjectClassRegistration::ObjectClassRegistration(StringView class_name, Function>()> factory, ObjectClassRegistration* parent_class) - : m_class_name(class_name) - , m_factory(move(factory)) - , m_parent_class(parent_class) -{ - object_classes().set(class_name, this); -} - -bool ObjectClassRegistration::is_derived_from(ObjectClassRegistration const& base_class) const -{ - if (&base_class == this) - return true; - if (!m_parent_class) - return false; - return m_parent_class->is_derived_from(base_class); -} - -void ObjectClassRegistration::for_each(Function callback) -{ - for (auto& it : object_classes()) { - callback(*it.value); - } -} - -ObjectClassRegistration const* ObjectClassRegistration::find(StringView class_name) -{ - return object_classes().get(class_name).value_or(nullptr); -} } diff --git a/Userland/Libraries/LibCore/Object.h b/Userland/Libraries/LibCore/Object.h index cea9759599..edc2189ca0 100644 --- a/Userland/Libraries/LibCore/Object.h +++ b/Userland/Libraries/LibCore/Object.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -17,46 +18,9 @@ #include #include #include -#include namespace Core { -#define REGISTER_ABSTRACT_CORE_OBJECT(namespace_, class_name) \ - namespace Core { \ - namespace Registration { \ - Core::ObjectClassRegistration registration_##class_name(#namespace_ "::" #class_name##sv, []() { return Error::from_string_literal("Attempted to construct an abstract object."); }); \ - } \ - } - -#define REGISTER_CORE_OBJECT(namespace_, class_name) \ - namespace Core { \ - namespace Registration { \ - Core::ObjectClassRegistration registration_##class_name(#namespace_ "::" #class_name##sv, []() { return namespace_::class_name::try_create(); }); \ - } \ - } - -class ObjectClassRegistration { - AK_MAKE_NONCOPYABLE(ObjectClassRegistration); - AK_MAKE_NONMOVABLE(ObjectClassRegistration); - -public: - ObjectClassRegistration(StringView class_name, Function>()> factory, ObjectClassRegistration* parent_class = nullptr); - ~ObjectClassRegistration() = default; - - StringView class_name() const { return m_class_name; } - ObjectClassRegistration const* parent_class() const { return m_parent_class; } - ErrorOr> construct() const { return m_factory(); } - bool is_derived_from(ObjectClassRegistration const& base_class) const; - - static void for_each(Function); - static ObjectClassRegistration const* find(StringView class_name); - -private: - StringView m_class_name; - Function>()> m_factory; - ObjectClassRegistration* m_parent_class { nullptr }; -}; - class InspectorServerConnection; enum class TimerShouldFireWhenNotVisible { @@ -171,10 +135,6 @@ public: void deferred_invoke(Function); - bool set_property(DeprecatedString const& name, JsonValue const& value); - JsonValue property(DeprecatedString const& name) const; - HashMap> const& properties() const { return m_properties; } - static IntrusiveList<&Object::m_all_objects_list_node>& all_objects(); void dispatch_event(Core::Event&, Object* stay_within = nullptr); @@ -211,8 +171,6 @@ public: protected: explicit Object(Object* parent = nullptr); - void register_property(DeprecatedString const& name, Function getter, Function setter = nullptr); - virtual void event(Core::Event&); virtual void timer_event(TimerEvent&); @@ -229,7 +187,6 @@ private: DeprecatedString m_name; int m_timer_id { 0 }; unsigned m_inspector_count { 0 }; - HashMap> m_properties; Vector> m_children; Function m_event_filter; }; @@ -289,186 +246,4 @@ requires IsBaseOf return found_child; } -#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()); \ - 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; \ - }); - -// FIXME: Port JsonValue to the new String class. -#define REGISTER_STRING_PROPERTY(property_name, getter, setter) \ - register_property( \ - property_name, \ - [this]() { return this->getter().to_deprecated_string(); }, \ - [this](auto& value) { \ - this->setter(String::from_deprecated_string(value.to_deprecated_string()).release_value_but_fixme_should_propagate_errors()); \ - return true; \ - }); - -#define REGISTER_DEPRECATED_STRING_PROPERTY(property_name, getter, setter) \ - register_property( \ - property_name, \ - [this] { return this->getter(); }, \ - [this](auto& value) { \ - this->setter(value.to_deprecated_string()); \ - return true; \ - }); - -#define REGISTER_READONLY_STRING_PROPERTY(property_name, getter) \ - register_property( \ - property_name, \ - [this] { return this->getter(); }, \ - {}); - -#define REGISTER_WRITE_ONLY_STRING_PROPERTY(property_name, setter) \ - register_property( \ - property_name, \ - {}, \ - [this](auto& value) { \ - this->setter(value.to_deprecated_string()); \ - return true; \ - }); - -#define REGISTER_READONLY_SIZE_PROPERTY(property_name, getter) \ - register_property( \ - property_name, \ - [this] { \ - auto size = this->getter(); \ - JsonArray size_array; \ - size_array.must_append(size.width()); \ - size_array.must_append(size.height()); \ - return size_array; \ - }, \ - {}); - -#define REGISTER_RECT_PROPERTY(property_name, getter, setter) \ - register_property( \ - property_name, \ - [this] { \ - auto rect = this->getter(); \ - JsonObject rect_object; \ - rect_object.set("x"sv, rect.x()); \ - rect_object.set("y"sv, rect.y()); \ - rect_object.set("width"sv, rect.width()); \ - rect_object.set("height"sv, rect.height()); \ - return rect_object; \ - }, \ - [this](auto& value) { \ - Gfx::IntRect rect; \ - if (value.is_object()) { \ - rect.set_x(value.as_object().get_i32("x"sv).value_or(0)); \ - rect.set_y(value.as_object().get_i32("y"sv).value_or(0)); \ - rect.set_width(value.as_object().get_i32("width"sv).value_or(0)); \ - rect.set_height(value.as_object().get_i32("height"sv).value_or(0)); \ - } else if (value.is_array() && value.as_array().size() == 4) { \ - rect.set_x(value.as_array()[0].to_i32()); \ - rect.set_y(value.as_array()[1].to_i32()); \ - rect.set_width(value.as_array()[2].to_i32()); \ - rect.set_height(value.as_array()[3].to_i32()); \ - } else { \ - return false; \ - } \ - setter(rect); \ - \ - return true; \ - }); - -#define REGISTER_SIZE_PROPERTY(property_name, getter, setter) \ - register_property( \ - property_name, \ - [this] { \ - auto size = this->getter(); \ - JsonArray size_array; \ - size_array.must_append(size.width()); \ - size_array.must_append(size.height()); \ - return size_array; \ - }, \ - [this](auto& value) { \ - if (!value.is_array()) \ - return false; \ - Gfx::IntSize size; \ - size.set_width(value.as_array()[0].to_i32()); \ - size.set_height(value.as_array()[1].to_i32()); \ - setter(size); \ - return true; \ - }); - -#define REGISTER_ENUM_PROPERTY(property_name, getter, setter, EnumType, ...) \ - register_property( \ - property_name, \ - [this]() -> JsonValue { \ - struct { \ - EnumType enum_value; \ - DeprecatedString string_value; \ - } options[] = { __VA_ARGS__ }; \ - auto enum_value = getter(); \ - for (size_t i = 0; i < array_size(options); ++i) { \ - auto& option = options[i]; \ - if (enum_value == option.enum_value) \ - return option.string_value; \ - } \ - return JsonValue(); \ - }, \ - [this](auto& value) { \ - struct { \ - EnumType enum_value; \ - DeprecatedString string_value; \ - } options[] = { __VA_ARGS__ }; \ - if (!value.is_string()) \ - return false; \ - auto string_value = value.as_string(); \ - for (size_t i = 0; i < array_size(options); ++i) { \ - auto& option = options[i]; \ - if (string_value == option.string_value) { \ - setter(option.enum_value); \ - return true; \ - } \ - } \ - return false; \ - }) - -#define REGISTER_TEXT_ALIGNMENT_PROPERTY(property_name, getter, setter) \ - REGISTER_ENUM_PROPERTY( \ - property_name, getter, setter, Gfx::TextAlignment, \ - { Gfx::TextAlignment::Center, "Center" }, \ - { Gfx::TextAlignment::CenterLeft, "CenterLeft" }, \ - { Gfx::TextAlignment::CenterRight, "CenterRight" }, \ - { Gfx::TextAlignment::TopCenter, "TopCenter" }, \ - { Gfx::TextAlignment::TopLeft, "TopLeft" }, \ - { Gfx::TextAlignment::TopRight, "TopRight" }, \ - { Gfx::TextAlignment::BottomCenter, "BottomCenter" }, \ - { Gfx::TextAlignment::BottomLeft, "BottomLeft" }, \ - { Gfx::TextAlignment::BottomRight, "BottomRight" }) - -#define REGISTER_FONT_WEIGHT_PROPERTY(property_name, getter, setter) \ - REGISTER_ENUM_PROPERTY( \ - property_name, getter, setter, unsigned, \ - { Gfx::FontWeight::Thin, "Thin" }, \ - { Gfx::FontWeight::ExtraLight, "ExtraLight" }, \ - { Gfx::FontWeight::Light, "Light" }, \ - { Gfx::FontWeight::Regular, "Regular" }, \ - { Gfx::FontWeight::Medium, "Medium" }, \ - { Gfx::FontWeight::SemiBold, "SemiBold" }, \ - { Gfx::FontWeight::Bold, "Bold" }, \ - { Gfx::FontWeight::ExtraBold, "ExtraBold" }, \ - { Gfx::FontWeight::Black, "Black" }, \ - { Gfx::FontWeight::ExtraBlack, "ExtraBlack" }) - -#define REGISTER_TEXT_WRAPPING_PROPERTY(property_name, getter, setter) \ - REGISTER_ENUM_PROPERTY( \ - property_name, getter, setter, Gfx::TextWrapping, \ - { Gfx::TextWrapping::Wrap, "Wrap" }, \ - { Gfx::TextWrapping::DontWrap, "DontWrap" }) } diff --git a/Userland/Libraries/LibGUI/CMakeLists.txt b/Userland/Libraries/LibGUI/CMakeLists.txt index f49d20a664..d5a78d0ecb 100644 --- a/Userland/Libraries/LibGUI/CMakeLists.txt +++ b/Userland/Libraries/LibGUI/CMakeLists.txt @@ -79,6 +79,7 @@ set(SOURCES MouseTracker.cpp MultiView.cpp Notification.cpp + Object.cpp OpacitySlider.cpp Painter.cpp PasswordInputDialog.cpp @@ -87,6 +88,7 @@ set(SOURCES Process.cpp ProcessChooser.cpp Progressbar.cpp + Property.cpp RadioButton.cpp RangeSlider.cpp RegularEditingEngine.cpp diff --git a/Userland/Libraries/LibGUI/Forward.h b/Userland/Libraries/LibGUI/Forward.h index a015e784c0..3ee49c9f26 100644 --- a/Userland/Libraries/LibGUI/Forward.h +++ b/Userland/Libraries/LibGUI/Forward.h @@ -54,6 +54,7 @@ class ModelIndex; class MouseEvent; class MultiPaintEvent; class MultiView; +class Object; class OpacitySlider; class PaintEvent; class Painter; diff --git a/Userland/Libraries/LibGUI/GML/AutocompleteProvider.cpp b/Userland/Libraries/LibGUI/GML/AutocompleteProvider.cpp index ea5e74be1d..4115fad0fe 100644 --- a/Userland/Libraries/LibGUI/GML/AutocompleteProvider.cpp +++ b/Userland/Libraries/LibGUI/GML/AutocompleteProvider.cpp @@ -107,8 +107,8 @@ void AutocompleteProvider::provide_completions(Function class_entries, identifier_entries; auto register_layouts_matching_pattern = [&](DeprecatedString pattern, size_t partial_input_length) { - Core::ObjectClassRegistration::for_each([&](const Core::ObjectClassRegistration& registration) { + GUI::ObjectClassRegistration::for_each([&](const GUI::ObjectClassRegistration& registration) { if (registration.is_derived_from(layout_class) && ®istration != &layout_class && registration.class_name().matches(pattern)) class_entries.empend(DeprecatedString::formatted("@{}", registration.class_name()), partial_input_length); }); }; auto register_widgets_matching_pattern = [&](DeprecatedString pattern, size_t partial_input_length) { - Core::ObjectClassRegistration::for_each([&](const Core::ObjectClassRegistration& registration) { + GUI::ObjectClassRegistration::for_each([&](const GUI::ObjectClassRegistration& registration) { if (registration.is_derived_from(widget_class) && registration.class_name().matches(pattern)) class_entries.empend(DeprecatedString::formatted("@{}", registration.class_name()), partial_input_length); }); @@ -141,7 +141,7 @@ void AutocompleteProvider::provide_completions(Functionis_derived_from(widget_class) || registration->is_derived_from(layout_class))) { if (auto instance = registration->construct(); !instance.is_error()) { for (auto& it : instance.value()->properties()) { @@ -161,7 +161,7 @@ void AutocompleteProvider::provide_completions(Functionis_derived_from(layout_class)) { // Layouts can't have child classes, so why suggest them? return; diff --git a/Userland/Libraries/LibGUI/Layout.cpp b/Userland/Libraries/LibGUI/Layout.cpp index 156d4653b6..52528515ba 100644 --- a/Userland/Libraries/LibGUI/Layout.cpp +++ b/Userland/Libraries/LibGUI/Layout.cpp @@ -10,7 +10,7 @@ #include #include -REGISTER_ABSTRACT_CORE_OBJECT(GUI, Layout) +REGISTER_ABSTRACT_GUI_OBJECT(GUI, Layout) namespace GUI { diff --git a/Userland/Libraries/LibGUI/Layout.h b/Userland/Libraries/LibGUI/Layout.h index 79fa68d42f..d917917e84 100644 --- a/Userland/Libraries/LibGUI/Layout.h +++ b/Userland/Libraries/LibGUI/Layout.h @@ -9,25 +9,25 @@ #include #include #include -#include #include #include +#include #include #include -namespace Core::Registration { -extern Core::ObjectClassRegistration registration_Layout; +namespace GUI::Registration { +extern GUI::ObjectClassRegistration registration_Layout; } -#define REGISTER_LAYOUT(namespace_, class_name) \ - namespace Core::Registration { \ - Core::ObjectClassRegistration registration_##class_name( \ - #namespace_ "::" #class_name##sv, []() { return static_ptr_cast(namespace_::class_name::construct()); }, ®istration_Layout); \ +#define REGISTER_LAYOUT(namespace_, class_name) \ + namespace GUI::Registration { \ + ::GUI::ObjectClassRegistration registration_##class_name( \ + #namespace_ "::" #class_name##sv, []() { return static_ptr_cast(namespace_::class_name::construct()); }, ®istration_Layout); \ } namespace GUI { -class Layout : public Core::Object { +class Layout : public GUI::Object { C_OBJECT_ABSTRACT(Layout); public: diff --git a/Userland/Libraries/LibGUI/Object.cpp b/Userland/Libraries/LibGUI/Object.cpp new file mode 100644 index 0000000000..29af94ad35 --- /dev/null +++ b/Userland/Libraries/LibGUI/Object.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace GUI { + +Object::Object(Core::Object* parent) + : Core::Object(parent) +{ +} + +Object::~Object() = default; + +void Object::register_property(DeprecatedString const& name, Function getter, Function setter) +{ + m_properties.set(name, make(name, move(getter), move(setter))); +} + +JsonValue Object::property(DeprecatedString const& name) const +{ + auto it = m_properties.find(name); + if (it == m_properties.end()) + return JsonValue(); + return it->value->get(); +} + +bool Object::set_property(DeprecatedString const& name, JsonValue const& value) +{ + auto it = m_properties.find(name); + if (it == m_properties.end()) + return false; + return it->value->set(value); +} + +static HashMap& object_classes() +{ + static HashMap s_map; + return s_map; +} + +ObjectClassRegistration::ObjectClassRegistration(StringView class_name, Function>()> factory, ObjectClassRegistration* parent_class) + : m_class_name(class_name) + , m_factory(move(factory)) + , m_parent_class(parent_class) +{ + object_classes().set(class_name, this); +} + +bool ObjectClassRegistration::is_derived_from(ObjectClassRegistration const& base_class) const +{ + if (&base_class == this) + return true; + if (!m_parent_class) + return false; + return m_parent_class->is_derived_from(base_class); +} + +void ObjectClassRegistration::for_each(Function callback) +{ + for (auto& it : object_classes()) { + callback(*it.value); + } +} + +ObjectClassRegistration const* ObjectClassRegistration::find(StringView class_name) +{ + return object_classes().get(class_name).value_or(nullptr); +} + +} diff --git a/Userland/Libraries/LibGUI/Object.h b/Userland/Libraries/LibGUI/Object.h new file mode 100644 index 0000000000..3c8c81a2d5 --- /dev/null +++ b/Userland/Libraries/LibGUI/Object.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2023, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace GUI { + +#define REGISTER_ABSTRACT_GUI_OBJECT(namespace_, class_name) \ + namespace GUI::Registration { \ + ::GUI::ObjectClassRegistration registration_##class_name(#namespace_ "::" #class_name##sv, []() { return Error::from_string_literal("Attempted to construct an abstract object."); }); \ + } + +#define REGISTER_GUI_OBJECT(namespace_, class_name) \ + namespace GUI::Registration { \ + ::GUI::ObjectClassRegistration registration_##class_name(#namespace_ "::" #class_name##sv, []() { return namespace_::class_name::try_create(); }); \ + } + +class ObjectClassRegistration { + AK_MAKE_NONCOPYABLE(ObjectClassRegistration); + AK_MAKE_NONMOVABLE(ObjectClassRegistration); + +public: + ObjectClassRegistration(StringView class_name, Function>()> factory, ObjectClassRegistration* parent_class = nullptr); + ~ObjectClassRegistration() = default; + + StringView class_name() const { return m_class_name; } + ObjectClassRegistration const* parent_class() const { return m_parent_class; } + ErrorOr> construct() const { return m_factory(); } + bool is_derived_from(ObjectClassRegistration const& base_class) const; + + static void for_each(Function); + static ObjectClassRegistration const* find(StringView class_name); + +private: + StringView m_class_name; + Function>()> m_factory; + ObjectClassRegistration* m_parent_class { nullptr }; +}; + +class Object : public Core::Object { + C_OBJECT_ABSTRACT(Object); + +public: + virtual ~Object() override; + + bool set_property(DeprecatedString const& name, JsonValue const& value); + JsonValue property(DeprecatedString const& name) const; + HashMap> const& properties() const { return m_properties; } + +protected: + explicit Object(Core::Object* parent = nullptr); + + void register_property(DeprecatedString const& name, Function getter, Function setter = nullptr); + +private: + HashMap> m_properties; +}; + +} + +#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()); \ + 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; \ + }); + +// FIXME: Port JsonValue to the new String class. +#define REGISTER_STRING_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, \ + [this]() { return this->getter().to_deprecated_string(); }, \ + [this](auto& value) { \ + this->setter(String::from_deprecated_string(value.to_deprecated_string()).release_value_but_fixme_should_propagate_errors()); \ + return true; \ + }); + +#define REGISTER_DEPRECATED_STRING_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, \ + [this] { return this->getter(); }, \ + [this](auto& value) { \ + this->setter(value.to_deprecated_string()); \ + return true; \ + }); + +#define REGISTER_READONLY_STRING_PROPERTY(property_name, getter) \ + register_property( \ + property_name, \ + [this] { return this->getter(); }, \ + {}); + +#define REGISTER_WRITE_ONLY_STRING_PROPERTY(property_name, setter) \ + register_property( \ + property_name, \ + {}, \ + [this](auto& value) { \ + this->setter(value.to_deprecated_string()); \ + return true; \ + }); + +#define REGISTER_READONLY_SIZE_PROPERTY(property_name, getter) \ + register_property( \ + property_name, \ + [this] { \ + auto size = this->getter(); \ + JsonArray size_array; \ + size_array.must_append(size.width()); \ + size_array.must_append(size.height()); \ + return size_array; \ + }, \ + {}); + +#define REGISTER_RECT_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, \ + [this] { \ + auto rect = this->getter(); \ + JsonObject rect_object; \ + rect_object.set("x"sv, rect.x()); \ + rect_object.set("y"sv, rect.y()); \ + rect_object.set("width"sv, rect.width()); \ + rect_object.set("height"sv, rect.height()); \ + return rect_object; \ + }, \ + [this](auto& value) { \ + Gfx::IntRect rect; \ + if (value.is_object()) { \ + rect.set_x(value.as_object().get_i32("x"sv).value_or(0)); \ + rect.set_y(value.as_object().get_i32("y"sv).value_or(0)); \ + rect.set_width(value.as_object().get_i32("width"sv).value_or(0)); \ + rect.set_height(value.as_object().get_i32("height"sv).value_or(0)); \ + } else if (value.is_array() && value.as_array().size() == 4) { \ + rect.set_x(value.as_array()[0].to_i32()); \ + rect.set_y(value.as_array()[1].to_i32()); \ + rect.set_width(value.as_array()[2].to_i32()); \ + rect.set_height(value.as_array()[3].to_i32()); \ + } else { \ + return false; \ + } \ + setter(rect); \ + \ + return true; \ + }); + +#define REGISTER_SIZE_PROPERTY(property_name, getter, setter) \ + register_property( \ + property_name, \ + [this] { \ + auto size = this->getter(); \ + JsonArray size_array; \ + size_array.must_append(size.width()); \ + size_array.must_append(size.height()); \ + return size_array; \ + }, \ + [this](auto& value) { \ + if (!value.is_array()) \ + return false; \ + Gfx::IntSize size; \ + size.set_width(value.as_array()[0].to_i32()); \ + size.set_height(value.as_array()[1].to_i32()); \ + setter(size); \ + return true; \ + }); + +#define REGISTER_ENUM_PROPERTY(property_name, getter, setter, EnumType, ...) \ + register_property( \ + property_name, \ + [this]() -> JsonValue { \ + struct { \ + EnumType enum_value; \ + DeprecatedString string_value; \ + } options[] = { __VA_ARGS__ }; \ + auto enum_value = getter(); \ + for (size_t i = 0; i < array_size(options); ++i) { \ + auto& option = options[i]; \ + if (enum_value == option.enum_value) \ + return option.string_value; \ + } \ + return JsonValue(); \ + }, \ + [this](auto& value) { \ + struct { \ + EnumType enum_value; \ + DeprecatedString string_value; \ + } options[] = { __VA_ARGS__ }; \ + if (!value.is_string()) \ + return false; \ + auto string_value = value.as_string(); \ + for (size_t i = 0; i < array_size(options); ++i) { \ + auto& option = options[i]; \ + if (string_value == option.string_value) { \ + setter(option.enum_value); \ + return true; \ + } \ + } \ + return false; \ + }) + +#define REGISTER_TEXT_ALIGNMENT_PROPERTY(property_name, getter, setter) \ + REGISTER_ENUM_PROPERTY( \ + property_name, getter, setter, Gfx::TextAlignment, \ + { Gfx::TextAlignment::Center, "Center" }, \ + { Gfx::TextAlignment::CenterLeft, "CenterLeft" }, \ + { Gfx::TextAlignment::CenterRight, "CenterRight" }, \ + { Gfx::TextAlignment::TopCenter, "TopCenter" }, \ + { Gfx::TextAlignment::TopLeft, "TopLeft" }, \ + { Gfx::TextAlignment::TopRight, "TopRight" }, \ + { Gfx::TextAlignment::BottomCenter, "BottomCenter" }, \ + { Gfx::TextAlignment::BottomLeft, "BottomLeft" }, \ + { Gfx::TextAlignment::BottomRight, "BottomRight" }) + +#define REGISTER_FONT_WEIGHT_PROPERTY(property_name, getter, setter) \ + REGISTER_ENUM_PROPERTY( \ + property_name, getter, setter, unsigned, \ + { Gfx::FontWeight::Thin, "Thin" }, \ + { Gfx::FontWeight::ExtraLight, "ExtraLight" }, \ + { Gfx::FontWeight::Light, "Light" }, \ + { Gfx::FontWeight::Regular, "Regular" }, \ + { Gfx::FontWeight::Medium, "Medium" }, \ + { Gfx::FontWeight::SemiBold, "SemiBold" }, \ + { Gfx::FontWeight::Bold, "Bold" }, \ + { Gfx::FontWeight::ExtraBold, "ExtraBold" }, \ + { Gfx::FontWeight::Black, "Black" }, \ + { Gfx::FontWeight::ExtraBlack, "ExtraBlack" }) + +#define REGISTER_TEXT_WRAPPING_PROPERTY(property_name, getter, setter) \ + REGISTER_ENUM_PROPERTY( \ + property_name, getter, setter, Gfx::TextWrapping, \ + { Gfx::TextWrapping::Wrap, "Wrap" }, \ + { Gfx::TextWrapping::DontWrap, "DontWrap" }) diff --git a/Userland/Libraries/LibCore/Property.cpp b/Userland/Libraries/LibGUI/Property.cpp similarity index 88% rename from Userland/Libraries/LibCore/Property.cpp rename to Userland/Libraries/LibGUI/Property.cpp index deaafe03dd..ea27cf1815 100644 --- a/Userland/Libraries/LibCore/Property.cpp +++ b/Userland/Libraries/LibGUI/Property.cpp @@ -5,9 +5,9 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include +#include -namespace Core { +namespace GUI { Property::Property(DeprecatedString name, Function getter, Function setter) : m_name(move(name)) diff --git a/Userland/Libraries/LibCore/Property.h b/Userland/Libraries/LibGUI/Property.h similarity index 98% rename from Userland/Libraries/LibCore/Property.h rename to Userland/Libraries/LibGUI/Property.h index e34fde9418..ed0bc73f2d 100644 --- a/Userland/Libraries/LibCore/Property.h +++ b/Userland/Libraries/LibGUI/Property.h @@ -10,7 +10,7 @@ #include #include -namespace Core { +namespace GUI { class Property { AK_MAKE_NONCOPYABLE(Property); diff --git a/Userland/Libraries/LibGUI/ScrollableContainerWidget.cpp b/Userland/Libraries/LibGUI/ScrollableContainerWidget.cpp index 9ecb198e7d..27f8770dcc 100644 --- a/Userland/Libraries/LibGUI/ScrollableContainerWidget.cpp +++ b/Userland/Libraries/LibGUI/ScrollableContainerWidget.cpp @@ -130,7 +130,7 @@ ErrorOr ScrollableContainerWidget::load_from_gml_ast(NonnullRefPtr child; - if (auto* registration = Core::ObjectClassRegistration::find(class_name)) { + if (auto* registration = GUI::ObjectClassRegistration::find(class_name)) { child = TRY(registration->construct()); } else { child = TRY(unregistered_child_handler(class_name)); diff --git a/Userland/Libraries/LibGUI/Widget.cpp b/Userland/Libraries/LibGUI/Widget.cpp index 1bea25afa3..e99000a118 100644 --- a/Userland/Libraries/LibGUI/Widget.cpp +++ b/Userland/Libraries/LibGUI/Widget.cpp @@ -31,17 +31,27 @@ #include #include -REGISTER_CORE_OBJECT(GUI, Widget) +REGISTER_GUI_OBJECT(GUI, Widget) namespace GUI { Widget::Widget() - : Core::Object(nullptr) - , m_background_role(Gfx::ColorRole::Window) + : m_background_role(Gfx::ColorRole::Window) , m_foreground_role(Gfx::ColorRole::WindowText) , m_font(Gfx::FontDatabase::default_font()) , m_palette(Application::the()->palette().impl()) { + REGISTER_READONLY_STRING_PROPERTY("class_name", class_name); + REGISTER_DEPRECATED_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; }); + 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); @@ -1170,8 +1180,8 @@ ErrorOr Widget::load_from_gml_ast(NonnullRefPtr ast, return Error::from_string_literal("Invalid layout class name"); } - auto& layout_class = *Core::ObjectClassRegistration::find("GUI::Layout"sv); - if (auto* registration = Core::ObjectClassRegistration::find(class_name)) { + auto& layout_class = *GUI::ObjectClassRegistration::find("GUI::Layout"sv); + if (auto* registration = GUI::ObjectClassRegistration::find(class_name)) { auto layout = TRY(registration->construct()); if (!registration->is_derived_from(layout_class)) { dbgln("Invalid layout class: '{}'", class_name.to_deprecated_string()); @@ -1188,7 +1198,7 @@ ErrorOr Widget::load_from_gml_ast(NonnullRefPtr ast, }); } - auto& widget_class = *Core::ObjectClassRegistration::find("GUI::Widget"sv); + auto& widget_class = *GUI::ObjectClassRegistration::find("GUI::Widget"sv); bool is_tab_widget = is(*this); TRY(object.try_for_each_child_object([&](auto const& child_data) -> ErrorOr { auto class_name = child_data.name(); @@ -1201,7 +1211,7 @@ ErrorOr Widget::load_from_gml_ast(NonnullRefPtr ast, this->layout()->add_spacer(); } else { RefPtr child; - if (auto* registration = Core::ObjectClassRegistration::find(class_name)) { + if (auto* registration = GUI::ObjectClassRegistration::find(class_name)) { child = TRY(registration->construct()); if (!registration->is_derived_from(widget_class)) { dbgln("Invalid widget class: '{}'", class_name); diff --git a/Userland/Libraries/LibGUI/Widget.h b/Userland/Libraries/LibGUI/Widget.h index 2d4c45992b..da32090cb1 100644 --- a/Userland/Libraries/LibGUI/Widget.h +++ b/Userland/Libraries/LibGUI/Widget.h @@ -13,13 +13,14 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -27,14 +28,14 @@ #include #include -namespace Core::Registration { -extern Core::ObjectClassRegistration registration_Widget; +namespace GUI::Registration { +extern GUI::ObjectClassRegistration registration_Widget; } -#define REGISTER_WIDGET(namespace_, class_name) \ - namespace Core::Registration { \ - Core::ObjectClassRegistration registration_##class_name( \ - #namespace_ "::" #class_name##sv, []() -> ErrorOr> { return static_ptr_cast(TRY(namespace_::class_name::try_create())); }, ®istration_Widget); \ +#define REGISTER_WIDGET(namespace_, class_name) \ + namespace GUI::Registration { \ + GUI::ObjectClassRegistration registration_##class_name( \ + #namespace_ "::" #class_name##sv, []() -> ErrorOr> { return static_ptr_cast(TRY(namespace_::class_name::try_create())); }, ®istration_Widget); \ } namespace GUI { @@ -70,7 +71,7 @@ enum class AllowCallback { Yes }; -class Widget : public Core::Object { +class Widget : public GUI::Object { C_OBJECT(Widget) public: virtual ~Widget() override; diff --git a/Userland/Libraries/LibGUI/Window.cpp b/Userland/Libraries/LibGUI/Window.cpp index 445c5644d0..8f796a783a 100644 --- a/Userland/Libraries/LibGUI/Window.cpp +++ b/Userland/Libraries/LibGUI/Window.cpp @@ -72,7 +72,7 @@ Window* Window::from_window_id(int window_id) } Window::Window(Core::Object* parent) - : Core::Object(parent) + : GUI::Object(parent) , m_menubar(Menubar::construct()) { if (parent) diff --git a/Userland/Libraries/LibGUI/Window.h b/Userland/Libraries/LibGUI/Window.h index dd0e758cf5..bad741fa89 100644 --- a/Userland/Libraries/LibGUI/Window.h +++ b/Userland/Libraries/LibGUI/Window.h @@ -13,9 +13,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -27,7 +27,7 @@ namespace GUI { class WindowBackingStore; -class Window : public Core::Object { +class Window : public GUI::Object { C_OBJECT(Window) public: virtual ~Window() override; diff --git a/Userland/Libraries/LibThreading/Thread.cpp b/Userland/Libraries/LibThreading/Thread.cpp index 668a8f8ccf..427970419c 100644 --- a/Userland/Libraries/LibThreading/Thread.cpp +++ b/Userland/Libraries/LibThreading/Thread.cpp @@ -16,11 +16,6 @@ Thread::Thread(Function action, StringView thread_name) , m_action(move(action)) , m_thread_name(thread_name.is_null() ? ""sv : thread_name) { - register_property("thread_name", [&] { return JsonValue { m_thread_name }; }); -#if defined(AK_OS_SERENITY) || defined(AK_OS_LINUX) - // FIXME: Print out a pretty TID for BSD and macOS platforms, too - register_property("tid", [&] { return JsonValue { m_tid }; }); -#endif } Thread::~Thread() diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp index ef179e6324..e5016aa9bf 100644 --- a/Userland/Utilities/js.cpp +++ b/Userland/Utilities/js.cpp @@ -6,6 +6,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include