Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/Badge.h>
9#include <AK/JsonObject.h>
10#include <LibGUI/Layout.h>
11#include <LibGUI/Widget.h>
12
13REGISTER_ABSTRACT_CORE_OBJECT(GUI, Layout)
14
15namespace GUI {
16
17Layout::Layout(Margins initial_margins, int spacing)
18 : m_margins(initial_margins)
19 , m_spacing(spacing)
20{
21 REGISTER_INT_PROPERTY("spacing", spacing, set_spacing);
22 REGISTER_MARGINS_PROPERTY("margins", margins, set_margins);
23
24 register_property("entries",
25 [this] {
26 JsonArray entries_array;
27 for (auto& entry : m_entries) {
28 JsonObject entry_object;
29 if (entry.type == Entry::Type::Widget) {
30 entry_object.set("type", "Widget");
31 entry_object.set("widget", (FlatPtr)entry.widget.ptr());
32 } else if (entry.type == Entry::Type::Spacer) {
33 entry_object.set("type", "Spacer");
34 } else {
35 VERIFY_NOT_REACHED();
36 }
37 entries_array.append(move(entry_object));
38 }
39 return entries_array;
40 });
41}
42
43Layout::~Layout() = default;
44
45void Layout::notify_adopted(Badge<Widget>, Widget& widget)
46{
47 if (m_owner == &widget)
48 return;
49 m_owner = widget;
50 m_owner->for_each_child_widget([&](Widget& child) {
51 add_widget(child);
52 return IterationDecision::Continue;
53 });
54}
55
56void Layout::notify_disowned(Badge<Widget>, Widget& widget)
57{
58 VERIFY(m_owner == &widget);
59 m_owner.clear();
60 m_entries.clear();
61}
62
63ErrorOr<void> Layout::try_add_entry(Entry&& entry)
64{
65 TRY(m_entries.try_append(move(entry)));
66 if (m_owner)
67 m_owner->notify_layout_changed({});
68 return {};
69}
70
71void Layout::add_entry(Entry&& entry)
72{
73 MUST(try_add_entry(move(entry)));
74}
75
76ErrorOr<void> Layout::try_add_spacer()
77{
78 TRY(try_add_entry(Entry { .type = Entry::Type::Spacer }));
79 return {};
80}
81
82void Layout::add_spacer()
83{
84 MUST(try_add_spacer());
85}
86
87void Layout::add_layout(OwnPtr<Layout>&& layout)
88{
89 Entry entry;
90 entry.type = Entry::Type::Layout;
91 entry.layout = move(layout);
92 add_entry(move(entry));
93}
94
95ErrorOr<void> Layout::try_add_widget(Widget& widget)
96{
97 TRY(try_add_entry(Entry {
98 .type = Entry::Type::Widget,
99 .widget = widget,
100 }));
101 return {};
102}
103
104void Layout::add_widget(Widget& widget)
105{
106 MUST(try_add_widget(widget));
107}
108
109ErrorOr<void> Layout::try_insert_widget_before(Widget& widget, Widget& before_widget)
110{
111 Entry entry;
112 entry.type = Entry::Type::Widget;
113 entry.widget = widget;
114 TRY(m_entries.try_insert_before_matching(move(entry), [&](auto& existing_entry) {
115 return existing_entry.type == Entry::Type::Widget && existing_entry.widget.ptr() == &before_widget;
116 }));
117 if (m_owner)
118 m_owner->notify_layout_changed({});
119 return {};
120}
121
122void Layout::insert_widget_before(Widget& widget, Widget& before_widget)
123{
124 MUST(try_insert_widget_before(widget, before_widget));
125}
126
127void Layout::remove_widget(Widget& widget)
128{
129 m_entries.remove_first_matching([&](auto& entry) {
130 return entry.widget == &widget;
131 });
132 if (m_owner)
133 m_owner->notify_layout_changed({});
134}
135
136void Layout::set_spacing(int spacing)
137{
138 if (m_spacing == spacing)
139 return;
140 m_spacing = spacing;
141 if (m_owner)
142 m_owner->notify_layout_changed({});
143}
144
145void Layout::set_margins(Margins const& margins)
146{
147 if (m_margins == margins)
148 return;
149 m_margins = margins;
150 if (m_owner)
151 m_owner->notify_layout_changed({});
152}
153
154}