InlineBox.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #include "InlineBox.h"
  2. #include "../../../Include/RmlUi/Core/Box.h"
  3. #include "../../../Include/RmlUi/Core/Element.h"
  4. #include "../../../Include/RmlUi/Core/FontMetrics.h"
  5. namespace Rml {
  6. static void ZeroBoxEdge(Box& box, BoxEdge edge)
  7. {
  8. box.SetEdge(BoxArea::Padding, edge, 0.f);
  9. box.SetEdge(BoxArea::Border, edge, 0.f);
  10. box.SetEdge(BoxArea::Margin, edge, 0.f);
  11. }
  12. InlineLevelBox* InlineBoxBase::AddChild(UniquePtr<InlineLevelBox> child)
  13. {
  14. auto result = child.get();
  15. children.push_back(std::move(child));
  16. return result;
  17. }
  18. void InlineBoxBase::GetStrut(float& out_total_height_above, float& out_total_depth_below) const
  19. {
  20. const FontMetrics& font_metrics = GetFontMetrics();
  21. const float line_height = GetElement()->GetLineHeight();
  22. const float half_leading = 0.5f * (line_height - (font_metrics.ascent + font_metrics.descent));
  23. out_total_height_above = font_metrics.ascent + half_leading;
  24. out_total_depth_below = line_height - out_total_height_above;
  25. }
  26. String InlineBoxBase::DebugDumpTree(int depth) const
  27. {
  28. String value = InlineLevelBox::DebugDumpTree(depth);
  29. for (auto&& child : children)
  30. value += child->DebugDumpTree(depth + 1);
  31. return value;
  32. }
  33. InlineBoxBase::InlineBoxBase(Element* element) : InlineLevelBox(element) {}
  34. InlineBoxRoot::InlineBoxRoot(Element* element) : InlineBoxBase(element)
  35. {
  36. float height_above_baseline, depth_below_baseline;
  37. GetStrut(height_above_baseline, depth_below_baseline);
  38. SetHeight(height_above_baseline, depth_below_baseline);
  39. }
  40. FragmentConstructor InlineBoxRoot::CreateFragment(InlineLayoutMode /*mode*/, float /*available_width*/, float /*right_spacing_width*/,
  41. bool /*first_box*/, LayoutOverflowHandle /*overflow_handle*/)
  42. {
  43. RMLUI_ERROR;
  44. return {};
  45. }
  46. void InlineBoxRoot::Submit(const PlacedFragment& /*placed_fragment*/)
  47. {
  48. RMLUI_ERROR;
  49. }
  50. InlineBox::InlineBox(const InlineLevelBox* parent, Element* element, const Box& _box) : InlineBoxBase(element), box(_box)
  51. {
  52. RMLUI_ASSERT(box.GetSize().x < 0.f && box.GetSize().y < 0.f);
  53. const FontMetrics& font_metrics = GetFontMetrics();
  54. // The inline box content height does not depend on the 'line-height' property, only on the font, and is not exactly
  55. // specified by CSS. Here we choose to size the content height equal to the default line-height for the font-size.
  56. const float inner_height = 1.2f * (float)font_metrics.size;
  57. box.SetContent(Vector2f(-1.f, inner_height));
  58. float height_above_baseline, depth_below_baseline;
  59. GetStrut(height_above_baseline, depth_below_baseline);
  60. SetHeightAndVerticalAlignment(height_above_baseline, depth_below_baseline, parent);
  61. const float edge_left = box.GetCumulativeEdge(BoxArea::Padding, BoxEdge::Left);
  62. const float edge_right = box.GetCumulativeEdge(BoxArea::Padding, BoxEdge::Right);
  63. SetInlineBoxSpacing(edge_left, edge_right);
  64. // Vertically position the box so that its content box is equally spaced around its font ascent and descent metrics.
  65. const float half_leading = 0.5f * (inner_height - (font_metrics.ascent + font_metrics.descent));
  66. baseline_to_border_height =
  67. font_metrics.ascent + half_leading + box.GetEdge(BoxArea::Border, BoxEdge::Top) + box.GetEdge(BoxArea::Padding, BoxEdge::Top);
  68. }
  69. FragmentConstructor InlineBox::CreateFragment(InlineLayoutMode mode, float available_width, float right_spacing_width, bool /*first_box*/,
  70. LayoutOverflowHandle /*overflow_handle*/)
  71. {
  72. if (mode != InlineLayoutMode::WrapAny || right_spacing_width <= available_width + GetSpacingLeft())
  73. return FragmentConstructor{FragmentType::InlineBox, -1.f, {}, {}};
  74. return {};
  75. }
  76. void InlineBox::Submit(const PlacedFragment& placed_fragment)
  77. {
  78. Element* element = GetElement();
  79. RMLUI_ASSERT(element && element != placed_fragment.offset_parent);
  80. Box element_box = box;
  81. element_box.SetContent({placed_fragment.layout_width, element_box.GetSize().y});
  82. if (placed_fragment.split_left)
  83. ZeroBoxEdge(element_box, BoxEdge::Left);
  84. if (placed_fragment.split_right)
  85. ZeroBoxEdge(element_box, BoxEdge::Right);
  86. // In inline layout, fragments are positioned in terms of (x: margin edge, y: baseline), while element offsets are
  87. // specified relative to their border box. Thus, find the offset from the fragment position to the border edge.
  88. const Vector2f border_position =
  89. placed_fragment.position + Vector2f{element_box.GetEdge(BoxArea::Margin, BoxEdge::Left), -baseline_to_border_height};
  90. // We can determine the principal fragment based on its left split: Only the principal one has its left side intact,
  91. // subsequent fragments have their left side split.
  92. const bool principal_box = !placed_fragment.split_left;
  93. if (principal_box)
  94. {
  95. element_offset = border_position;
  96. element->SetOffset(border_position, placed_fragment.offset_parent);
  97. element->SetBox(element_box);
  98. SubmitElementOnLayout();
  99. }
  100. else
  101. {
  102. element->AddBox(element_box, border_position - element_offset);
  103. }
  104. }
  105. } // namespace Rml