InlineLevelIterator.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /*
  2. * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/Layout/BreakNode.h>
  7. #include <LibWeb/Layout/InlineLevelIterator.h>
  8. #include <LibWeb/Layout/InlineNode.h>
  9. #include <LibWeb/Layout/ReplacedBox.h>
  10. namespace Web::Layout {
  11. // This is similar to Layout::Node::next_in_pre_order() but will not descend into inline-block nodes.
  12. static Layout::Node* next_inline_node_in_pre_order(Layout::Node& current, Layout::Node const* stay_within)
  13. {
  14. if (current.first_child() && current.first_child()->is_inline() && !current.is_inline_block())
  15. return current.first_child();
  16. Layout::Node* node = &current;
  17. Layout::Node* next = nullptr;
  18. while (!(next = node->next_sibling())) {
  19. node = node->parent();
  20. if (!node || node == stay_within)
  21. return nullptr;
  22. }
  23. return next;
  24. }
  25. void InlineLevelIterator::skip_to_next()
  26. {
  27. VERIFY(m_current_node);
  28. do {
  29. m_current_node = next_inline_node_in_pre_order(*m_current_node, &m_container);
  30. } while (m_current_node && !m_current_node->is_inline());
  31. }
  32. Optional<InlineLevelIterator::Item> InlineLevelIterator::next(float available_width)
  33. {
  34. if (!m_current_node)
  35. return {};
  36. if (is<Layout::TextNode>(*m_current_node)) {
  37. auto& text_node = static_cast<Layout::TextNode&>(*m_current_node);
  38. if (!m_text_node_context.has_value())
  39. enter_text_node(text_node);
  40. auto chunk_opt = m_text_node_context->chunk_iterator.next();
  41. if (!chunk_opt.has_value()) {
  42. m_text_node_context = {};
  43. skip_to_next();
  44. return next(available_width);
  45. }
  46. auto& chunk = chunk_opt.value();
  47. float chunk_width = text_node.font().width(chunk.view) + text_node.font().glyph_spacing();
  48. Item item {
  49. .type = Item::Type::Text,
  50. .node = &text_node,
  51. .offset_in_node = chunk.start,
  52. .length_in_node = chunk.length,
  53. .width = chunk_width,
  54. };
  55. return item;
  56. }
  57. if (is<Layout::BreakNode>(*m_current_node)) {
  58. skip_to_next();
  59. return Item {
  60. .type = Item::Type::ForcedBreak,
  61. };
  62. }
  63. if (!is<Layout::Box>(*m_current_node)) {
  64. skip_to_next();
  65. return next(available_width);
  66. }
  67. if (is<Layout::ReplacedBox>(*m_current_node)) {
  68. auto& replaced_box = static_cast<Layout::ReplacedBox&>(*m_current_node);
  69. replaced_box.prepare_for_replaced_layout();
  70. }
  71. auto& box = verify_cast<Layout::Box>(*m_current_node);
  72. skip_to_next();
  73. return Item {
  74. .type = Item::Type::Element,
  75. .node = &box,
  76. .offset_in_node = 0,
  77. .length_in_node = 0,
  78. .width = box.width(),
  79. };
  80. }
  81. void InlineLevelIterator::enter_text_node(Layout::TextNode& text_node)
  82. {
  83. bool do_collapse = true;
  84. bool do_wrap_lines = true;
  85. bool do_respect_linebreaks = false;
  86. if (text_node.computed_values().white_space() == CSS::WhiteSpace::Nowrap) {
  87. do_collapse = true;
  88. do_wrap_lines = false;
  89. do_respect_linebreaks = false;
  90. } else if (text_node.computed_values().white_space() == CSS::WhiteSpace::Pre) {
  91. do_collapse = false;
  92. do_wrap_lines = false;
  93. do_respect_linebreaks = true;
  94. } else if (text_node.computed_values().white_space() == CSS::WhiteSpace::PreLine) {
  95. do_collapse = true;
  96. do_wrap_lines = true;
  97. do_respect_linebreaks = true;
  98. } else if (text_node.computed_values().white_space() == CSS::WhiteSpace::PreWrap) {
  99. do_collapse = false;
  100. do_wrap_lines = true;
  101. do_respect_linebreaks = true;
  102. }
  103. // FIXME: Pass the correct value for the last boolean!
  104. text_node.compute_text_for_rendering(do_collapse, true);
  105. m_text_node_context = TextNodeContext {
  106. .do_collapse = do_collapse,
  107. .do_wrap_lines = do_wrap_lines,
  108. .do_respect_linebreaks = do_respect_linebreaks,
  109. .chunk_iterator = TextNode::ChunkIterator { text_node.text_for_rendering(), m_layout_mode, do_wrap_lines, do_respect_linebreaks },
  110. };
  111. }
  112. }