InlineLevelIterator.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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. .should_force_break = m_text_node_context->do_respect_linebreaks && chunk.has_breaking_newline,
  55. };
  56. return item;
  57. }
  58. if (is<Layout::BreakNode>(*m_current_node)) {
  59. skip_to_next();
  60. return Item {
  61. .type = Item::Type::ForcedBreak,
  62. };
  63. }
  64. if (!is<Layout::Box>(*m_current_node)) {
  65. skip_to_next();
  66. return next(available_width);
  67. }
  68. if (is<Layout::ReplacedBox>(*m_current_node)) {
  69. auto& replaced_box = static_cast<Layout::ReplacedBox&>(*m_current_node);
  70. replaced_box.prepare_for_replaced_layout();
  71. }
  72. auto& box = verify_cast<Layout::Box>(*m_current_node);
  73. skip_to_next();
  74. return Item {
  75. .type = Item::Type::Element,
  76. .node = &box,
  77. .offset_in_node = 0,
  78. .length_in_node = 0,
  79. .width = box.width(),
  80. };
  81. }
  82. void InlineLevelIterator::enter_text_node(Layout::TextNode& text_node)
  83. {
  84. bool do_collapse = true;
  85. bool do_wrap_lines = true;
  86. bool do_respect_linebreaks = false;
  87. if (text_node.computed_values().white_space() == CSS::WhiteSpace::Nowrap) {
  88. do_collapse = true;
  89. do_wrap_lines = false;
  90. do_respect_linebreaks = false;
  91. } else if (text_node.computed_values().white_space() == CSS::WhiteSpace::Pre) {
  92. do_collapse = false;
  93. do_wrap_lines = false;
  94. do_respect_linebreaks = true;
  95. } else if (text_node.computed_values().white_space() == CSS::WhiteSpace::PreLine) {
  96. do_collapse = true;
  97. do_wrap_lines = true;
  98. do_respect_linebreaks = true;
  99. } else if (text_node.computed_values().white_space() == CSS::WhiteSpace::PreWrap) {
  100. do_collapse = false;
  101. do_wrap_lines = true;
  102. do_respect_linebreaks = true;
  103. }
  104. // FIXME: Pass the correct value for the last boolean!
  105. text_node.compute_text_for_rendering(do_collapse, true);
  106. m_text_node_context = TextNodeContext {
  107. .do_collapse = do_collapse,
  108. .do_wrap_lines = do_wrap_lines,
  109. .do_respect_linebreaks = do_respect_linebreaks,
  110. .chunk_iterator = TextNode::ChunkIterator { text_node.text_for_rendering(), m_layout_mode, do_wrap_lines, do_respect_linebreaks },
  111. };
  112. }
  113. }