Serenity Operating System
at master 1275 lines 38 kB view raw
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}