ScrollableContainerWidget.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /*
  2. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/RefPtr.h>
  7. #include <LibGUI/Layout.h>
  8. #include <LibGUI/ScrollableContainerWidget.h>
  9. REGISTER_WIDGET(GUI, ScrollableContainerWidget)
  10. namespace GUI {
  11. ScrollableContainerWidget::ScrollableContainerWidget()
  12. {
  13. REGISTER_BOOL_PROPERTY("scrollbars_enabled", is_scrollbars_enabled, set_scrollbars_enabled);
  14. REGISTER_BOOL_PROPERTY("should_hide_unnecessary_scrollbars", should_hide_unnecessary_scrollbars, set_should_hide_unnecessary_scrollbars);
  15. }
  16. ScrollableContainerWidget::~ScrollableContainerWidget()
  17. {
  18. }
  19. void ScrollableContainerWidget::did_scroll()
  20. {
  21. AbstractScrollableWidget::did_scroll();
  22. update_widget_position();
  23. }
  24. void ScrollableContainerWidget::update_widget_position()
  25. {
  26. if (!m_widget)
  27. return;
  28. m_widget->move_to(-horizontal_scrollbar().value() + content_margins().left(), -vertical_scrollbar().value() + content_margins().top());
  29. }
  30. void ScrollableContainerWidget::update_widget_size()
  31. {
  32. if (!m_widget)
  33. return;
  34. m_widget->do_layout();
  35. if (m_widget->is_shrink_to_fit() && m_widget->layout()) {
  36. auto new_size = Widget::content_size();
  37. auto preferred_size = m_widget->layout()->preferred_size();
  38. if (preferred_size.width() != -1)
  39. new_size.set_width(preferred_size.width());
  40. if (preferred_size.height() != -1)
  41. new_size.set_height(preferred_size.height());
  42. m_widget->resize(new_size);
  43. set_content_size(new_size);
  44. } else {
  45. auto inner_size = Widget::content_size();
  46. auto min_size = m_widget->min_size();
  47. auto new_size = Gfx::Size {
  48. max(inner_size.width(), min_size.width()),
  49. max(inner_size.height(), min_size.height())
  50. };
  51. m_widget->resize(new_size);
  52. set_content_size(new_size);
  53. }
  54. }
  55. void ScrollableContainerWidget::resize_event(GUI::ResizeEvent& event)
  56. {
  57. AbstractScrollableWidget::resize_event(event);
  58. update_widget_position();
  59. update_widget_size();
  60. }
  61. void ScrollableContainerWidget::set_widget(GUI::Widget* widget)
  62. {
  63. if (m_widget == widget)
  64. return;
  65. if (m_widget)
  66. remove_child(*m_widget);
  67. m_widget = widget;
  68. if (m_widget) {
  69. add_child(*m_widget);
  70. m_widget->move_to_back();
  71. }
  72. update_widget_size();
  73. update_widget_position();
  74. }
  75. bool ScrollableContainerWidget::load_from_gml_ast(NonnullRefPtr<GUI::GML::Node> ast, RefPtr<Core::Object> (*unregistered_child_handler)(const String&))
  76. {
  77. if (is<GUI::GML::GMLFile>(ast.ptr()))
  78. return load_from_gml_ast(static_ptr_cast<GUI::GML::GMLFile>(ast)->main_class(), unregistered_child_handler);
  79. VERIFY(is<GUI::GML::Object>(ast.ptr()));
  80. auto object = static_ptr_cast<GUI::GML::Object>(ast);
  81. object->for_each_property([&](auto key, auto value) {
  82. set_property(key, value);
  83. });
  84. auto content_widget_value = object->get_property("content_widget"sv);
  85. if (!content_widget_value.is_null() && !is<Object>(content_widget_value.ptr())) {
  86. dbgln("content widget is not an object");
  87. return false;
  88. }
  89. auto has_children = false;
  90. object->for_each_child_object([&](auto) { has_children = true; });
  91. if (has_children) {
  92. dbgln("children specified for ScrollableContainerWidget, but only 1 widget as content_widget is supported");
  93. return false;
  94. }
  95. if (!content_widget_value.is_null() && is<Object>(content_widget_value.ptr())) {
  96. auto content_widget = static_ptr_cast<GUI::GML::Object>(content_widget_value);
  97. auto class_name = content_widget->name();
  98. RefPtr<Core::Object> child;
  99. if (auto* registration = Core::ObjectClassRegistration::find(class_name)) {
  100. child = registration->construct();
  101. } else {
  102. child = unregistered_child_handler(class_name);
  103. }
  104. if (!child)
  105. return false;
  106. auto widget_ptr = verify_cast<GUI::Widget>(child.ptr());
  107. set_widget(widget_ptr);
  108. static_ptr_cast<Widget>(child)->load_from_gml_ast(content_widget.release_nonnull(), unregistered_child_handler);
  109. return true;
  110. }
  111. return true;
  112. }
  113. }