ScrollableContainerWidget.cpp 4.9 KB

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