mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 16:18:12 +00:00
LibWeb: Add inline-level iterator that enumerates items for line layout
This patch adds a new mechanism that allows InlineFormattingContext to build line boxes incrementally instead of all-in-one go. Incremental build will eventually allow much better support for CSS floating objects.
This commit is contained in:
parent
9358f108c4
commit
1f603c54ff
6 changed files with 248 additions and 13 deletions
117
Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp
Normal file
117
Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Layout/BreakNode.h>
|
||||
#include <LibWeb/Layout/InlineLevelIterator.h>
|
||||
#include <LibWeb/Layout/InlineNode.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
void InlineLevelIterator::skip_to_next()
|
||||
{
|
||||
VERIFY(m_current_node);
|
||||
do {
|
||||
m_current_node = m_current_node->next_in_pre_order(&m_container);
|
||||
} while (m_current_node && !m_current_node->is_inline());
|
||||
}
|
||||
|
||||
Optional<InlineLevelIterator::Item> InlineLevelIterator::next(float available_width)
|
||||
{
|
||||
if (!m_current_node)
|
||||
return {};
|
||||
|
||||
if (is<Layout::TextNode>(*m_current_node)) {
|
||||
auto& text_node = static_cast<Layout::TextNode&>(*m_current_node);
|
||||
|
||||
if (!m_text_node_context.has_value())
|
||||
enter_text_node(text_node);
|
||||
|
||||
auto chunk_opt = m_text_node_context->chunk_iterator.next();
|
||||
if (!chunk_opt.has_value()) {
|
||||
m_text_node_context = {};
|
||||
skip_to_next();
|
||||
return next(available_width);
|
||||
}
|
||||
|
||||
auto& chunk = chunk_opt.value();
|
||||
float chunk_width = text_node.font().width(chunk.view) + text_node.font().glyph_spacing();
|
||||
Item item {
|
||||
.type = Item::Type::Text,
|
||||
.node = &text_node,
|
||||
.offset_in_node = chunk.start,
|
||||
.length_in_node = chunk.length,
|
||||
.width = chunk_width,
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
if (is<Layout::BreakNode>(*m_current_node)) {
|
||||
skip_to_next();
|
||||
return Item {
|
||||
.type = Item::Type::ForcedBreak,
|
||||
};
|
||||
}
|
||||
|
||||
if (!is<Layout::Box>(*m_current_node)) {
|
||||
skip_to_next();
|
||||
return next(available_width);
|
||||
}
|
||||
|
||||
if (is<Layout::ReplacedBox>(*m_current_node)) {
|
||||
auto& replaced_box = static_cast<Layout::ReplacedBox&>(*m_current_node);
|
||||
replaced_box.prepare_for_replaced_layout();
|
||||
}
|
||||
|
||||
auto& box = verify_cast<Layout::Box>(*m_current_node);
|
||||
|
||||
skip_to_next();
|
||||
return Item {
|
||||
.type = Item::Type::Element,
|
||||
.node = &box,
|
||||
.offset_in_node = 0,
|
||||
.length_in_node = 0,
|
||||
.width = box.width(),
|
||||
};
|
||||
}
|
||||
|
||||
void InlineLevelIterator::enter_text_node(Layout::TextNode& text_node)
|
||||
{
|
||||
bool do_collapse = true;
|
||||
bool do_wrap_lines = true;
|
||||
bool do_respect_linebreaks = false;
|
||||
|
||||
if (text_node.computed_values().white_space() == CSS::WhiteSpace::Nowrap) {
|
||||
do_collapse = true;
|
||||
do_wrap_lines = false;
|
||||
do_respect_linebreaks = false;
|
||||
} else if (text_node.computed_values().white_space() == CSS::WhiteSpace::Pre) {
|
||||
do_collapse = false;
|
||||
do_wrap_lines = false;
|
||||
do_respect_linebreaks = true;
|
||||
} else if (text_node.computed_values().white_space() == CSS::WhiteSpace::PreLine) {
|
||||
do_collapse = true;
|
||||
do_wrap_lines = true;
|
||||
do_respect_linebreaks = true;
|
||||
} else if (text_node.computed_values().white_space() == CSS::WhiteSpace::PreWrap) {
|
||||
do_collapse = false;
|
||||
do_wrap_lines = true;
|
||||
do_respect_linebreaks = true;
|
||||
}
|
||||
|
||||
// FIXME: Pass the correct value for the last boolean!
|
||||
text_node.compute_text_for_rendering(do_collapse, true);
|
||||
|
||||
m_text_node_context = TextNodeContext {
|
||||
.do_collapse = do_collapse,
|
||||
.do_wrap_lines = do_wrap_lines,
|
||||
.do_respect_linebreaks = do_respect_linebreaks,
|
||||
.chunk_iterator = TextNode::ChunkIterator { text_node.text_for_rendering(), m_layout_mode, do_wrap_lines, do_respect_linebreaks },
|
||||
};
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue