ElementScroll.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #include "../../Include/RmlUi/Core/ElementScroll.h"
  2. #include "../../Include/RmlUi/Core/ComputedValues.h"
  3. #include "../../Include/RmlUi/Core/Context.h"
  4. #include "../../Include/RmlUi/Core/Element.h"
  5. #include "../../Include/RmlUi/Core/ElementUtilities.h"
  6. #include "../../Include/RmlUi/Core/Event.h"
  7. #include "../../Include/RmlUi/Core/Factory.h"
  8. #include "Layout/LayoutDetails.h"
  9. #include "WidgetScroll.h"
  10. namespace Rml {
  11. ElementScroll::ElementScroll(Element* _element)
  12. {
  13. element = _element;
  14. corner = nullptr;
  15. }
  16. ElementScroll::~ElementScroll() {}
  17. void ElementScroll::Update()
  18. {
  19. for (int i = 0; i < 2; i++)
  20. {
  21. if (scrollbars[i].widget != nullptr)
  22. scrollbars[i].widget->Update();
  23. }
  24. }
  25. void ElementScroll::EnableScrollbar(Orientation orientation, float element_width)
  26. {
  27. if (!scrollbars[orientation].enabled)
  28. {
  29. CreateScrollbar(orientation);
  30. scrollbars[orientation].element->SetProperty(PropertyId::Visibility, Property(Style::Visibility::Visible));
  31. scrollbars[orientation].enabled = true;
  32. }
  33. // Determine the size of the scrollbar.
  34. Box box;
  35. LayoutDetails::BuildBox(box, Vector2f(element_width, element_width), scrollbars[orientation].element);
  36. if (orientation == VERTICAL)
  37. scrollbars[orientation].size = box.GetSize(BoxArea::Margin).x;
  38. if (orientation == HORIZONTAL)
  39. {
  40. if (box.GetSize().y < 0)
  41. scrollbars[orientation].size = box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Left) +
  42. box.GetCumulativeEdge(BoxArea::Content, BoxEdge::Right) +
  43. ResolveValue(scrollbars[orientation].element->GetComputedValues().height(), element_width);
  44. else
  45. scrollbars[orientation].size = box.GetSize(BoxArea::Margin).y;
  46. }
  47. }
  48. void ElementScroll::DisableScrollbar(Orientation orientation)
  49. {
  50. if (scrollbars[orientation].enabled)
  51. {
  52. scrollbars[orientation].element->SetProperty(PropertyId::Visibility, Property(Style::Visibility::Hidden));
  53. scrollbars[orientation].enabled = false;
  54. if (corner)
  55. corner->SetProperty(PropertyId::Visibility, Property(Style::Visibility::Hidden));
  56. }
  57. }
  58. void ElementScroll::UpdateScrollbar(Orientation orientation)
  59. {
  60. float bar_position;
  61. float traversable_track;
  62. if (orientation == VERTICAL)
  63. {
  64. bar_position = element->GetScrollTop();
  65. traversable_track = element->GetScrollHeight() - element->GetClientHeight();
  66. }
  67. else
  68. {
  69. bar_position = element->GetScrollLeft();
  70. traversable_track = element->GetScrollWidth() - element->GetClientWidth();
  71. }
  72. if (traversable_track <= 0)
  73. bar_position = 0;
  74. else
  75. bar_position /= traversable_track;
  76. if (scrollbars[orientation].widget != nullptr)
  77. {
  78. bar_position = Math::Clamp(bar_position, 0.0f, 1.0f);
  79. if (scrollbars[orientation].widget->GetBarPosition() != bar_position)
  80. scrollbars[orientation].widget->SetBarPosition(bar_position);
  81. }
  82. }
  83. Element* ElementScroll::GetScrollbar(Orientation orientation)
  84. {
  85. return scrollbars[orientation].element;
  86. }
  87. float ElementScroll::GetScrollbarSize(Orientation orientation)
  88. {
  89. if (!scrollbars[orientation].enabled)
  90. return 0;
  91. return scrollbars[orientation].size;
  92. }
  93. void ElementScroll::FormatScrollbars()
  94. {
  95. const Box& element_box = element->GetBox();
  96. const Vector2f containing_block = element_box.GetSize(BoxArea::Padding);
  97. for (int i = 0; i < 2; i++)
  98. {
  99. if (!scrollbars[i].enabled)
  100. continue;
  101. if (i == VERTICAL)
  102. {
  103. scrollbars[i].widget->SetBarLength(element->GetClientHeight());
  104. scrollbars[i].widget->SetTrackLength(element->GetScrollHeight());
  105. float traversable_track = element->GetScrollHeight() - element->GetClientHeight();
  106. if (traversable_track > 0)
  107. scrollbars[i].widget->SetBarPosition(element->GetScrollTop() / traversable_track);
  108. else
  109. scrollbars[i].widget->SetBarPosition(0);
  110. }
  111. else
  112. {
  113. scrollbars[i].widget->SetBarLength(element->GetClientWidth());
  114. scrollbars[i].widget->SetTrackLength(element->GetScrollWidth());
  115. float traversable_track = element->GetScrollWidth() - element->GetClientWidth();
  116. if (traversable_track > 0)
  117. scrollbars[i].widget->SetBarPosition(element->GetScrollLeft() / traversable_track);
  118. else
  119. scrollbars[i].widget->SetBarPosition(0);
  120. }
  121. float slider_length = containing_block[1 - i];
  122. float user_scrollbar_margin = scrollbars[i].element->GetComputedValues().scrollbar_margin();
  123. float min_scrollbar_margin = GetScrollbarSize(i == VERTICAL ? HORIZONTAL : VERTICAL);
  124. slider_length -= Math::Max(user_scrollbar_margin, min_scrollbar_margin);
  125. scrollbars[i].widget->FormatElements(containing_block, slider_length);
  126. int variable_axis = i == VERTICAL ? 0 : 1;
  127. Vector2f offset = element_box.GetPosition(BoxArea::Padding);
  128. offset[variable_axis] += containing_block[variable_axis] -
  129. (scrollbars[i].element->GetBox().GetSize(BoxArea::Border)[variable_axis] +
  130. scrollbars[i].element->GetBox().GetEdge(BoxArea::Margin, i == VERTICAL ? BoxEdge::Right : BoxEdge::Bottom));
  131. // Add the top or left margin (as appropriate) onto the scrollbar's position.
  132. offset[1 - variable_axis] += scrollbars[i].element->GetBox().GetEdge(BoxArea::Margin, i == VERTICAL ? BoxEdge::Top : BoxEdge::Left);
  133. scrollbars[i].element->SetOffset(offset, element, true);
  134. }
  135. // Format the corner, if it is necessary.
  136. if (scrollbars[0].enabled && scrollbars[1].enabled)
  137. {
  138. CreateCorner();
  139. Box corner_box;
  140. LayoutDetails::BuildBox(corner_box, Vector2f(containing_block.x), corner);
  141. corner_box.SetContent(Vector2f(scrollbars[VERTICAL].size, scrollbars[HORIZONTAL].size));
  142. corner->SetBox(corner_box);
  143. corner->SetOffset(containing_block + element_box.GetPosition(BoxArea::Padding) -
  144. Vector2f(scrollbars[VERTICAL].size, scrollbars[HORIZONTAL].size) - corner_box.GetPosition(BoxArea::Margin),
  145. element, true);
  146. corner->SetProperty(PropertyId::Visibility, Property(Style::Visibility::Visible));
  147. }
  148. }
  149. void ElementScroll::UpdateProperties()
  150. {
  151. for (Element* scroll_element : {scrollbars[VERTICAL].element, scrollbars[HORIZONTAL].element, corner})
  152. {
  153. if (scroll_element)
  154. UpdateScrollElementProperties(scroll_element);
  155. }
  156. }
  157. bool ElementScroll::CreateScrollbar(Orientation orientation)
  158. {
  159. if (scrollbars[orientation].element && scrollbars[orientation].widget)
  160. return true;
  161. ElementPtr scrollbar_element =
  162. Factory::InstanceElement(element, "*", orientation == VERTICAL ? "scrollbarvertical" : "scrollbarhorizontal", XMLAttributes());
  163. scrollbars[orientation].element = scrollbar_element.get();
  164. scrollbars[orientation].element->SetProperty(PropertyId::Clip, Property(1, Unit::NUMBER));
  165. scrollbars[orientation].element->SetProperty(PropertyId::Drag, Property(Style::Drag::Block));
  166. scrollbars[orientation].widget = MakeUnique<WidgetScroll>(scrollbars[orientation].element);
  167. scrollbars[orientation].widget->Initialise(orientation == VERTICAL ? WidgetScroll::VERTICAL : WidgetScroll::HORIZONTAL);
  168. Element* child = element->AppendChild(std::move(scrollbar_element), false);
  169. UpdateScrollElementProperties(child);
  170. return true;
  171. }
  172. bool ElementScroll::CreateCorner()
  173. {
  174. if (corner != nullptr)
  175. return true;
  176. ElementPtr corner_element = Factory::InstanceElement(element, "*", "scrollbarcorner", XMLAttributes());
  177. corner = corner_element.get();
  178. corner->SetProperty(PropertyId::Clip, Property(1, Unit::NUMBER));
  179. corner->SetProperty(PropertyId::Drag, Property(Style::Drag::Block));
  180. Element* child = element->AppendChild(std::move(corner_element), false);
  181. UpdateScrollElementProperties(child);
  182. return true;
  183. }
  184. void ElementScroll::UpdateScrollElementProperties(Element* scroll_element)
  185. {
  186. // The construction of scrollbars can occur during layouting, then we need some properties and computed values straight away.
  187. // In particular their size. Furthermore, updating these properties straight away avoids dirtying the document after layouting,
  188. // which may result in one less additional and unnecessary layouting procedure.
  189. Context* context = element->GetContext();
  190. const float dp_ratio = (context ? context->GetDensityIndependentPixelRatio() : 1.0f);
  191. const Vector2f vp_dimensions = (context ? Vector2f(context->GetDimensions()) : Vector2f(1.0f));
  192. scroll_element->Update(dp_ratio, vp_dimensions);
  193. }
  194. ElementScroll::Scrollbar::Scrollbar() {}
  195. ElementScroll::Scrollbar::~Scrollbar() {}
  196. } // namespace Rml