Serenity Operating System
1/*
2 * Copyright (c) 2018-2023, 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/Assertions.h>
9#include <AK/Debug.h>
10#include <AK/IterationDecision.h>
11#include <AK/JsonObject.h>
12#include <AK/NonnullRefPtr.h>
13#include <AK/RefPtr.h>
14#include <LibGUI/Action.h>
15#include <LibGUI/Application.h>
16#include <LibGUI/BoxLayout.h>
17#include <LibGUI/ConnectionToWindowServer.h>
18#include <LibGUI/Event.h>
19#include <LibGUI/GML/AST.h>
20#include <LibGUI/GML/Parser.h>
21#include <LibGUI/Layout.h>
22#include <LibGUI/Menu.h>
23#include <LibGUI/Painter.h>
24#include <LibGUI/TabWidget.h>
25#include <LibGUI/Widget.h>
26#include <LibGUI/Window.h>
27#include <LibGfx/Bitmap.h>
28#include <LibGfx/Font/Font.h>
29#include <LibGfx/Font/FontDatabase.h>
30#include <LibGfx/Palette.h>
31#include <LibGfx/SystemTheme.h>
32#include <unistd.h>
33
34REGISTER_CORE_OBJECT(GUI, Widget)
35
36namespace GUI {
37
38Widget::Widget()
39 : Core::Object(nullptr)
40 , m_background_role(Gfx::ColorRole::Window)
41 , m_foreground_role(Gfx::ColorRole::WindowText)
42 , m_font(Gfx::FontDatabase::default_font())
43 , m_palette(Application::the()->palette().impl())
44{
45 REGISTER_RECT_PROPERTY("relative_rect", relative_rect, set_relative_rect);
46 REGISTER_BOOL_PROPERTY("fill_with_background_color", fill_with_background_color, set_fill_with_background_color);
47 REGISTER_BOOL_PROPERTY("visible", is_visible, set_visible);
48 REGISTER_BOOL_PROPERTY("focused", is_focused, set_focus);
49 REGISTER_BOOL_PROPERTY("enabled", is_enabled, set_enabled);
50 REGISTER_STRING_PROPERTY("tooltip", tooltip, set_tooltip);
51
52 REGISTER_UI_SIZE_PROPERTY("min_size", min_size, set_min_size);
53 REGISTER_READONLY_UI_SIZE_PROPERTY("effective_min_size", effective_min_size);
54 REGISTER_UI_SIZE_PROPERTY("max_size", max_size, set_max_size);
55 REGISTER_UI_SIZE_PROPERTY("preferred_size", preferred_size, set_preferred_size);
56 REGISTER_READONLY_UI_SIZE_PROPERTY("effective_preferred_size", effective_preferred_size);
57 REGISTER_INT_PROPERTY("width", width, set_width);
58 REGISTER_UI_DIMENSION_PROPERTY("min_width", min_width, set_min_width);
59 REGISTER_UI_DIMENSION_PROPERTY("max_width", max_width, set_max_width);
60 REGISTER_UI_DIMENSION_PROPERTY("preferred_width", preferred_width, set_preferred_width);
61 REGISTER_INT_PROPERTY("height", height, set_height);
62 REGISTER_UI_DIMENSION_PROPERTY("min_height", min_height, set_min_height);
63 REGISTER_UI_DIMENSION_PROPERTY("max_height", max_height, set_max_height);
64 REGISTER_UI_DIMENSION_PROPERTY("preferred_height", preferred_height, set_preferred_height);
65
66 REGISTER_INT_PROPERTY("fixed_width", dummy_fixed_width, set_fixed_width);
67 REGISTER_INT_PROPERTY("fixed_height", dummy_fixed_height, set_fixed_height);
68 REGISTER_SIZE_PROPERTY("fixed_size", dummy_fixed_size, set_fixed_size);
69
70 REGISTER_BOOL_PROPERTY("shrink_to_fit", is_shrink_to_fit, set_shrink_to_fit);
71
72 REGISTER_INT_PROPERTY("x", x, set_x);
73 REGISTER_INT_PROPERTY("y", y, set_y);
74
75 REGISTER_STRING_PROPERTY("font", m_font->family, set_font_family);
76 REGISTER_INT_PROPERTY("font_size", m_font->presentation_size, set_font_size);
77 REGISTER_FONT_WEIGHT_PROPERTY("font_weight", m_font->weight, set_font_weight);
78
79 REGISTER_STRING_PROPERTY("title", title, set_title);
80
81 register_property(
82 "font_type", [this] { return m_font->is_fixed_width() ? "FixedWidth" : "Normal"; },
83 [this](auto& value) {
84 if (value.to_deprecated_string() == "FixedWidth") {
85 set_font_fixed_width(true);
86 return true;
87 }
88 if (value.to_deprecated_string() == "Normal") {
89 set_font_fixed_width(false);
90 return true;
91 }
92 return false;
93 });
94
95 register_property(
96 "focus_policy", [this]() -> JsonValue {
97 auto policy = focus_policy();
98 if (policy == GUI::FocusPolicy::ClickFocus)
99 return "ClickFocus";
100 if (policy == GUI::FocusPolicy::NoFocus)
101 return "NoFocus";
102 if (policy == GUI::FocusPolicy::TabFocus)
103 return "TabFocus";
104 if (policy == GUI::FocusPolicy::StrongFocus)
105 return "StrongFocus";
106 return JsonValue(); },
107 [this](auto& value) {
108 if (!value.is_string())
109 return false;
110 if (value.as_string() == "ClickFocus") {
111 set_focus_policy(GUI::FocusPolicy::ClickFocus);
112 return true;
113 }
114 if (value.as_string() == "NoFocus") {
115 set_focus_policy(GUI::FocusPolicy::NoFocus);
116 return true;
117 }
118 if (value.as_string() == "TabFocus") {
119 set_focus_policy(GUI::FocusPolicy::TabFocus);
120 return true;
121 }
122 if (value.as_string() == "StrongFocus") {
123 set_focus_policy(GUI::FocusPolicy::StrongFocus);
124 return true;
125 }
126 return false;
127 });
128
129 register_property(
130 "foreground_color", [this]() -> JsonValue { return palette().color(foreground_role()).to_deprecated_string(); },
131 [this](auto& value) {
132 auto c = Color::from_string(value.to_deprecated_string());
133 if (c.has_value()) {
134 auto _palette = palette();
135 _palette.set_color(foreground_role(), c.value());
136 set_palette(_palette);
137 return true;
138 }
139 return false;
140 });
141
142 register_property(
143 "background_color", [this]() -> JsonValue { return palette().color(background_role()).to_deprecated_string(); },
144 [this](auto& value) {
145 auto c = Color::from_string(value.to_deprecated_string());
146 if (c.has_value()) {
147 auto _palette = palette();
148 _palette.set_color(background_role(), c.value());
149 set_palette(_palette);
150 return true;
151 }
152 return false;
153 });
154
155 register_property(
156 "foreground_role", [this]() -> JsonValue { return Gfx::to_string(foreground_role()); },
157 [this](auto& value) {
158 if (!value.is_string())
159 return false;
160 auto str = value.as_string();
161 if (str == "NoRole") {
162 set_foreground_role(Gfx::ColorRole::NoRole);
163 return true;
164 }
165#undef __ENUMERATE_COLOR_ROLE
166#define __ENUMERATE_COLOR_ROLE(role) \
167 else if (str == #role) \
168 { \
169 set_foreground_role(Gfx::ColorRole::role); \
170 return true; \
171 }
172 ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE)
173#undef __ENUMERATE_COLOR_ROLE
174 return false;
175 });
176
177 register_property(
178 "background_role", [this]() -> JsonValue { return Gfx::to_string(background_role()); },
179 [this](auto& value) {
180 if (!value.is_string())
181 return false;
182 auto str = value.as_string();
183 if (str == "NoRole") {
184 set_background_role(Gfx::ColorRole::NoRole);
185 return true;
186 }
187#undef __ENUMERATE_COLOR_ROLE
188#define __ENUMERATE_COLOR_ROLE(role) \
189 else if (str == #role) \
190 { \
191 set_background_role(Gfx::ColorRole::role); \
192 return true; \
193 }
194 ENUMERATE_COLOR_ROLES(__ENUMERATE_COLOR_ROLE)
195#undef __ENUMERATE_COLOR_ROLE
196 return false;
197 });
198}
199
200Widget::~Widget() = default;
201
202void Widget::layout_relevant_change_occurred()
203{
204 if (auto* parent = parent_widget())
205 parent->layout_relevant_change_occurred();
206 else if (window())
207 window()->schedule_relayout();
208}
209
210void Widget::child_event(Core::ChildEvent& event)
211{
212 if (event.type() == Event::ChildAdded) {
213 if (event.child() && is<Widget>(*event.child()) && layout()) {
214 if (event.insertion_before_child() && is<Widget>(event.insertion_before_child()))
215 layout()->insert_widget_before(verify_cast<Widget>(*event.child()), verify_cast<Widget>(*event.insertion_before_child()));
216 else
217 layout()->add_widget(verify_cast<Widget>(*event.child()));
218 layout_relevant_change_occurred();
219 }
220 if (window() && event.child() && is<Widget>(*event.child()))
221 window()->did_add_widget({}, verify_cast<Widget>(*event.child()));
222
223 if (event.child() && is<Widget>(*event.child()) && static_cast<Widget const&>(*event.child()).is_visible()) {
224 ShowEvent show_event;
225 event.child()->dispatch_event(show_event);
226 }
227 }
228 if (event.type() == Event::ChildRemoved) {
229 if (layout()) {
230 if (event.child() && is<Widget>(*event.child()))
231 layout()->remove_widget(verify_cast<Widget>(*event.child()));
232 layout_relevant_change_occurred();
233 }
234 if (window() && event.child() && is<Widget>(*event.child()))
235 window()->did_remove_widget({}, verify_cast<Widget>(*event.child()));
236 if (event.child() && is<Widget>(*event.child())) {
237 HideEvent hide_event;
238 event.child()->dispatch_event(hide_event);
239 }
240 update();
241 }
242 return Core::Object::child_event(event);
243}
244
245void Widget::set_relative_rect(Gfx::IntRect const& a_rect)
246{
247 // Get rid of negative width/height values.
248 Gfx::IntRect rect = {
249 a_rect.x(),
250 a_rect.y(),
251 max(a_rect.width(), 0),
252 max(a_rect.height(), 0)
253 };
254
255 if (rect == m_relative_rect)
256 return;
257
258 auto old_rect = m_relative_rect;
259
260 bool size_changed = m_relative_rect.size() != rect.size();
261 m_relative_rect = rect;
262
263 if (size_changed) {
264 ResizeEvent resize_event(rect.size());
265 event(resize_event);
266 }
267
268 if (auto* parent = parent_widget())
269 parent->update(old_rect);
270 update();
271}
272
273void Widget::event(Core::Event& event)
274{
275 if (!is_enabled()) {
276 switch (event.type()) {
277 case Event::MouseUp:
278 case Event::MouseDown:
279 case Event::MouseMove:
280 case Event::MouseWheel:
281 case Event::MouseDoubleClick:
282 case Event::KeyUp:
283 case Event::KeyDown:
284 return;
285 default:
286 break;
287 }
288 }
289
290 switch (event.type()) {
291 case Event::Paint:
292 return handle_paint_event(static_cast<PaintEvent&>(event));
293 case Event::Resize:
294 return handle_resize_event(static_cast<ResizeEvent&>(event));
295 case Event::FocusIn:
296 return focusin_event(static_cast<FocusEvent&>(event));
297 case Event::FocusOut:
298 return focusout_event(static_cast<FocusEvent&>(event));
299 case Event::Show:
300 return show_event(static_cast<ShowEvent&>(event));
301 case Event::Hide:
302 return hide_event(static_cast<HideEvent&>(event));
303 case Event::KeyDown:
304 return handle_keydown_event(static_cast<KeyEvent&>(event));
305 case Event::KeyUp:
306 return keyup_event(static_cast<KeyEvent&>(event));
307 case Event::MouseMove:
308 return mousemove_event(static_cast<MouseEvent&>(event));
309 case Event::MouseDown:
310 return handle_mousedown_event(static_cast<MouseEvent&>(event));
311 case Event::MouseDoubleClick:
312 return handle_mousedoubleclick_event(static_cast<MouseEvent&>(event));
313 case Event::MouseUp:
314 return handle_mouseup_event(static_cast<MouseEvent&>(event));
315 case Event::MouseWheel:
316 return mousewheel_event(static_cast<MouseEvent&>(event));
317 case Event::DragEnter:
318 return drag_enter_event(static_cast<DragEvent&>(event));
319 case Event::DragMove:
320 return drag_move_event(static_cast<DragEvent&>(event));
321 case Event::DragLeave:
322 return drag_leave_event(static_cast<Event&>(event));
323 case Event::Drop:
324 return drop_event(static_cast<DropEvent&>(event));
325 case Event::ThemeChange:
326 return theme_change_event(static_cast<ThemeChangeEvent&>(event));
327 case Event::FontsChange:
328 return fonts_change_event(static_cast<FontsChangeEvent&>(event));
329 case Event::Enter:
330 return handle_enter_event(event);
331 case Event::Leave:
332 return handle_leave_event(event);
333 case Event::EnabledChange:
334 return change_event(static_cast<Event&>(event));
335 case Event::ContextMenu:
336 return context_menu_event(static_cast<ContextMenuEvent&>(event));
337 case Event::AppletAreaRectChange:
338 return applet_area_rect_change_event(static_cast<AppletAreaRectChangeEvent&>(event));
339 default:
340 return Core::Object::event(event);
341 }
342}
343
344void Widget::handle_keydown_event(KeyEvent& event)
345{
346 keydown_event(event);
347 if (event.is_accepted())
348 return;
349
350 if (auto action = Action::find_action_for_shortcut(*this, Shortcut(event.modifiers(), event.key()))) {
351 action->process_event(*window(), event);
352 if (event.is_accepted())
353 return;
354 }
355
356 if (event.key() == KeyCode::Key_Menu) {
357 ContextMenuEvent c_event(window_relative_rect().bottom_right(), screen_relative_rect().bottom_right());
358 dispatch_event(c_event);
359 return;
360 }
361
362 event.ignore();
363}
364
365void Widget::handle_paint_event(PaintEvent& event)
366{
367 VERIFY(is_visible());
368
369 if (!rect().intersects(event.rect())) {
370 // This widget is not inside the paint event rect.
371 // Since widgets fully contain their children, we don't need to recurse further.
372 return;
373 }
374
375 if (fill_with_background_color()) {
376 Painter painter(*this);
377 painter.fill_rect(event.rect(), palette().color(background_role()));
378 }
379 paint_event(event);
380 auto children_clip_rect = this->children_clip_rect();
381 for_each_child_widget([&](auto& child) {
382 if (!child.is_visible())
383 return IterationDecision::Continue;
384 if (child.relative_rect().intersects(event.rect())) {
385 PaintEvent local_event(event.rect().intersected(children_clip_rect).intersected(child.relative_rect()).translated(-child.relative_position()));
386 child.dispatch_event(local_event, this);
387 }
388 return IterationDecision::Continue;
389 });
390 second_paint_event(event);
391
392 auto* app = Application::the();
393
394 if (app && app->dnd_debugging_enabled() && has_pending_drop()) {
395 Painter painter(*this);
396 painter.draw_rect(rect(), Color::Blue);
397 }
398
399 if (app && app->focus_debugging_enabled() && is_focused()) {
400 Painter painter(*this);
401 painter.draw_rect(rect(), Color::Cyan);
402 }
403
404 if (app && app->hover_debugging_enabled() && this == window()->hovered_widget()) {
405 Painter painter(*this);
406 painter.draw_rect(rect(), Color::Red);
407 }
408
409 if (is_being_inspected()) {
410 Painter painter(*this);
411 painter.draw_rect(rect(), Color::Magenta);
412 }
413}
414
415void Widget::set_layout(NonnullRefPtr<Layout> layout)
416{
417 if (m_layout) {
418 m_layout->notify_disowned({}, *this);
419 m_layout->remove_from_parent();
420 }
421 m_layout = move(layout);
422 if (m_layout) {
423 add_child(*m_layout);
424 m_layout->notify_adopted({}, *this);
425 do_layout();
426 } else {
427 update();
428 }
429 layout_relevant_change_occurred();
430}
431
432void Widget::do_layout()
433{
434 for_each_child_widget([&](auto& child) {
435 child.do_layout();
436 return IterationDecision::Continue;
437 });
438 custom_layout();
439 if (!m_layout)
440 return;
441 m_layout->run(*this);
442 did_layout();
443 update();
444}
445
446void Widget::notify_layout_changed(Badge<Layout>)
447{
448 invalidate_layout();
449}
450
451void Widget::handle_resize_event(ResizeEvent& event)
452{
453 resize_event(event);
454 do_layout();
455}
456
457void Widget::handle_mouseup_event(MouseEvent& event)
458{
459 mouseup_event(event);
460}
461
462void Widget::handle_mousedown_event(MouseEvent& event)
463{
464 if (has_flag(focus_policy(), FocusPolicy::ClickFocus))
465 set_focus(true, FocusSource::Mouse);
466 mousedown_event(event);
467 if (event.button() == MouseButton::Secondary) {
468 ContextMenuEvent c_event(event.position(), screen_relative_rect().location().translated(event.position()));
469 dispatch_event(c_event);
470 }
471}
472
473void Widget::handle_mousedoubleclick_event(MouseEvent& event)
474{
475 doubleclick_event(event);
476}
477
478void Widget::handle_enter_event(Core::Event& event)
479{
480 if (auto* window = this->window())
481 window->update_cursor({});
482 show_or_hide_tooltip();
483 enter_event(event);
484}
485
486void Widget::handle_leave_event(Core::Event& event)
487{
488 if (auto* window = this->window())
489 window->update_cursor({});
490 if (Application::the()->tooltip_source_widget() == this)
491 Application::the()->hide_tooltip();
492 leave_event(event);
493}
494
495void Widget::doubleclick_event(MouseEvent&)
496{
497}
498
499void Widget::resize_event(ResizeEvent&)
500{
501}
502
503void Widget::paint_event(PaintEvent&)
504{
505}
506
507void Widget::second_paint_event(PaintEvent&)
508{
509}
510
511void Widget::show_event(ShowEvent&)
512{
513}
514
515void Widget::hide_event(HideEvent&)
516{
517}
518
519void Widget::keydown_event(KeyEvent& event)
520{
521 if (!event.alt() && !event.ctrl() && !event.super()) {
522 if (event.key() == KeyCode::Key_Tab) {
523 if (event.shift())
524 focus_previous_widget(FocusSource::Keyboard, false);
525 else
526 focus_next_widget(FocusSource::Keyboard, false);
527 event.accept();
528 return;
529 }
530 if (!event.shift() && (event.key() == KeyCode::Key_Left || event.key() == KeyCode::Key_Up)) {
531 focus_previous_widget(FocusSource::Keyboard, true);
532 event.accept();
533 return;
534 }
535 if (!event.shift() && (event.key() == KeyCode::Key_Right || event.key() == KeyCode::Key_Down)) {
536 focus_next_widget(FocusSource::Keyboard, true);
537 event.accept();
538 return;
539 }
540 }
541 event.ignore();
542}
543
544void Widget::keyup_event(KeyEvent& event)
545{
546 event.ignore();
547}
548
549void Widget::mousedown_event(MouseEvent&)
550{
551}
552
553void Widget::mouseup_event(MouseEvent&)
554{
555}
556
557void Widget::mousemove_event(MouseEvent&)
558{
559}
560
561void Widget::mousewheel_event(MouseEvent& event)
562{
563 event.ignore();
564}
565
566void Widget::context_menu_event(ContextMenuEvent& event)
567{
568 event.ignore();
569}
570
571void Widget::focusin_event(FocusEvent&)
572{
573}
574
575void Widget::focusout_event(FocusEvent&)
576{
577}
578
579void Widget::enter_event(Core::Event&)
580{
581}
582
583void Widget::leave_event(Core::Event&)
584{
585}
586
587void Widget::change_event(Event&)
588{
589}
590
591void Widget::drag_move_event(DragEvent&)
592{
593}
594
595void Widget::drag_enter_event(DragEvent& event)
596{
597 StringBuilder builder;
598 builder.join(',', event.mime_types());
599 dbgln_if(DRAG_DEBUG, "{} {:p} DRAG ENTER @ {}, {}", class_name(), this, event.position(), builder.string_view());
600}
601
602void Widget::drag_leave_event(Event&)
603{
604 dbgln_if(DRAG_DEBUG, "{} {:p} DRAG LEAVE", class_name(), this);
605}
606
607void Widget::drop_event(DropEvent& event)
608{
609 dbgln_if(DRAG_DEBUG, "{} {:p} DROP @ {}, '{}'", class_name(), this, event.position(), event.text());
610 event.ignore();
611}
612
613void Widget::theme_change_event(ThemeChangeEvent&)
614{
615}
616
617void Widget::fonts_change_event(FontsChangeEvent&)
618{
619 if (m_default_font)
620 set_font(nullptr);
621}
622
623void Widget::screen_rects_change_event(ScreenRectsChangeEvent&)
624{
625}
626
627void Widget::applet_area_rect_change_event(AppletAreaRectChangeEvent&)
628{
629}
630
631void Widget::update()
632{
633 if (rect().is_empty())
634 return;
635 update(rect());
636
637 for (auto& it : m_focus_delegators) {
638 if (!it.is_null() && !it->rect().is_empty())
639 it->update(it->rect());
640 }
641}
642
643void Widget::update(Gfx::IntRect const& rect)
644{
645 if (!is_visible())
646 return;
647
648 if (!updates_enabled())
649 return;
650
651 auto bound_by_widget = rect.intersected(this->rect());
652 if (bound_by_widget.is_empty())
653 return;
654
655 Window* window = m_window;
656 Widget* parent = parent_widget();
657 while (parent) {
658 if (!parent->updates_enabled())
659 return;
660 window = parent->m_window;
661 parent = parent->parent_widget();
662 }
663 if (window)
664 window->update(bound_by_widget.translated(window_relative_rect().location()));
665}
666
667void Widget::repaint()
668{
669 if (rect().is_empty())
670 return;
671 repaint(rect());
672}
673
674void Widget::repaint(Gfx::IntRect const& rect)
675{
676 auto* window = this->window();
677 if (!window)
678 return;
679 update(rect);
680 window->flush_pending_paints_immediately();
681}
682
683Gfx::IntRect Widget::window_relative_rect() const
684{
685 auto rect = relative_rect();
686 for (auto* parent = parent_widget(); parent; parent = parent->parent_widget()) {
687 rect.translate_by(parent->relative_position());
688 }
689 return rect;
690}
691
692Gfx::IntRect Widget::screen_relative_rect() const
693{
694 auto window_position = window()->window_type() == WindowType::Applet
695 ? window()->applet_rect_on_screen().location()
696 : window()->rect().location();
697 return window_relative_rect().translated(window_position);
698}
699
700Widget* Widget::child_at(Gfx::IntPoint point) const
701{
702 for (int i = children().size() - 1; i >= 0; --i) {
703 if (!is<Widget>(children()[i]))
704 continue;
705 auto& child = verify_cast<Widget>(*children()[i]);
706 if (!child.is_visible())
707 continue;
708 if (child.relative_non_grabbable_rect().contains(point))
709 return const_cast<Widget*>(&child);
710 }
711 return nullptr;
712}
713
714Widget::HitTestResult Widget::hit_test(Gfx::IntPoint position, ShouldRespectGreediness should_respect_greediness)
715{
716 if (should_respect_greediness == ShouldRespectGreediness::Yes && is_greedy_for_hits())
717 return { this, position };
718 if (auto* child = child_at(position))
719 return child->hit_test(position - child->relative_position());
720 return { this, position };
721}
722
723void Widget::set_window(Window* window)
724{
725 if (m_window == window)
726 return;
727 m_window = window;
728}
729
730void Widget::set_focus_proxy(Widget* proxy)
731{
732 if (m_focus_proxy == proxy)
733 return;
734 if (proxy)
735 proxy->add_focus_delegator(this);
736 else if (m_focus_proxy)
737 m_focus_proxy->remove_focus_delegator(this);
738 m_focus_proxy = proxy;
739}
740
741void Widget::add_focus_delegator(Widget* delegator)
742{
743 m_focus_delegators.remove_all_matching([&](auto& entry) {
744 return entry.is_null() || entry == delegator;
745 });
746 m_focus_delegators.append(delegator);
747}
748
749void Widget::remove_focus_delegator(Widget* delegator)
750{
751 m_focus_delegators.remove_first_matching([&](auto& entry) {
752 return entry == delegator;
753 });
754}
755
756FocusPolicy Widget::focus_policy() const
757{
758 if (m_focus_proxy)
759 return m_focus_proxy->focus_policy();
760 return m_focus_policy;
761}
762
763void Widget::set_focus_policy(FocusPolicy policy)
764{
765 if (m_focus_proxy)
766 return m_focus_proxy->set_focus_policy(policy);
767 m_focus_policy = policy;
768}
769
770bool Widget::is_focused() const
771{
772 if (m_focus_proxy)
773 return m_focus_proxy->is_focused();
774
775 auto* win = window();
776 if (!win)
777 return false;
778 if (win->is_focusable())
779 return win->focused_widget() == this;
780 return false;
781}
782
783void Widget::set_focus(bool focus, FocusSource source)
784{
785 if (m_focus_proxy)
786 return m_focus_proxy->set_focus(focus, source);
787
788 auto* win = window();
789 if (!win)
790 return;
791 if (focus) {
792 win->set_focused_widget(this, source);
793 } else {
794 if (win->focused_widget() == this)
795 win->set_focused_widget(nullptr, source);
796 }
797}
798
799void Widget::set_font(Gfx::Font const* font)
800{
801 if (m_font.ptr() == font)
802 return;
803
804 if (!font) {
805 m_font = Gfx::FontDatabase::default_font();
806 m_default_font = true;
807 } else {
808 m_font = *font;
809 m_default_font = false;
810 }
811
812 did_change_font();
813 update();
814}
815
816void Widget::set_font_family(DeprecatedString const& family)
817{
818 set_font(Gfx::FontDatabase::the().get(family, m_font->presentation_size(), m_font->weight(), m_font->width(), m_font->slope()));
819}
820
821void Widget::set_font_size(unsigned size)
822{
823 set_font(Gfx::FontDatabase::the().get(m_font->family(), size, m_font->weight(), m_font->width(), m_font->slope()));
824}
825
826void Widget::set_font_weight(unsigned weight)
827{
828 set_font(Gfx::FontDatabase::the().get(m_font->family(), m_font->presentation_size(), weight, m_font->width(), m_font->slope()));
829}
830
831void Widget::set_font_fixed_width(bool fixed_width)
832{
833 if (fixed_width)
834 set_font(Gfx::FontDatabase::the().get(Gfx::FontDatabase::the().default_fixed_width_font().family(), m_font->presentation_size(), m_font->weight(), m_font->width(), m_font->slope()));
835 else
836 set_font(Gfx::FontDatabase::the().get(Gfx::FontDatabase::the().default_font().family(), m_font->presentation_size(), m_font->weight(), m_font->width(), m_font->slope()));
837}
838
839void Widget::set_min_size(UISize const& size)
840{
841 VERIFY(size.width().is_one_of(SpecialDimension::Regular, SpecialDimension::Shrink));
842 if (m_min_size == size)
843 return;
844 m_min_size = size;
845 layout_relevant_change_occurred();
846}
847
848void Widget::set_max_size(UISize const& size)
849{
850 VERIFY(size.width().is_one_of(SpecialDimension::Regular, SpecialDimension::Grow));
851 if (m_max_size == size)
852 return;
853 m_max_size = size;
854 layout_relevant_change_occurred();
855}
856
857void Widget::set_preferred_size(UISize const& size)
858{
859 if (m_preferred_size == size)
860 return;
861 m_preferred_size = size;
862 layout_relevant_change_occurred();
863}
864
865Optional<UISize> Widget::calculated_preferred_size() const
866{
867 if (layout())
868 return { layout()->preferred_size() };
869 return {};
870}
871
872Optional<UISize> Widget::calculated_min_size() const
873{
874 if (layout())
875 return { layout()->min_size() };
876 // Fall back to at least displaying the margins, so the Widget is not 0 size.
877 auto m = content_margins();
878 if (!m.is_null())
879 return UISize { m.left() + m.right(), m.top() + m.bottom() };
880 return {};
881}
882
883void Widget::invalidate_layout()
884{
885 if (window())
886 window()->schedule_relayout();
887}
888
889void Widget::set_visible(bool visible)
890{
891 if (visible == m_visible)
892 return;
893 m_visible = visible;
894 layout_relevant_change_occurred();
895 if (m_visible)
896 update();
897 if (!m_visible && is_focused())
898 set_focus(false);
899
900 if (m_visible) {
901 ShowEvent e;
902 event(e);
903 } else {
904 HideEvent e;
905 event(e);
906 }
907}
908
909bool Widget::spans_entire_window_horizontally() const
910{
911 auto* w = window();
912 if (!w)
913 return false;
914 auto* main_widget = w->main_widget();
915 if (!main_widget)
916 return false;
917 if (main_widget == this)
918 return true;
919 auto wrr = window_relative_rect();
920 return wrr.left() == main_widget->rect().left() && wrr.right() == main_widget->rect().right();
921}
922
923void Widget::set_enabled(bool enabled)
924{
925 if (m_enabled == enabled)
926 return;
927 m_enabled = enabled;
928
929 for_each_child_widget([enabled](auto& child) {
930 child.set_enabled(enabled);
931 return IterationDecision::Continue;
932 });
933
934 if (!m_enabled && window() && window()->focused_widget() == this) {
935 window()->did_disable_focused_widget({});
936 }
937
938 if (!m_enabled)
939 set_override_cursor(Gfx::StandardCursor::None);
940
941 Event e(Event::EnabledChange);
942 event(e);
943 update();
944}
945
946void Widget::move_to_front()
947{
948 auto* parent = parent_widget();
949 if (!parent)
950 return;
951 if (parent->children().size() == 1)
952 return;
953 parent->children().remove_first_matching([this](auto& entry) {
954 return entry == this;
955 });
956 parent->children().append(*this);
957 parent->update();
958}
959
960void Widget::move_to_back()
961{
962 auto* parent = parent_widget();
963 if (!parent)
964 return;
965 if (parent->children().size() == 1)
966 return;
967 parent->children().remove_first_matching([this](auto& entry) {
968 return entry == this;
969 });
970 parent->children().prepend(*this);
971 parent->update();
972}
973
974bool Widget::is_frontmost() const
975{
976 auto* parent = parent_widget();
977 if (!parent)
978 return true;
979 return parent->children().last() == this;
980}
981
982bool Widget::is_backmost() const
983{
984 auto* parent = parent_widget();
985 if (!parent)
986 return true;
987 return parent->children().first() == this;
988}
989
990Action* Widget::action_for_shortcut(Shortcut const& shortcut)
991{
992 return Action::find_action_for_shortcut(*this, shortcut);
993}
994
995void Widget::set_updates_enabled(bool enabled)
996{
997 if (m_updates_enabled == enabled)
998 return;
999 m_updates_enabled = enabled;
1000 if (enabled)
1001 update();
1002}
1003
1004void Widget::focus_previous_widget(FocusSource source, bool siblings_only)
1005{
1006 auto focusable_widgets = window()->focusable_widgets(source);
1007 if (siblings_only)
1008 focusable_widgets.remove_all_matching([this](auto& entry) { return entry.parent() != parent(); });
1009 for (int i = focusable_widgets.size() - 1; i >= 0; --i) {
1010 if (&focusable_widgets[i] != this)
1011 continue;
1012 if (i > 0)
1013 focusable_widgets[i - 1].set_focus(true, source);
1014 else
1015 focusable_widgets.last().set_focus(true, source);
1016 }
1017}
1018
1019void Widget::focus_next_widget(FocusSource source, bool siblings_only)
1020{
1021 auto focusable_widgets = window()->focusable_widgets(source);
1022 if (siblings_only)
1023 focusable_widgets.remove_all_matching([this](auto& entry) { return entry.parent() != parent(); });
1024 for (size_t i = 0; i < focusable_widgets.size(); ++i) {
1025 if (&focusable_widgets[i] != this)
1026 continue;
1027 if (i < focusable_widgets.size() - 1)
1028 focusable_widgets[i + 1].set_focus(true, source);
1029 else
1030 focusable_widgets.first().set_focus(true, source);
1031 }
1032}
1033
1034Vector<Widget&> Widget::child_widgets() const
1035{
1036 Vector<Widget&> widgets;
1037 widgets.ensure_capacity(children().size());
1038 for (auto& child : const_cast<Widget*>(this)->children()) {
1039 if (is<Widget>(*child))
1040 widgets.append(static_cast<Widget&>(*child));
1041 }
1042 return widgets;
1043}
1044
1045void Widget::set_palette(Palette& palette)
1046{
1047 m_palette = palette.impl();
1048 update();
1049}
1050
1051void Widget::set_title(DeprecatedString title)
1052{
1053 m_title = move(title);
1054 layout_relevant_change_occurred();
1055 // For tab widget children, our change in title also affects the parent.
1056 if (parent_widget())
1057 parent_widget()->update();
1058}
1059
1060DeprecatedString Widget::title() const
1061{
1062 return m_title;
1063}
1064
1065void Widget::set_background_role(ColorRole role)
1066{
1067 m_background_role = role;
1068 update();
1069}
1070
1071void Widget::set_foreground_role(ColorRole role)
1072{
1073 m_foreground_role = role;
1074 update();
1075}
1076
1077Gfx::Palette Widget::palette() const
1078{
1079 return Gfx::Palette(*m_palette);
1080}
1081
1082void Widget::did_begin_inspection()
1083{
1084 update();
1085}
1086
1087void Widget::did_end_inspection()
1088{
1089 update();
1090}
1091
1092void Widget::set_grabbable_margins(Margins const& margins)
1093{
1094 if (m_grabbable_margins == margins)
1095 return;
1096 m_grabbable_margins = margins;
1097 layout_relevant_change_occurred();
1098}
1099
1100Gfx::IntRect Widget::relative_non_grabbable_rect() const
1101{
1102 auto rect = relative_rect();
1103 rect.translate_by(m_grabbable_margins.left(), m_grabbable_margins.top());
1104 rect.set_width(rect.width() - (m_grabbable_margins.left() + m_grabbable_margins.right()));
1105 rect.set_height(rect.height() - (m_grabbable_margins.top() + m_grabbable_margins.bottom()));
1106 return rect;
1107}
1108
1109void Widget::set_tooltip(DeprecatedString tooltip)
1110{
1111 m_tooltip = move(tooltip);
1112 if (Application::the()->tooltip_source_widget() == this)
1113 show_or_hide_tooltip();
1114}
1115
1116void Widget::show_or_hide_tooltip()
1117{
1118 if (has_tooltip())
1119 Application::the()->show_tooltip(m_tooltip, this);
1120 else
1121 Application::the()->hide_tooltip();
1122}
1123
1124Gfx::IntRect Widget::children_clip_rect() const
1125{
1126 return rect();
1127}
1128
1129void Widget::set_override_cursor(AK::Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> cursor)
1130{
1131 auto const& are_cursors_the_same = [](auto const& a, auto const& b) {
1132 if (a.template has<Gfx::StandardCursor>() != b.template has<Gfx::StandardCursor>())
1133 return false;
1134 if (a.template has<Gfx::StandardCursor>())
1135 return a.template get<Gfx::StandardCursor>() == b.template get<Gfx::StandardCursor>();
1136 return a.template get<NonnullRefPtr<Gfx::Bitmap const>>().ptr() == b.template get<NonnullRefPtr<Gfx::Bitmap const>>().ptr();
1137 };
1138
1139 if (are_cursors_the_same(m_override_cursor, cursor))
1140 return;
1141
1142 m_override_cursor = move(cursor);
1143 if (auto* window = this->window()) {
1144 window->update_cursor({});
1145 }
1146}
1147
1148ErrorOr<void> Widget::load_from_gml(StringView gml_string)
1149{
1150 return load_from_gml(gml_string, [](DeprecatedString const& class_name) -> ErrorOr<NonnullRefPtr<Core::Object>> {
1151 dbgln("Class '{}' not registered", class_name);
1152 return Error::from_string_literal("Class not registered");
1153 });
1154}
1155
1156ErrorOr<void> Widget::load_from_gml(StringView gml_string, UnregisteredChildHandler unregistered_child_handler)
1157{
1158 auto value = TRY(GML::parse_gml(gml_string));
1159 return load_from_gml_ast(value, unregistered_child_handler);
1160}
1161
1162ErrorOr<void> Widget::load_from_gml_ast(NonnullRefPtr<GUI::GML::Node const> ast, UnregisteredChildHandler unregistered_child_handler)
1163{
1164 if (is<GUI::GML::GMLFile>(ast.ptr()))
1165 return load_from_gml_ast(static_cast<GUI::GML::GMLFile const&>(*ast).main_class(), unregistered_child_handler);
1166
1167 VERIFY(is<GUI::GML::Object>(ast.ptr()));
1168 auto const& object = static_cast<GUI::GML::Object const&>(*ast);
1169
1170 object.for_each_property([&](auto key, auto value) {
1171 set_property(key, value);
1172 });
1173
1174 auto layout = object.layout_object();
1175 if (!layout.is_null()) {
1176 auto class_name = layout->name();
1177 if (class_name.is_null()) {
1178 return Error::from_string_literal("Invalid layout class name");
1179 }
1180
1181 auto& layout_class = *Core::ObjectClassRegistration::find("GUI::Layout"sv);
1182 if (auto* registration = Core::ObjectClassRegistration::find(class_name)) {
1183 auto layout = TRY(registration->construct());
1184 if (!registration->is_derived_from(layout_class)) {
1185 dbgln("Invalid layout class: '{}'", class_name.to_deprecated_string());
1186 return Error::from_string_literal("Invalid layout class");
1187 }
1188 set_layout(static_ptr_cast<Layout>(layout));
1189 } else {
1190 dbgln("Unknown layout class: '{}'", class_name.to_deprecated_string());
1191 return Error::from_string_literal("Unknown layout class");
1192 }
1193
1194 layout->for_each_property([&](auto key, auto value) {
1195 this->layout()->set_property(key, value);
1196 });
1197 }
1198
1199 auto& widget_class = *Core::ObjectClassRegistration::find("GUI::Widget"sv);
1200 bool is_tab_widget = is<TabWidget>(*this);
1201 TRY(object.try_for_each_child_object([&](auto const& child_data) -> ErrorOr<void> {
1202 auto class_name = child_data.name();
1203
1204 // It is very questionable if this pseudo object should exist, but it works fine like this for now.
1205 if (class_name == "GUI::Layout::Spacer") {
1206 if (!this->layout()) {
1207 return Error::from_string_literal("Specified GUI::Layout::Spacer in GML, but the parent has no Layout.");
1208 }
1209 this->layout()->add_spacer();
1210 } else {
1211 RefPtr<Core::Object> child;
1212 if (auto* registration = Core::ObjectClassRegistration::find(class_name)) {
1213 child = TRY(registration->construct());
1214 if (!registration->is_derived_from(widget_class)) {
1215 dbgln("Invalid widget class: '{}'", class_name);
1216 return Error::from_string_literal("Invalid widget class");
1217 }
1218 } else {
1219 child = TRY(unregistered_child_handler(class_name));
1220 }
1221 if (!child)
1222 return Error::from_string_literal("Unable to construct a Widget class for child");
1223 add_child(*child);
1224
1225 // This is possible as we ensure that Widget is a base class above.
1226 TRY(static_ptr_cast<Widget>(child)->load_from_gml_ast(child_data, unregistered_child_handler));
1227
1228 if (is_tab_widget) {
1229 // FIXME: We need to have the child added before loading it so that it can access us. But the TabWidget logic requires the child to not be present yet.
1230 remove_child(*child);
1231 reinterpret_cast<TabWidget*>(this)->add_widget(*static_ptr_cast<Widget>(child));
1232 }
1233 }
1234
1235 return {};
1236 }));
1237
1238 return {};
1239}
1240
1241bool Widget::has_focus_within() const
1242{
1243 auto* window = this->window();
1244 if (!window)
1245 return false;
1246 if (!window->focused_widget())
1247 return false;
1248 auto& effective_focus_widget = focus_proxy() ? *focus_proxy() : *this;
1249 return window->focused_widget() == &effective_focus_widget || is_ancestor_of(*window->focused_widget());
1250}
1251
1252void Widget::set_shrink_to_fit(bool shrink_to_fit)
1253{
1254 // This function is deprecated, and soon to be removed, it is only still here to ease the transition to UIDimensions
1255 if (shrink_to_fit)
1256 set_preferred_size(SpecialDimension::Fit);
1257}
1258
1259bool Widget::has_pending_drop() const
1260{
1261 return Application::the()->pending_drop_widget() == this;
1262}
1263
1264bool Widget::is_visible_for_timer_purposes() const
1265{
1266 return is_visible() && Object::is_visible_for_timer_purposes();
1267}
1268
1269ErrorOr<void> Widget::add_spacer()
1270{
1271 VERIFY(layout());
1272 return layout()->try_add_spacer();
1273}
1274
1275}