LineBox.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #pragma once
  2. #include "../../../Include/RmlUi/Core/StyleTypes.h"
  3. #include "InlineTypes.h"
  4. namespace Rml {
  5. class InlineBox;
  6. class InlineBoxRoot;
  7. class InlineLevelBox;
  8. /*
  9. Horizontally places fragments generated from inline-level boxes.
  10. Inline boxes can nest other inline-level boxes, thereby creating a tree of inline-level boxes. This tree structure
  11. needs to be considered for the generated fragments as well, since their size and position depend on this tree
  12. structure.
  13. Note that a single inline-level box can generate multiple fragments, possibly placed in different line boxes.
  14. */
  15. class LineBox final {
  16. public:
  17. LineBox() = default;
  18. ~LineBox();
  19. // Set the line box position and dimensions.
  20. void SetLineBox(Vector2f line_position, float line_width, float line_minimum_height);
  21. /// Generates a fragment from a box and places it on this line, if possible.
  22. /// @param[in] box The inline-level box to be placed.
  23. /// @param[in] layout_mode The inline layout mode, which affects the fragment generation.
  24. /// @param[in,out] inout_overflow_handle A handle to resume fragment generation from a partially placed box.
  25. /// @return True if the box should be placed again on a new line, either because the box could not fit or there is more content to be placed.
  26. bool AddBox(InlineLevelBox* box, InlineLayoutMode layout_mode, LayoutOverflowHandle& inout_overflow_handle);
  27. /// Closes the open inline box.
  28. /// @param[in] inline_box The inline box to be closed. Should match the currently open box, strictly used for verification.
  29. /// @note Only inline boxes need to be closed, not other inline-level boxes since they do not contain child boxes.
  30. void CloseInlineBox(InlineBox* inline_box);
  31. /// Vertically positions each fragment and sizes the line, after splitting any open inline boxes to be placed on a new line.
  32. /// @param[in] root_inline_box The root inline box of our inline container.
  33. /// @param[in] split_all_open_boxes Split all open inline boxes, even if they have no content.
  34. /// @param[out] out_height_of_line Resulting height of line. This can be different from the element's computed line-height property.
  35. /// @return The next line if any open fragments had to be split or wrapped down.
  36. UniquePtr<LineBox> DetermineVerticalPositioning(const InlineBoxRoot* root_inline_box, bool split_all_open_boxes, float& out_height_of_line);
  37. // Closes the line and submits all fragments. Thereby positioning, sizing, and placing their corresponding boxes.
  38. // @note The line must have been vertically positioned before closing.
  39. void Close(Element* offset_parent, Vector2f offset_parent_position, Style::TextAlign text_align);
  40. float GetBoxCursor() const { return box_cursor; }
  41. Vector2f GetPosition() const { return line_position; }
  42. float GetLineWidth() const { return line_width; }
  43. float GetLineMinimumHeight() const { return line_minimum_height; }
  44. InlineBox* GetOpenInlineBox();
  45. bool IsClosed() const { return is_closed; }
  46. bool HasContent() const { return has_content; }
  47. // Returns true if this line should be collapsed, such that it takes up no height in the inline container.
  48. bool CanCollapseLine() const;
  49. // Returns the width of the contents of the line, relative to the left edge of the line box. Includes spacing due to horizontal alignment.
  50. // @note Only available after line has been closed.
  51. float GetExtentRight() const;
  52. // Returns the baseline position, relative to the top of the line box.
  53. // @note Only available after line has been closed.
  54. float GetBaseline() const;
  55. String DebugDumpTree(int depth) const;
  56. void* operator new(size_t size);
  57. void operator delete(void* chunk, size_t size);
  58. private:
  59. using FragmentIndex = int;
  60. using VerticalAlignType = Style::VerticalAlign::Type;
  61. static constexpr FragmentIndex RootFragmentIndex = -1;
  62. struct Fragment {
  63. Fragment() = default;
  64. Fragment(InlineLevelBox* box, FragmentConstructor constructor, VerticalAlignType vertical_align, float position_x, FragmentIndex parent) :
  65. box(box), type(constructor.type), fragment_handle(constructor.fragment_handle), vertical_align(vertical_align), position(position_x, 0.f),
  66. layout_width(constructor.layout_width), parent(parent)
  67. {}
  68. InlineLevelBox* box = nullptr;
  69. FragmentType type = FragmentType::Invalid;
  70. LayoutFragmentHandle fragment_handle = {};
  71. VerticalAlignType vertical_align = {};
  72. // Layout state.
  73. Vector2f position; // Position relative to start of the line, disregarding floats, (x: outer-left edge, y: baseline).
  74. float layout_width = 0.f; // Inner width for inline boxes, otherwise outer width.
  75. // Vertical alignment state.
  76. FragmentIndex parent = RootFragmentIndex;
  77. FragmentIndex aligned_subtree_root = RootFragmentIndex; // Index of the aligned subtree the fragment belongs to.
  78. float baseline_offset = 0.f; // Vertical offset from aligned subtree root baseline to our baseline.
  79. // For inline boxes.
  80. bool split_left = false;
  81. bool split_right = false;
  82. bool has_content = false;
  83. FragmentIndex children_end_index = 0; // One-past-last-child of this box, as index into fragment list.
  84. // For aligned subtree root.
  85. float max_ascent = 0.f;
  86. float max_descent = 0.f;
  87. };
  88. using FragmentList = Vector<Fragment>;
  89. // Place an open fragment.
  90. void CloseFragment(Fragment& open_fragment, float right_inner_edge_position);
  91. // Splits the line, returning a new line if there are any open fragments.
  92. UniquePtr<LineBox> SplitLine(bool split_all_open_boxes);
  93. // Vertically align all descendants of the subtree. Returns the ascent of the top-most box, and descent of the bottom-most box.
  94. void VerticallyAlignSubtree(int subtree_root_index, int children_end_index, float& max_ascent, float& max_descent);
  95. // Returns true if the fragment establishes an aligned subtree.
  96. static bool IsAlignedSubtreeRoot(const Fragment& fragment)
  97. {
  98. return (fragment.vertical_align == VerticalAlignType::Top || fragment.vertical_align == VerticalAlignType::Center ||
  99. fragment.vertical_align == VerticalAlignType::Bottom);
  100. }
  101. // Returns the aligned subtree root for a given fragment, based on its ancestors.
  102. FragmentIndex DetermineAlignedSubtreeRoot(FragmentIndex index) const
  103. {
  104. while (index != RootFragmentIndex)
  105. {
  106. const Fragment& fragment = fragments[index];
  107. if (IsAlignedSubtreeRoot(fragment))
  108. return index;
  109. index = fragment.parent;
  110. }
  111. return index;
  112. }
  113. template <typename Func>
  114. void ForAllOpenFragments(Func&& func)
  115. {
  116. FragmentIndex index = open_fragments_leaf;
  117. while (index != RootFragmentIndex)
  118. {
  119. Fragment& fragment = fragments[index];
  120. index = fragment.parent;
  121. func(fragment);
  122. }
  123. }
  124. // Position of the line, relative to our block formatting context root.
  125. Vector2f line_position;
  126. // Available space for the line. Based on our parent box content width, possibly shrinked due to floating boxes.
  127. float line_width = 0.f;
  128. // Lower-bound estimate for the line box height.
  129. float line_minimum_height = 0.f;
  130. // The horizontal cursor. This is the outer-right position of the last placed fragment.
  131. float box_cursor = 0.f;
  132. // The contribution of opened inline boxes to the placement of the next fragment, due to their left edges (margin-border-padding).
  133. float open_spacing_left = 0.f;
  134. // List of fragments in this line box.
  135. FragmentList fragments;
  136. // Index of the last open fragment. The list of open fragments is this leaf and all of its ancestors up to the root.
  137. FragmentIndex open_fragments_leaf = RootFragmentIndex;
  138. bool has_content = false;
  139. bool is_vertically_positioned = false;
  140. bool is_closed = false;
  141. // Content offset due to space distribution from 'text-align'. Available after close.
  142. float offset_horizontal_alignment = 0.f;
  143. // The line box's height above baseline. Available after close.
  144. float total_height_above_baseline = 0.f;
  145. };
  146. } // namespace Rml