LibWeb: First slightly naive implementation of CSS floats :^)
Boxes can now be floated left or right, which makes text within the same block formatting context flow around them. We were creating way too many block formatting contexts. As it turns out, we don't need one for every new block, but rather there's a set of rules that determines whether a given block creates a new block formatting context. Each BFC keeps track of the floating boxes within it, and IFC's can then query it to find the available space for line boxes. There's a huge hack in here where we assume all lines are the exact line-height. Making this work with vertically non-uniform lines will require some architectural changes.
This commit is contained in:
parent
11256de366
commit
615a4d4f71
Notes:
sideshowbarker
2024-07-19 01:02:59 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/615a4d4f71d
18 changed files with 209 additions and 41 deletions
|
@ -30,6 +30,7 @@
|
|||
#include <LibWeb/Dump.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/InitialContainingBlockBox.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/InlineNode.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
|
@ -107,10 +108,14 @@ HitTestResult BlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type
|
|||
return { absolute_rect().contains(position.x(), position.y()) ? this : nullptr };
|
||||
}
|
||||
|
||||
void BlockBox::split_into_lines(BlockBox& container, LayoutMode layout_mode)
|
||||
void BlockBox::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
|
||||
{
|
||||
auto& container = context.context_box();
|
||||
auto* line_box = &container.ensure_last_line_box();
|
||||
if (layout_mode != LayoutMode::OnlyRequiredLineBreaks && line_box->width() > 0 && line_box->width() + width() > container.width()) {
|
||||
|
||||
float available_width = context.available_width_at_line(container.line_boxes().size());
|
||||
|
||||
if (layout_mode != LayoutMode::OnlyRequiredLineBreaks && line_box->width() > 0 && line_box->width() + width() > available_width) {
|
||||
line_box = &container.add_line_box();
|
||||
}
|
||||
line_box->add_fragment(*this, 0, 0, width(), height());
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
template<typename Callback>
|
||||
void for_each_fragment(Callback) const;
|
||||
|
||||
virtual void split_into_lines(BlockBox& container, LayoutMode) override;
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
|
||||
private:
|
||||
virtual bool is_block() const override { return true; }
|
||||
|
|
|
@ -65,6 +65,8 @@ void BlockFormattingContext::run(LayoutMode layout_mode)
|
|||
if (layout_mode == LayoutMode::Default)
|
||||
compute_width(context_box());
|
||||
|
||||
layout_floating_descendants();
|
||||
|
||||
if (context_box().children_are_inline()) {
|
||||
layout_inline_children(layout_mode);
|
||||
} else {
|
||||
|
@ -403,7 +405,7 @@ void BlockFormattingContext::layout_block_level_children(LayoutMode layout_mode)
|
|||
float content_width = 0;
|
||||
|
||||
context_box().for_each_in_subtree_of_type<Box>([&](auto& box) {
|
||||
if (box.is_absolutely_positioned() || box.containing_block() != &context_box())
|
||||
if (box.is_absolutely_positioned() || box.is_floating() || box.containing_block() != &context_box())
|
||||
return IterationDecision::Continue;
|
||||
|
||||
compute_width(box);
|
||||
|
@ -530,6 +532,8 @@ void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_m
|
|||
|
||||
icb.set_width(viewport_rect.width());
|
||||
|
||||
layout_floating_descendants();
|
||||
|
||||
layout_block_level_children(layout_mode);
|
||||
|
||||
ASSERT(!icb.children_are_inline());
|
||||
|
@ -564,6 +568,46 @@ void BlockFormattingContext::layout_absolutely_positioned_descendants()
|
|||
});
|
||||
}
|
||||
|
||||
void BlockFormattingContext::layout_floating_descendants()
|
||||
{
|
||||
context_box().for_each_in_subtree_of_type<Box>([&](auto& box) {
|
||||
if (box.is_floating() && box.containing_block() == &context_box()) {
|
||||
layout_floating_descendant(box);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void BlockFormattingContext::layout_floating_descendant(Box& box)
|
||||
{
|
||||
ASSERT(box.is_floating());
|
||||
auto& containing_block = context_box();
|
||||
|
||||
compute_width(box);
|
||||
layout_inside(box, LayoutMode::Default);
|
||||
compute_height(box);
|
||||
|
||||
if (box.style().float_() == CSS::Float::Left) {
|
||||
float x = 0;
|
||||
if (!m_left_floating_boxes.is_empty()) {
|
||||
auto& previous_floating_box = *m_left_floating_boxes.last();
|
||||
x = previous_floating_box.effective_offset().x() + previous_floating_box.width();
|
||||
}
|
||||
box.set_offset(x, 0);
|
||||
m_left_floating_boxes.append(&box);
|
||||
} else if (box.style().float_() == CSS::Float::Right) {
|
||||
float x = 0;
|
||||
if (!m_right_floating_boxes.is_empty()) {
|
||||
auto& previous_floating_box = *m_right_floating_boxes.last();
|
||||
x = previous_floating_box.effective_offset().x() - box.width();
|
||||
} else {
|
||||
x = containing_block.width() - box.width();
|
||||
}
|
||||
box.set_offset(x, 0);
|
||||
m_right_floating_boxes.append(&box);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockFormattingContext::layout_absolutely_positioned_descendant(Box& box)
|
||||
{
|
||||
auto& containing_block = context_box();
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/Layout/FormattingContext.h>
|
||||
|
||||
|
@ -40,22 +41,32 @@ public:
|
|||
|
||||
bool is_initial() const;
|
||||
|
||||
const Vector<Box*>& left_floating_boxes() const { return m_left_floating_boxes; }
|
||||
const Vector<Box*>& right_floating_boxes() const { return m_right_floating_boxes; }
|
||||
|
||||
protected:
|
||||
void compute_width(Box&);
|
||||
void compute_height(Box&);
|
||||
|
||||
private:
|
||||
virtual bool is_block_formatting_context() const final { return true; }
|
||||
|
||||
void compute_width_for_absolutely_positioned_block(Box&);
|
||||
|
||||
void layout_initial_containing_block(LayoutMode);
|
||||
void layout_block_level_children(LayoutMode);
|
||||
void layout_inline_children(LayoutMode);
|
||||
void layout_absolutely_positioned_descendants();
|
||||
void layout_floating_descendants();
|
||||
|
||||
void place_block_level_replaced_element_in_normal_flow(Box&);
|
||||
void place_block_level_non_replaced_element_in_normal_flow(Box&);
|
||||
|
||||
void layout_absolutely_positioned_descendant(Box&);
|
||||
void layout_floating_descendant(Box&);
|
||||
|
||||
Vector<Box*> m_left_floating_boxes;
|
||||
Vector<Box*> m_right_floating_boxes;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/BreakNode.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
|
@ -39,9 +40,9 @@ BreakNode::~BreakNode()
|
|||
{
|
||||
}
|
||||
|
||||
void BreakNode::split_into_lines(BlockBox& block, LayoutMode)
|
||||
void BreakNode::split_into_lines(InlineFormattingContext& block, LayoutMode)
|
||||
{
|
||||
block.add_line_box();
|
||||
block.context_box().add_line_box();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public:
|
|||
private:
|
||||
virtual bool is_break() const override { return true; }
|
||||
virtual const char* class_name() const override { return "BreakNode"; }
|
||||
virtual void split_into_lines(BlockBox&, LayoutMode) override;
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <LibWeb/Dump.h>
|
||||
#include <LibWeb/Layout/BlockFormattingContext.h>
|
||||
#include <LibWeb/Layout/Box.h>
|
||||
#include <LibWeb/Layout/FormattingContext.h>
|
||||
|
@ -34,7 +35,7 @@ namespace Web::Layout {
|
|||
|
||||
FormattingContext::FormattingContext(Box& context_box, FormattingContext* parent)
|
||||
: m_parent(parent)
|
||||
, m_context_box(context_box)
|
||||
, m_context_box(&context_box)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -42,8 +43,37 @@ FormattingContext::~FormattingContext()
|
|||
{
|
||||
}
|
||||
|
||||
bool FormattingContext::creates_block_formatting_context(const Box& box)
|
||||
{
|
||||
if (box.is_root_element())
|
||||
return true;
|
||||
if (box.is_floating())
|
||||
return true;
|
||||
if (box.is_absolutely_positioned())
|
||||
return true;
|
||||
if (box.is_inline_block())
|
||||
return true;
|
||||
if (box.is_table_cell())
|
||||
return true;
|
||||
// FIXME: table-caption
|
||||
// FIXME: anonymous table cells
|
||||
// FIXME: Block elements where overflow has a value other than visible and clip.
|
||||
// FIXME: display: flow-root
|
||||
// FIXME: Elements with contain: layout, content, or paint.
|
||||
// FIXME: flex
|
||||
// FIXME: grid
|
||||
// FIXME: multicol
|
||||
// FIXME: column-span: all
|
||||
return false;
|
||||
}
|
||||
|
||||
void FormattingContext::layout_inside(Box& box, LayoutMode layout_mode)
|
||||
{
|
||||
if (creates_block_formatting_context(box)) {
|
||||
BlockFormattingContext context(box, this);
|
||||
context.run(layout_mode);
|
||||
return;
|
||||
}
|
||||
if (box.is_table()) {
|
||||
TableFormattingContext context(box, this);
|
||||
context.run(layout_mode);
|
||||
|
@ -51,8 +81,12 @@ void FormattingContext::layout_inside(Box& box, LayoutMode layout_mode)
|
|||
InlineFormattingContext context(box, this);
|
||||
context.run(layout_mode);
|
||||
} else {
|
||||
BlockFormattingContext context(box, this);
|
||||
context.run(layout_mode);
|
||||
// FIXME: This needs refactoring!
|
||||
ASSERT(is_block_formatting_context());
|
||||
auto& old_box = context_box();
|
||||
set_context_box(box);
|
||||
run(layout_mode);
|
||||
set_context_box(old_box);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,16 +34,22 @@ class FormattingContext {
|
|||
public:
|
||||
virtual void run(LayoutMode) = 0;
|
||||
|
||||
Box& context_box() { return m_context_box; }
|
||||
const Box& context_box() const { return m_context_box; }
|
||||
Box& context_box() { return *m_context_box; }
|
||||
const Box& context_box() const { return *m_context_box; }
|
||||
|
||||
FormattingContext* parent() { return m_parent; }
|
||||
const FormattingContext* parent() const { return m_parent; }
|
||||
|
||||
virtual bool is_block_formatting_context() const { return false; }
|
||||
|
||||
static bool creates_block_formatting_context(const Box&);
|
||||
|
||||
protected:
|
||||
FormattingContext(Box&, FormattingContext* parent = nullptr);
|
||||
virtual ~FormattingContext();
|
||||
|
||||
void set_context_box(Box& box) { m_context_box = &box; }
|
||||
|
||||
void layout_inside(Box&, LayoutMode);
|
||||
|
||||
struct ShrinkToFitResult {
|
||||
|
@ -54,7 +60,7 @@ protected:
|
|||
ShrinkToFitResult calculate_shrink_to_fit_widths(Box&);
|
||||
|
||||
FormattingContext* m_parent { nullptr };
|
||||
Box& m_context_box;
|
||||
Box* m_context_box { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -45,6 +45,50 @@ InlineFormattingContext::~InlineFormattingContext()
|
|||
{
|
||||
}
|
||||
|
||||
struct AvailableSpaceForLineInfo {
|
||||
float left { 0 };
|
||||
float right { 0 };
|
||||
};
|
||||
|
||||
static AvailableSpaceForLineInfo available_space_for_line(const InlineFormattingContext& context, size_t line_index)
|
||||
{
|
||||
AvailableSpaceForLineInfo info;
|
||||
|
||||
// FIXME: This is a total hack guess since we don't actually know the final y position of lines here!
|
||||
float line_height = context.context_box().specified_style().line_height(context.context_box());
|
||||
float y = (line_index * line_height) + line_height / 2;
|
||||
|
||||
auto& bfc = static_cast<const BlockFormattingContext&>(*context.parent());
|
||||
|
||||
for (ssize_t i = bfc.left_floating_boxes().size() - 1; i >= 0; --i) {
|
||||
auto& floating_box = *bfc.left_floating_boxes().at(i);
|
||||
Gfx::FloatRect rect { floating_box.effective_offset(), floating_box.size() };
|
||||
if (rect.contains_vertically(y)) {
|
||||
info.left = rect.right() + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
info.right = context.context_box().width();
|
||||
|
||||
for (ssize_t i = bfc.right_floating_boxes().size() - 1; i >= 0; --i) {
|
||||
auto& floating_box = *bfc.right_floating_boxes().at(i);
|
||||
Gfx::FloatRect rect { floating_box.effective_offset(), floating_box.size() };
|
||||
if (rect.contains_vertically(y)) {
|
||||
info.right = rect.left() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
float InlineFormattingContext::available_width_at_line(size_t line_index) const
|
||||
{
|
||||
auto info = available_space_for_line(*this, line_index);
|
||||
return info.right - info.left;
|
||||
}
|
||||
|
||||
void InlineFormattingContext::run(LayoutMode layout_mode)
|
||||
{
|
||||
auto& containing_block = downcast<BlockBox>(context_box());
|
||||
|
@ -56,7 +100,7 @@ void InlineFormattingContext::run(LayoutMode layout_mode)
|
|||
if (child.is_absolutely_positioned())
|
||||
return;
|
||||
|
||||
child.split_into_lines(containing_block, layout_mode);
|
||||
child.split_into_lines(*this, layout_mode);
|
||||
});
|
||||
|
||||
for (auto& line_box : containing_block.line_boxes()) {
|
||||
|
@ -73,13 +117,15 @@ void InlineFormattingContext::run(LayoutMode layout_mode)
|
|||
float content_height = 0;
|
||||
float max_linebox_width = 0;
|
||||
|
||||
for (auto& line_box : containing_block.line_boxes()) {
|
||||
for (size_t line_index = 0; line_index < containing_block.line_boxes().size(); ++line_index) {
|
||||
auto& line_box = containing_block.line_boxes()[line_index];
|
||||
float max_height = min_line_height;
|
||||
for (auto& fragment : line_box.fragments()) {
|
||||
max_height = max(max_height, fragment.height());
|
||||
}
|
||||
|
||||
float x_offset = 0;
|
||||
float x_offset = available_space_for_line(*this, line_index).left;
|
||||
|
||||
float excess_horizontal_space = (float)containing_block.width() - line_box.width();
|
||||
|
||||
switch (text_align) {
|
||||
|
|
|
@ -38,6 +38,8 @@ public:
|
|||
|
||||
virtual void run(LayoutMode) override;
|
||||
|
||||
float available_width_at_line(size_t line_index) const;
|
||||
|
||||
private:
|
||||
void dimension_box_on_line(Box&, LayoutMode);
|
||||
};
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <LibGfx/Painter.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/InlineNode.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
@ -41,14 +42,16 @@ InlineNode::~InlineNode()
|
|||
{
|
||||
}
|
||||
|
||||
void InlineNode::split_into_lines(BlockBox& containing_block, LayoutMode layout_mode)
|
||||
void InlineNode::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
|
||||
{
|
||||
auto& containing_block = context.context_box();
|
||||
|
||||
if (!style().padding().left.is_undefined_or_auto()) {
|
||||
float padding_left = style().padding().left.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this);
|
||||
containing_block.ensure_last_line_box().add_fragment(*this, 0, 0, padding_left, 0, LineBoxFragment::Type::Leading);
|
||||
}
|
||||
|
||||
NodeWithStyleAndBoxModelMetrics::split_into_lines(containing_block, layout_mode);
|
||||
NodeWithStyleAndBoxModelMetrics::split_into_lines(context, layout_mode);
|
||||
|
||||
if (!style().padding().right.is_undefined_or_auto()) {
|
||||
float padding_right = style().padding().right.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this);
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
|
||||
virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const override;
|
||||
|
||||
virtual void split_into_lines(BlockBox& containing_block, LayoutMode) override;
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
|
||||
private:
|
||||
virtual bool is_inline_node() const final { return true; }
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <LibWeb/Dump.h>
|
||||
#include <LibWeb/HTML/HTMLHtmlElement.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/FormattingContext.h>
|
||||
#include <LibWeb/Layout/InitialContainingBlockBox.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
@ -65,6 +66,13 @@ const BlockBox* Node::containing_block() const
|
|||
return downcast<BlockBox>(ancestor);
|
||||
};
|
||||
|
||||
auto nearest_block_ancestor_that_creates_a_block_formatting_context = [this] {
|
||||
auto* ancestor = parent();
|
||||
while (ancestor && (!is<Box>(*ancestor) || !FormattingContext::creates_block_formatting_context(downcast<Box>(*ancestor))))
|
||||
ancestor = ancestor->parent();
|
||||
return downcast<BlockBox>(ancestor);
|
||||
};
|
||||
|
||||
if (is_text())
|
||||
return nearest_block_ancestor();
|
||||
|
||||
|
@ -82,6 +90,9 @@ const BlockBox* Node::containing_block() const
|
|||
if (position == CSS::Position::Fixed)
|
||||
return &root();
|
||||
|
||||
if (is_floating())
|
||||
return nearest_block_ancestor_that_creates_a_block_formatting_context();
|
||||
|
||||
return nearest_block_ancestor();
|
||||
}
|
||||
|
||||
|
@ -140,10 +151,10 @@ InitialContainingBlockBox& Node::root()
|
|||
return *document().layout_node();
|
||||
}
|
||||
|
||||
void Node::split_into_lines(BlockBox& container, LayoutMode layout_mode)
|
||||
void Node::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
|
||||
{
|
||||
for_each_child([&](auto& child) {
|
||||
child.split_into_lines(container, layout_mode);
|
||||
child.split_into_lines(context, layout_mode);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ public:
|
|||
void removed_from(Node&) { }
|
||||
void children_changed() { }
|
||||
|
||||
virtual void split_into_lines(BlockBox& container, LayoutMode);
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode);
|
||||
|
||||
bool is_visible() const { return m_visible; }
|
||||
void set_visible(bool visible) { m_visible = visible; }
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
||||
|
@ -117,17 +118,19 @@ float ReplacedBox::calculate_height() const
|
|||
return used_height;
|
||||
}
|
||||
|
||||
void ReplacedBox::split_into_lines(Layout::BlockBox& container, LayoutMode)
|
||||
void ReplacedBox::split_into_lines(InlineFormattingContext& context, LayoutMode)
|
||||
{
|
||||
auto& containing_block = context.context_box();
|
||||
|
||||
// FIXME: This feels out of place. It would be nice if someone at a higher level
|
||||
// made sure we had usable geometry by the time we start splitting.
|
||||
prepare_for_replaced_layout();
|
||||
auto width = calculate_width();
|
||||
auto height = calculate_height();
|
||||
|
||||
auto* line_box = &container.ensure_last_line_box();
|
||||
if (line_box->width() > 0 && line_box->width() + width > container.width())
|
||||
line_box = &container.add_line_box();
|
||||
auto* line_box = &containing_block.ensure_last_line_box();
|
||||
if (line_box->width() > 0 && line_box->width() + width > context.available_width_at_line(containing_block.line_boxes().size()))
|
||||
line_box = &containing_block.add_line_box();
|
||||
line_box->add_fragment(*this, 0, 0, width, height);
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
virtual bool can_have_children() const override { return false; }
|
||||
|
||||
protected:
|
||||
virtual void split_into_lines(Layout::BlockBox& container, LayoutMode) override;
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "ReplacedBox"; }
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <LibGfx/Font.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
#include <ctype.h>
|
||||
|
@ -189,14 +190,15 @@ void TextNode::for_each_chunk(Callback callback, LayoutMode layout_mode, bool do
|
|||
commit_chunk(view.end(), false, true);
|
||||
}
|
||||
|
||||
void TextNode::split_into_lines_by_rules(BlockBox& container, LayoutMode layout_mode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks)
|
||||
void TextNode::split_into_lines_by_rules(InlineFormattingContext& context, LayoutMode layout_mode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks)
|
||||
{
|
||||
auto& containing_block = context.context_box();
|
||||
auto& font = specified_style().font();
|
||||
float space_width = font.glyph_width(' ') + font.glyph_spacing();
|
||||
|
||||
auto& line_boxes = container.line_boxes();
|
||||
container.ensure_last_line_box();
|
||||
float available_width = container.width() - line_boxes.last().width();
|
||||
auto& line_boxes = containing_block.line_boxes();
|
||||
containing_block.ensure_last_line_box();
|
||||
float available_width = context.available_width_at_line(line_boxes.size()) - line_boxes.last().width();
|
||||
|
||||
// Collapse whitespace into single spaces
|
||||
if (do_collapse) {
|
||||
|
@ -261,8 +263,8 @@ void TextNode::split_into_lines_by_rules(BlockBox& container, LayoutMode layout_
|
|||
chunk_width = font.width(chunk.view) + font.glyph_spacing();
|
||||
|
||||
if (line_boxes.last().width() > 0 && chunk_width > available_width) {
|
||||
container.add_line_box();
|
||||
available_width = container.width();
|
||||
containing_block.add_line_box();
|
||||
available_width = context.available_width_at_line(line_boxes.size());
|
||||
}
|
||||
if (need_collapse & line_boxes.last().fragments().is_empty())
|
||||
continue;
|
||||
|
@ -275,21 +277,21 @@ void TextNode::split_into_lines_by_rules(BlockBox& container, LayoutMode layout_
|
|||
|
||||
if (do_wrap_lines) {
|
||||
if (available_width < 0) {
|
||||
container.add_line_box();
|
||||
available_width = container.width();
|
||||
containing_block.add_line_box();
|
||||
available_width = context.available_width_at_line(line_boxes.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (do_wrap_breaks) {
|
||||
if (chunk.is_break) {
|
||||
container.add_line_box();
|
||||
available_width = container.width();
|
||||
containing_block.add_line_box();
|
||||
available_width = context.available_width_at_line(line_boxes.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextNode::split_into_lines(BlockBox& container, LayoutMode layout_mode)
|
||||
void TextNode::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
|
||||
{
|
||||
bool do_collapse = true;
|
||||
bool do_wrap_lines = true;
|
||||
|
@ -313,7 +315,7 @@ void TextNode::split_into_lines(BlockBox& container, LayoutMode layout_mode)
|
|||
do_wrap_breaks = true;
|
||||
}
|
||||
|
||||
split_into_lines_by_rules(container, layout_mode, do_collapse, do_wrap_lines, do_wrap_breaks);
|
||||
split_into_lines_by_rules(context, layout_mode, do_collapse, do_wrap_lines, do_wrap_breaks);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -48,12 +48,12 @@ public:
|
|||
|
||||
virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const override;
|
||||
|
||||
virtual void split_into_lines(BlockBox& container, LayoutMode) override;
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
|
||||
const CSS::StyleProperties& specified_style() const { return parent()->specified_style(); }
|
||||
|
||||
private:
|
||||
void split_into_lines_by_rules(BlockBox& container, LayoutMode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks);
|
||||
void split_into_lines_by_rules(InlineFormattingContext&, LayoutMode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks);
|
||||
void paint_cursor_if_needed(PaintContext&, const LineBoxFragment&) const;
|
||||
|
||||
template<typename Callback>
|
||||
|
|
Loading…
Add table
Reference in a new issue