summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-12-05 20:10:39 +0100
committerAndreas Kling <kling@serenityos.org>2020-12-05 22:51:03 +0100
commit615a4d4f71db0750313ae8ac1494e65f67f25b31 (patch)
tree56547106c593617d5d517402056655643b3326f2 /Libraries
parent11256de366eb7d882e91ac46bcc535511d54fdde (diff)
downloadserenity-615a4d4f71db0750313ae8ac1494e65f67f25b31.zip
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.
Diffstat (limited to 'Libraries')
-rw-r--r--Libraries/LibWeb/Layout/BlockBox.cpp9
-rw-r--r--Libraries/LibWeb/Layout/BlockBox.h2
-rw-r--r--Libraries/LibWeb/Layout/BlockFormattingContext.cpp46
-rw-r--r--Libraries/LibWeb/Layout/BlockFormattingContext.h11
-rw-r--r--Libraries/LibWeb/Layout/BreakNode.cpp5
-rw-r--r--Libraries/LibWeb/Layout/BreakNode.h2
-rw-r--r--Libraries/LibWeb/Layout/FormattingContext.cpp40
-rw-r--r--Libraries/LibWeb/Layout/FormattingContext.h12
-rw-r--r--Libraries/LibWeb/Layout/InlineFormattingContext.cpp52
-rw-r--r--Libraries/LibWeb/Layout/InlineFormattingContext.h2
-rw-r--r--Libraries/LibWeb/Layout/InlineNode.cpp7
-rw-r--r--Libraries/LibWeb/Layout/InlineNode.h2
-rw-r--r--Libraries/LibWeb/Layout/Node.cpp15
-rw-r--r--Libraries/LibWeb/Layout/Node.h2
-rw-r--r--Libraries/LibWeb/Layout/ReplacedBox.cpp11
-rw-r--r--Libraries/LibWeb/Layout/ReplacedBox.h2
-rw-r--r--Libraries/LibWeb/Layout/TextNode.cpp26
-rw-r--r--Libraries/LibWeb/Layout/TextNode.h4
18 files changed, 209 insertions, 41 deletions
diff --git a/Libraries/LibWeb/Layout/BlockBox.cpp b/Libraries/LibWeb/Layout/BlockBox.cpp
index 2fbebba10a..ed10978314 100644
--- a/Libraries/LibWeb/Layout/BlockBox.cpp
+++ b/Libraries/LibWeb/Layout/BlockBox.cpp
@@ -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());
diff --git a/Libraries/LibWeb/Layout/BlockBox.h b/Libraries/LibWeb/Layout/BlockBox.h
index 8d1b43d3a8..83929ac14e 100644
--- a/Libraries/LibWeb/Layout/BlockBox.h
+++ b/Libraries/LibWeb/Layout/BlockBox.h
@@ -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; }
diff --git a/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
index 4b888bdd7d..e6e1421743 100644
--- a/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
+++ b/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
@@ -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();
diff --git a/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Libraries/LibWeb/Layout/BlockFormattingContext.h
index dbbbbe5caf..dd30469935 100644
--- a/Libraries/LibWeb/Layout/BlockFormattingContext.h
+++ b/Libraries/LibWeb/Layout/BlockFormattingContext.h
@@ -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;
};
}
diff --git a/Libraries/LibWeb/Layout/BreakNode.cpp b/Libraries/LibWeb/Layout/BreakNode.cpp
index 0a230a9873..a16633e71a 100644
--- a/Libraries/LibWeb/Layout/BreakNode.cpp
+++ b/Libraries/LibWeb/Layout/BreakNode.cpp
@@ -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();
}
}
diff --git a/Libraries/LibWeb/Layout/BreakNode.h b/Libraries/LibWeb/Layout/BreakNode.h
index 07ecb6926f..2bc15fc529 100644
--- a/Libraries/LibWeb/Layout/BreakNode.h
+++ b/Libraries/LibWeb/Layout/BreakNode.h
@@ -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;
};
}
diff --git a/Libraries/LibWeb/Layout/FormattingContext.cpp b/Libraries/LibWeb/Layout/FormattingContext.cpp
index 670191f41f..5d81536260 100644
--- a/Libraries/LibWeb/Layout/FormattingContext.cpp
+++ b/Libraries/LibWeb/Layout/FormattingContext.cpp
@@ -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);
}
}
diff --git a/Libraries/LibWeb/Layout/FormattingContext.h b/Libraries/LibWeb/Layout/FormattingContext.h
index c8f7487dc2..b505702d67 100644
--- a/Libraries/LibWeb/Layout/FormattingContext.h
+++ b/Libraries/LibWeb/Layout/FormattingContext.h
@@ -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 };
};
}
diff --git a/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
index 105dffa119..270a80a97d 100644
--- a/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
+++ b/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
@@ -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) {
diff --git a/Libraries/LibWeb/Layout/InlineFormattingContext.h b/Libraries/LibWeb/Layout/InlineFormattingContext.h
index 2cc6c5f88f..a96f0c9901 100644
--- a/Libraries/LibWeb/Layout/InlineFormattingContext.h
+++ b/Libraries/LibWeb/Layout/InlineFormattingContext.h
@@ -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);
};
diff --git a/Libraries/LibWeb/Layout/InlineNode.cpp b/Libraries/LibWeb/Layout/InlineNode.cpp
index b34237f400..4a69afe6ff 100644
--- a/Libraries/LibWeb/Layout/InlineNode.cpp
+++ b/Libraries/LibWeb/Layout/InlineNode.cpp
@@ -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);
diff --git a/Libraries/LibWeb/Layout/InlineNode.h b/Libraries/LibWeb/Layout/InlineNode.h
index a140339599..9725651b2e 100644
--- a/Libraries/LibWeb/Layout/InlineNode.h
+++ b/Libraries/LibWeb/Layout/InlineNode.h
@@ -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; }
diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp
index a7eafcef39..b2c79b922c 100644
--- a/Libraries/LibWeb/Layout/Node.cpp
+++ b/Libraries/LibWeb/Layout/Node.cpp
@@ -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);
});
}
diff --git a/Libraries/LibWeb/Layout/Node.h b/Libraries/LibWeb/Layout/Node.h
index 90121e7621..3a7f702e0b 100644
--- a/Libraries/LibWeb/Layout/Node.h
+++ b/Libraries/LibWeb/Layout/Node.h
@@ -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; }
diff --git a/Libraries/LibWeb/Layout/ReplacedBox.cpp b/Libraries/LibWeb/Layout/ReplacedBox.cpp
index 340d96462c..be2b4d77ec 100644
--- a/Libraries/LibWeb/Layout/ReplacedBox.cpp
+++ b/Libraries/LibWeb/Layout/ReplacedBox.cpp
@@ -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);
}
diff --git a/Libraries/LibWeb/Layout/ReplacedBox.h b/Libraries/LibWeb/Layout/ReplacedBox.h
index 1a9ded51af..081bfd8348 100644
--- a/Libraries/LibWeb/Layout/ReplacedBox.h
+++ b/Libraries/LibWeb/Layout/ReplacedBox.h
@@ -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"; }
diff --git a/Libraries/LibWeb/Layout/TextNode.cpp b/Libraries/LibWeb/Layout/TextNode.cpp
index 3846627fa0..0433167093 100644
--- a/Libraries/LibWeb/Layout/TextNode.cpp
+++ b/Libraries/LibWeb/Layout/TextNode.cpp
@@ -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);
}
}
diff --git a/Libraries/LibWeb/Layout/TextNode.h b/Libraries/LibWeb/Layout/TextNode.h
index 389cf0f3e8..e19692511e 100644
--- a/Libraries/LibWeb/Layout/TextNode.h
+++ b/Libraries/LibWeb/Layout/TextNode.h
@@ -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>