Serenity Operating System
1/*
2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#pragma once
9
10#include <AK/DeprecatedString.h>
11#include <AK/Forward.h>
12#include <AK/HashMap.h>
13#include <AK/IntrusiveList.h>
14#include <AK/Noncopyable.h>
15#include <AK/OwnPtr.h>
16#include <AK/StringView.h>
17#include <AK/TypeCasts.h>
18#include <AK/Weakable.h>
19#include <LibCore/Forward.h>
20#include <LibCore/Property.h>
21
22namespace Core {
23
24#define REGISTER_ABSTRACT_CORE_OBJECT(namespace_, class_name) \
25 namespace Core { \
26 namespace Registration { \
27 Core::ObjectClassRegistration registration_##class_name(#namespace_ "::" #class_name##sv, []() { return Error::from_string_literal("Attempted to construct an abstract object."); }); \
28 } \
29 }
30
31#define REGISTER_CORE_OBJECT(namespace_, class_name) \
32 namespace Core { \
33 namespace Registration { \
34 Core::ObjectClassRegistration registration_##class_name(#namespace_ "::" #class_name##sv, []() { return namespace_::class_name::try_create(); }); \
35 } \
36 }
37
38class ObjectClassRegistration {
39 AK_MAKE_NONCOPYABLE(ObjectClassRegistration);
40 AK_MAKE_NONMOVABLE(ObjectClassRegistration);
41
42public:
43 ObjectClassRegistration(StringView class_name, Function<ErrorOr<NonnullRefPtr<Object>>()> factory, ObjectClassRegistration* parent_class = nullptr);
44 ~ObjectClassRegistration() = default;
45
46 StringView class_name() const { return m_class_name; }
47 ObjectClassRegistration const* parent_class() const { return m_parent_class; }
48 ErrorOr<NonnullRefPtr<Object>> construct() const { return m_factory(); }
49 bool is_derived_from(ObjectClassRegistration const& base_class) const;
50
51 static void for_each(Function<void(ObjectClassRegistration const&)>);
52 static ObjectClassRegistration const* find(StringView class_name);
53
54private:
55 StringView m_class_name;
56 Function<ErrorOr<NonnullRefPtr<Object>>()> m_factory;
57 ObjectClassRegistration* m_parent_class { nullptr };
58};
59
60class InspectorServerConnection;
61
62enum class TimerShouldFireWhenNotVisible {
63 No = 0,
64 Yes
65};
66
67#define C_OBJECT(klass) \
68public: \
69 virtual StringView class_name() const override \
70 { \
71 return #klass##sv; \
72 } \
73 template<typename Klass = klass, class... Args> \
74 static NonnullRefPtr<klass> construct(Args&&... args) \
75 { \
76 return adopt_ref(*new Klass(::forward<Args>(args)...)); \
77 } \
78 template<typename Klass = klass, class... Args> \
79 static ErrorOr<NonnullRefPtr<klass>> try_create(Args&&... args) \
80 { \
81 return adopt_nonnull_ref_or_enomem(new (nothrow) Klass(::forward<Args>(args)...)); \
82 }
83
84#define C_OBJECT_ABSTRACT(klass) \
85public: \
86 virtual StringView class_name() const override \
87 { \
88 return #klass##sv; \
89 }
90
91class Object
92 : public RefCounted<Object>
93 , public Weakable<Object> {
94 // NOTE: No C_OBJECT macro for Core::Object itself.
95
96 AK_MAKE_NONCOPYABLE(Object);
97 AK_MAKE_NONMOVABLE(Object);
98
99 IntrusiveListNode<Object> m_all_objects_list_node;
100
101public:
102 virtual ~Object();
103
104 virtual StringView class_name() const = 0;
105
106 template<typename T>
107 bool fast_is() const = delete;
108
109 virtual bool is_widget() const { return false; }
110
111 DeprecatedString const& name() const { return m_name; }
112 void set_name(DeprecatedString name) { m_name = move(name); }
113
114 Vector<NonnullRefPtr<Object>>& children() { return m_children; }
115 Vector<NonnullRefPtr<Object>> const& children() const { return m_children; }
116
117 template<typename Callback>
118 void for_each_child(Callback callback)
119 {
120 for (auto& child : m_children) {
121 if (callback(*child) == IterationDecision::Break)
122 return;
123 }
124 }
125
126 template<typename T, typename Callback>
127 void for_each_child_of_type(Callback callback)
128 requires IsBaseOf<Object, T>;
129
130 template<typename T>
131 T* find_child_of_type_named(DeprecatedString const&)
132 requires IsBaseOf<Object, T>;
133
134 template<typename T>
135 T* find_descendant_of_type_named(DeprecatedString const&)
136 requires IsBaseOf<Object, T>;
137
138 bool is_ancestor_of(Object const&) const;
139
140 Object* parent() { return m_parent; }
141 Object const* parent() const { return m_parent; }
142
143 void start_timer(int ms, TimerShouldFireWhenNotVisible = TimerShouldFireWhenNotVisible::No);
144 void stop_timer();
145 bool has_timer() const { return m_timer_id; }
146
147 ErrorOr<void> try_add_child(Object&);
148
149 void add_child(Object&);
150 void insert_child_before(Object& new_child, Object& before_child);
151 void remove_child(Object&);
152 void remove_all_children();
153
154 void set_event_filter(Function<bool(Core::Event&)>);
155
156 void dump_tree(int indent = 0);
157
158 void deferred_invoke(Function<void()>);
159
160 void save_to(JsonObject&);
161
162 bool set_property(DeprecatedString const& name, JsonValue const& value);
163 JsonValue property(DeprecatedString const& name) const;
164 HashMap<DeprecatedString, NonnullOwnPtr<Property>> const& properties() const { return m_properties; }
165
166 static IntrusiveList<&Object::m_all_objects_list_node>& all_objects();
167
168 void dispatch_event(Core::Event&, Object* stay_within = nullptr);
169
170 void remove_from_parent()
171 {
172 if (m_parent)
173 m_parent->remove_child(*this);
174 }
175
176 template<class T, class... Args>
177 inline T& add(Args&&... args)
178 {
179 auto child = T::construct(forward<Args>(args)...);
180 add_child(*child);
181 return child;
182 }
183
184 template<class T, class... Args>
185 inline ErrorOr<NonnullRefPtr<T>> try_add(Args&&... args)
186 {
187 auto child = TRY(T::try_create(forward<Args>(args)...));
188 TRY(try_add_child(*child));
189 return child;
190 }
191
192 virtual bool is_visible_for_timer_purposes() const;
193
194 bool is_being_inspected() const { return m_inspector_count; }
195
196 void increment_inspector_count(Badge<InspectorServerConnection>);
197 void decrement_inspector_count(Badge<InspectorServerConnection>);
198
199protected:
200 explicit Object(Object* parent = nullptr);
201
202 void register_property(DeprecatedString const& name, Function<JsonValue()> getter, Function<bool(JsonValue const&)> setter = nullptr);
203
204 virtual void event(Core::Event&);
205
206 virtual void timer_event(TimerEvent&);
207 virtual void custom_event(CustomEvent&);
208
209 // NOTE: You may get child events for children that are not yet fully constructed!
210 virtual void child_event(ChildEvent&);
211
212 virtual void did_begin_inspection() { }
213 virtual void did_end_inspection() { }
214
215private:
216 Object* m_parent { nullptr };
217 DeprecatedString m_name;
218 int m_timer_id { 0 };
219 unsigned m_inspector_count { 0 };
220 HashMap<DeprecatedString, NonnullOwnPtr<Property>> m_properties;
221 Vector<NonnullRefPtr<Object>> m_children;
222 Function<bool(Core::Event&)> m_event_filter;
223};
224
225}
226
227template<>
228struct AK::Formatter<Core::Object> : AK::Formatter<FormatString> {
229 ErrorOr<void> format(FormatBuilder& builder, Core::Object const& value)
230 {
231 return AK::Formatter<FormatString>::format(builder, "{}({})"sv, value.class_name(), &value);
232 }
233};
234
235namespace Core {
236template<typename T, typename Callback>
237inline void Object::for_each_child_of_type(Callback callback)
238requires IsBaseOf<Object, T>
239{
240 for_each_child([&](auto& child) {
241 if (is<T>(child))
242 return callback(static_cast<T&>(child));
243 return IterationDecision::Continue;
244 });
245}
246
247template<typename T>
248T* Object::find_child_of_type_named(DeprecatedString const& name)
249requires IsBaseOf<Object, T>
250{
251 T* found_child = nullptr;
252 for_each_child_of_type<T>([&](auto& child) {
253 if (child.name() == name) {
254 found_child = &child;
255 return IterationDecision::Break;
256 }
257 return IterationDecision::Continue;
258 });
259
260 return found_child;
261}
262
263template<typename T>
264T* Object::find_descendant_of_type_named(DeprecatedString const& name)
265requires IsBaseOf<Object, T>
266{
267 if (is<T>(*this) && this->name() == name) {
268 return static_cast<T*>(this);
269 }
270 T* found_child = nullptr;
271 for_each_child([&](auto& child) {
272 found_child = child.template find_descendant_of_type_named<T>(name);
273 if (found_child)
274 return IterationDecision::Break;
275 return IterationDecision::Continue;
276 });
277 return found_child;
278}
279
280#define REGISTER_INT_PROPERTY(property_name, getter, setter) \
281 register_property( \
282 property_name, \
283 [this] { return this->getter(); }, \
284 [this](auto& value) { \
285 this->setter(value.template to_number<int>()); \
286 return true; \
287 });
288
289#define REGISTER_BOOL_PROPERTY(property_name, getter, setter) \
290 register_property( \
291 property_name, \
292 [this] { return this->getter(); }, \
293 [this](auto& value) { \
294 this->setter(value.to_bool()); \
295 return true; \
296 });
297
298#define REGISTER_STRING_PROPERTY(property_name, getter, setter) \
299 register_property( \
300 property_name, \
301 [this] { return this->getter(); }, \
302 [this](auto& value) { \
303 this->setter(value.to_deprecated_string()); \
304 return true; \
305 });
306
307#define REGISTER_READONLY_STRING_PROPERTY(property_name, getter) \
308 register_property( \
309 property_name, \
310 [this] { return this->getter(); }, \
311 {});
312
313#define REGISTER_WRITE_ONLY_STRING_PROPERTY(property_name, setter) \
314 register_property( \
315 property_name, \
316 {}, \
317 [this](auto& value) { \
318 this->setter(value.to_deprecated_string()); \
319 return true; \
320 });
321
322#define REGISTER_READONLY_SIZE_PROPERTY(property_name, getter) \
323 register_property( \
324 property_name, \
325 [this] { \
326 auto size = this->getter(); \
327 JsonArray size_array; \
328 size_array.append(size.width()); \
329 size_array.append(size.height()); \
330 return size_array; \
331 }, \
332 {});
333
334#define REGISTER_RECT_PROPERTY(property_name, getter, setter) \
335 register_property( \
336 property_name, \
337 [this] { \
338 auto rect = this->getter(); \
339 JsonObject rect_object; \
340 rect_object.set("x"sv, rect.x()); \
341 rect_object.set("y"sv, rect.y()); \
342 rect_object.set("width"sv, rect.width()); \
343 rect_object.set("height"sv, rect.height()); \
344 return rect_object; \
345 }, \
346 [this](auto& value) { \
347 Gfx::IntRect rect; \
348 if (value.is_object()) { \
349 rect.set_x(value.as_object().get_i32("x"sv).value_or(0)); \
350 rect.set_y(value.as_object().get_i32("y"sv).value_or(0)); \
351 rect.set_width(value.as_object().get_i32("width"sv).value_or(0)); \
352 rect.set_height(value.as_object().get_i32("height"sv).value_or(0)); \
353 } else if (value.is_array() && value.as_array().size() == 4) { \
354 rect.set_x(value.as_array()[0].to_i32()); \
355 rect.set_y(value.as_array()[1].to_i32()); \
356 rect.set_width(value.as_array()[2].to_i32()); \
357 rect.set_height(value.as_array()[3].to_i32()); \
358 } else { \
359 return false; \
360 } \
361 setter(rect); \
362 \
363 return true; \
364 });
365
366#define REGISTER_SIZE_PROPERTY(property_name, getter, setter) \
367 register_property( \
368 property_name, \
369 [this] { \
370 auto size = this->getter(); \
371 JsonArray size_array; \
372 size_array.append(size.width()); \
373 size_array.append(size.height()); \
374 return size_array; \
375 }, \
376 [this](auto& value) { \
377 if (!value.is_array()) \
378 return false; \
379 Gfx::IntSize size; \
380 size.set_width(value.as_array()[0].to_i32()); \
381 size.set_height(value.as_array()[1].to_i32()); \
382 setter(size); \
383 return true; \
384 });
385
386#define REGISTER_ENUM_PROPERTY(property_name, getter, setter, EnumType, ...) \
387 register_property( \
388 property_name, \
389 [this]() -> JsonValue { \
390 struct { \
391 EnumType enum_value; \
392 DeprecatedString string_value; \
393 } options[] = { __VA_ARGS__ }; \
394 auto enum_value = getter(); \
395 for (size_t i = 0; i < array_size(options); ++i) { \
396 auto& option = options[i]; \
397 if (enum_value == option.enum_value) \
398 return option.string_value; \
399 } \
400 return JsonValue(); \
401 }, \
402 [this](auto& value) { \
403 struct { \
404 EnumType enum_value; \
405 DeprecatedString string_value; \
406 } options[] = { __VA_ARGS__ }; \
407 if (!value.is_string()) \
408 return false; \
409 auto string_value = value.as_string(); \
410 for (size_t i = 0; i < array_size(options); ++i) { \
411 auto& option = options[i]; \
412 if (string_value == option.string_value) { \
413 setter(option.enum_value); \
414 return true; \
415 } \
416 } \
417 return false; \
418 })
419
420#define REGISTER_TEXT_ALIGNMENT_PROPERTY(property_name, getter, setter) \
421 REGISTER_ENUM_PROPERTY( \
422 property_name, getter, setter, Gfx::TextAlignment, \
423 { Gfx::TextAlignment::Center, "Center" }, \
424 { Gfx::TextAlignment::CenterLeft, "CenterLeft" }, \
425 { Gfx::TextAlignment::CenterRight, "CenterRight" }, \
426 { Gfx::TextAlignment::TopCenter, "TopCenter" }, \
427 { Gfx::TextAlignment::TopLeft, "TopLeft" }, \
428 { Gfx::TextAlignment::TopRight, "TopRight" }, \
429 { Gfx::TextAlignment::BottomCenter, "BottomCenter" }, \
430 { Gfx::TextAlignment::BottomLeft, "BottomLeft" }, \
431 { Gfx::TextAlignment::BottomRight, "BottomRight" })
432
433#define REGISTER_FONT_WEIGHT_PROPERTY(property_name, getter, setter) \
434 REGISTER_ENUM_PROPERTY( \
435 property_name, getter, setter, unsigned, \
436 { Gfx::FontWeight::Thin, "Thin" }, \
437 { Gfx::FontWeight::ExtraLight, "ExtraLight" }, \
438 { Gfx::FontWeight::Light, "Light" }, \
439 { Gfx::FontWeight::Regular, "Regular" }, \
440 { Gfx::FontWeight::Medium, "Medium" }, \
441 { Gfx::FontWeight::SemiBold, "SemiBold" }, \
442 { Gfx::FontWeight::Bold, "Bold" }, \
443 { Gfx::FontWeight::ExtraBold, "ExtraBold" }, \
444 { Gfx::FontWeight::Black, "Black" }, \
445 { Gfx::FontWeight::ExtraBlack, "ExtraBlack" })
446
447#define REGISTER_TEXT_WRAPPING_PROPERTY(property_name, getter, setter) \
448 REGISTER_ENUM_PROPERTY( \
449 property_name, getter, setter, Gfx::TextWrapping, \
450 { Gfx::TextWrapping::Wrap, "Wrap" }, \
451 { Gfx::TextWrapping::DontWrap, "DontWrap" })
452}