LineBox.h 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * This source file is part of RmlUi, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://github.com/mikke89/RmlUi
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. * Copyright (c) 2019-2023 The RmlUi Team, and contributors
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. */
  28. #ifndef RMLUI_CORE_LAYOUT_LINEBOX_H
  29. #define RMLUI_CORE_LAYOUT_LINEBOX_H
  30. #include "../../../Include/RmlUi/Core/StyleTypes.h"
  31. #include "InlineTypes.h"
  32. namespace Rml {
  33. class InlineBox;
  34. class InlineBoxRoot;
  35. class InlineLevelBox;
  36. /*
  37. Horizontally places fragments generated from inline-level boxes.
  38. Inline boxes can nest other inline-level boxes, thereby creating a tree of inline-level boxes. This tree structure
  39. needs to be considered for the generated fragments as well, since their size and position depend on this tree
  40. structure.
  41. Note that a single inline-level box can generate multiple fragments, possibly placed in different line boxes.
  42. */
  43. class LineBox final {
  44. public:
  45. LineBox() = default;
  46. ~LineBox();
  47. // Set the line box position and dimensions.
  48. void SetLineBox(Vector2f line_position, float line_width, float line_minimum_height);
  49. /// Generates a fragment from a box and places it on this line, if possible.
  50. /// @param[in] box The inline-level box to be placed.
  51. /// @param[in] layout_mode The inline layout mode, which affects the fragment generation.
  52. /// @param[in,out] inout_overflow_handle A handle to resume fragment generation from a partially placed box.
  53. /// @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.
  54. bool AddBox(InlineLevelBox* box, InlineLayoutMode layout_mode, LayoutOverflowHandle& inout_overflow_handle);
  55. /// Closes the open inline box.
  56. /// @param[in] inline_box The inline box to be closed. Should match the currently open box, strictly used for verification.
  57. /// @note Only inline boxes need to be closed, not other inline-level boxes since they do not contain child boxes.
  58. void CloseInlineBox(InlineBox* inline_box);
  59. /// Vertically positions each fragment and sizes the line, after splitting any open inline boxes to be placed on a new line.
  60. /// @param[in] root_inline_box The root inline box of our inline container.
  61. /// @param[in] split_all_open_boxes Split all open inline boxes, even if they have no content.
  62. /// @param[out] out_height_of_line Resulting height of line. This can be different from the element's computed line-height property.
  63. /// @return The next line if any open fragments had to be split or wrapped down.
  64. UniquePtr<LineBox> DetermineVerticalPositioning(const InlineBoxRoot* root_inline_box, bool split_all_open_boxes, float& out_height_of_line);
  65. // Closes the line and submits all fragments. Thereby positioning, sizing, and placing their corresponding boxes.
  66. // @note The line must have been vertically positioned before closing.
  67. void Close(Element* offset_parent, Vector2f offset_parent_position, Style::TextAlign text_align);
  68. float GetBoxCursor() const { return box_cursor; }
  69. Vector2f GetPosition() const { return line_position; }
  70. float GetLineWidth() const { return line_width; }
  71. float GetLineMinimumHeight() const { return line_minimum_height; }
  72. InlineBox* GetOpenInlineBox();
  73. bool IsClosed() const { return is_closed; }
  74. bool HasContent() const { return has_content; }
  75. // Returns true if this line should be collapsed, such that it takes up no height in the inline container.
  76. bool CanCollapseLine() const;
  77. // Returns the width of the contents of the line, relative to the left edge of the line box. Includes spacing due to horizontal alignment.
  78. // @note Only available after line has been closed.
  79. float GetExtentRight() const;
  80. // Returns the baseline position, relative to the top of the line box.
  81. // @note Only available after line has been closed.
  82. float GetBaseline() const;
  83. String DebugDumpTree(int depth) const;
  84. void* operator new(size_t size);
  85. void operator delete(void* chunk, size_t size);
  86. private:
  87. using FragmentIndex = int;
  88. using VerticalAlignType = Style::VerticalAlign::Type;
  89. static constexpr FragmentIndex RootFragmentIndex = -1;
  90. struct Fragment {
  91. Fragment() = default;
  92. Fragment(InlineLevelBox* box, FragmentConstructor constructor, VerticalAlignType vertical_align, float position_x, FragmentIndex parent) :
  93. box(box), type(constructor.type), fragment_handle(constructor.fragment_handle), vertical_align(vertical_align), position(position_x, 0.f),
  94. layout_width(constructor.layout_width), parent(parent)
  95. {}
  96. InlineLevelBox* box = nullptr;
  97. FragmentType type = FragmentType::Invalid;
  98. LayoutFragmentHandle fragment_handle = {};
  99. VerticalAlignType vertical_align = {};
  100. // Layout state.
  101. Vector2f position; // Position relative to start of the line, disregarding floats, (x: outer-left edge, y: baseline).
  102. float layout_width = 0.f; // Inner width for inline boxes, otherwise outer width.
  103. // Vertical alignment state.
  104. FragmentIndex parent = RootFragmentIndex;
  105. FragmentIndex aligned_subtree_root = RootFragmentIndex; // Index of the aligned subtree the fragment belongs to.
  106. float baseline_offset = 0.f; // Vertical offset from aligned subtree root baseline to our baseline.
  107. // For inline boxes.
  108. bool split_left = false;
  109. bool split_right = false;
  110. bool has_content = false;
  111. FragmentIndex children_end_index = 0; // One-past-last-child of this box, as index into fragment list.
  112. // For aligned subtree root.
  113. float max_ascent = 0.f;
  114. float max_descent = 0.f;
  115. };
  116. using FragmentList = Vector<Fragment>;
  117. // Place an open fragment.
  118. void CloseFragment(Fragment& open_fragment, float right_inner_edge_position);
  119. // Splits the line, returning a new line if there are any open fragments.
  120. UniquePtr<LineBox> SplitLine(bool split_all_open_boxes);
  121. // Vertically align all descendants of the subtree. Returns the ascent of the top-most box, and descent of the bottom-most box.
  122. void VerticallyAlignSubtree(int subtree_root_index, int children_end_index, float& max_ascent, float& max_descent);
  123. // Returns true if the fragment establishes an aligned subtree.
  124. static bool IsAlignedSubtreeRoot(const Fragment& fragment)
  125. {
  126. return (fragment.vertical_align == VerticalAlignType::Top || fragment.vertical_align == VerticalAlignType::Center ||
  127. fragment.vertical_align == VerticalAlignType::Bottom);
  128. }
  129. // Returns the aligned subtree root for a given fragment, based on its ancestors.
  130. FragmentIndex DetermineAlignedSubtreeRoot(FragmentIndex index) const
  131. {
  132. while (index != RootFragmentIndex)
  133. {
  134. const Fragment& fragment = fragments[index];
  135. if (IsAlignedSubtreeRoot(fragment))
  136. return index;
  137. index = fragment.parent;
  138. }
  139. return index;
  140. }
  141. template <typename Func>
  142. void ForAllOpenFragments(Func&& func)
  143. {
  144. FragmentIndex index = open_fragments_leaf;
  145. while (index != RootFragmentIndex)
  146. {
  147. Fragment& fragment = fragments[index];
  148. index = fragment.parent;
  149. func(fragment);
  150. }
  151. }
  152. // Position of the line, relative to our block formatting context root.
  153. Vector2f line_position;
  154. // Available space for the line. Based on our parent box content width, possibly shrinked due to floating boxes.
  155. float line_width = 0.f;
  156. // Lower-bound estimate for the line box height.
  157. float line_minimum_height = 0.f;
  158. // The horizontal cursor. This is the outer-right position of the last placed fragment.
  159. float box_cursor = 0.f;
  160. // The contribution of opened inline boxes to the placement of the next fragment, due to their left edges (margin-border-padding).
  161. float open_spacing_left = 0.f;
  162. // List of fragments in this line box.
  163. FragmentList fragments;
  164. // Index of the last open fragment. The list of open fragments is this leaf and all of its ancestors up to the root.
  165. FragmentIndex open_fragments_leaf = RootFragmentIndex;
  166. bool has_content = false;
  167. bool is_vertically_positioned = false;
  168. bool is_closed = false;
  169. // Content offset due to space distribution from 'text-align'. Available after close.
  170. float offset_horizontal_alignment = 0.f;
  171. // The line box's height above baseline. Available after close.
  172. float total_height_above_baseline = 0.f;
  173. };
  174. } // namespace Rml
  175. #endif