From 656b01eb0fb659fb2d3ee4e6e4413a82543414e3 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 10 Jun 2020 10:42:29 +0200 Subject: [PATCH] LibWeb: Rework the layout engine to use relative offsets The box tree and line boxes now all store a relative offset from their containing block, instead of an absolute (document-relative) position. This removes a huge pain point from the layout system which was having to adjust offsets recursively when something moved. It also makes some layout logic significantly simpler. Every box can still find its absolute position by walking its chain of containing blocks and accumulating the translation from the root. This is currently what we do both for rendering and hit testing. --- Libraries/LibWeb/Dump.cpp | 6 +- Libraries/LibWeb/Forward.h | 3 + Libraries/LibWeb/Layout/LayoutBlock.cpp | 60 ++++++++----------- Libraries/LibWeb/Layout/LayoutBox.cpp | 45 +++++++++++--- Libraries/LibWeb/Layout/LayoutBox.h | 39 ++++++++---- Libraries/LibWeb/Layout/LayoutCanvas.cpp | 4 +- Libraries/LibWeb/Layout/LayoutDocument.cpp | 19 ++++-- Libraries/LibWeb/Layout/LayoutFrame.cpp | 6 +- Libraries/LibWeb/Layout/LayoutImage.cpp | 16 ++--- Libraries/LibWeb/Layout/LayoutListItem.cpp | 4 +- .../LibWeb/Layout/LayoutListItemMarker.cpp | 2 +- Libraries/LibWeb/Layout/LayoutNode.cpp | 6 +- Libraries/LibWeb/Layout/LayoutReplaced.cpp | 15 ++--- .../LibWeb/Layout/LayoutTableRowGroup.cpp | 2 +- Libraries/LibWeb/Layout/LayoutText.cpp | 8 +-- Libraries/LibWeb/Layout/LayoutWidget.cpp | 2 +- Libraries/LibWeb/Layout/LineBox.cpp | 12 ++-- Libraries/LibWeb/Layout/LineBox.h | 10 ++-- Libraries/LibWeb/Layout/LineBoxFragment.cpp | 10 +++- Libraries/LibWeb/Layout/LineBoxFragment.h | 29 +++++---- Libraries/LibWeb/PageView.cpp | 4 +- 21 files changed, 183 insertions(+), 119 deletions(-) diff --git a/Libraries/LibWeb/Dump.cpp b/Libraries/LibWeb/Dump.cpp index 7d8f3733e81..77c3acc95b8 100644 --- a/Libraries/LibWeb/Dump.cpp +++ b/Libraries/LibWeb/Dump.cpp @@ -110,8 +110,8 @@ void dump_tree(const LayoutNode& layout_node) layout_box.class_name(), tag_name.characters(), identifier.characters(), - layout_box.x(), - layout_box.y(), + layout_box.absolute_x(), + layout_box.absolute_y(), layout_box.width(), layout_box.height()); @@ -158,7 +158,7 @@ void dump_tree(const LayoutNode& layout_node) &fragment.layout_node(), fragment.start(), fragment.length(), - fragment.rect().to_string().characters()); + fragment.absolute_rect().to_string().characters()); if (fragment.layout_node().is_text()) { for (size_t i = 0; i < indent; ++i) dbgprintf(" "); diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 7c9d0f0a81c..d3f7b19cf19 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -47,6 +47,8 @@ class HTMLImageElement; class HTMLScriptElement; class PageView; class ImageData; +class LineBox; +class LineBoxFragment; class LayoutBlock; class LayoutDocument; class LayoutNode; @@ -57,6 +59,7 @@ class Node; class Origin; class Page; class PageClient; +class RenderingContext; class Resource; class ResourceLoader; class Selector; diff --git a/Libraries/LibWeb/Layout/LayoutBlock.cpp b/Libraries/LibWeb/Layout/LayoutBlock.cpp index e6422ef685e..6b4816d74f9 100644 --- a/Libraries/LibWeb/Layout/LayoutBlock.cpp +++ b/Libraries/LibWeb/Layout/LayoutBlock.cpp @@ -86,7 +86,7 @@ void LayoutBlock::layout_block_children(LayoutMode layout_mode) child_block.layout(layout_mode); if (!child_block.is_absolutely_positioned()) - content_height = child_block.rect().bottom() + child_block.box_model().full_margin(*this).bottom - rect().top(); + content_height = max(content_height, child_block.effective_offset().y() + child_block.height() + child_block.box_model().full_margin(*this).bottom); }); if (layout_mode != LayoutMode::Default) { float max_width = 0; @@ -94,9 +94,9 @@ void LayoutBlock::layout_block_children(LayoutMode layout_mode) if (child.is_box() && !child.is_absolutely_positioned()) max_width = max(max_width, to(child).width()); }); - rect().set_width(max_width); + set_width(max_width); } - rect().set_height(content_height); + set_height(content_height); } void LayoutBlock::layout_inline_children(LayoutMode layout_mode) @@ -133,10 +133,10 @@ void LayoutBlock::layout_inline_children(LayoutMode layout_mode) for (auto& line_box : m_line_boxes) { float max_height = min_line_height; for (auto& fragment : line_box.fragments()) { - max_height = max(max_height, fragment.rect().height()); + max_height = max(max_height, fragment.height()); } - float x_offset = x(); + float x_offset = 0; float excess_horizontal_space = (float)width() - line_box.width(); switch (text_align) { @@ -158,7 +158,7 @@ void LayoutBlock::layout_inline_children(LayoutMode layout_mode) for (auto& fragment : line_box.fragments()) { if (fragment.is_justifiable_whitespace()) { ++whitespace_count; - excess_horizontal_space_including_whitespace += fragment.rect().width(); + excess_horizontal_space_including_whitespace += fragment.width(); } } } @@ -173,34 +173,32 @@ void LayoutBlock::layout_inline_children(LayoutMode layout_mode) // Vertically align everyone's bottom to the line. // FIXME: Support other kinds of vertical alignment. - fragment.rect().set_x(roundf(x_offset + fragment.rect().x())); - fragment.rect().set_y(y() + content_height + (max_height - fragment.rect().height()) - (line_spacing / 2)); + fragment.set_offset({ roundf(x_offset + fragment.offset().x()), content_height + (max_height - fragment.height()) - (line_spacing / 2) }); if (text_align == CSS::ValueID::Justify) { if (fragment.is_justifiable_whitespace()) { - if (fragment.rect().width() != justified_space_width) { - float diff = justified_space_width - fragment.rect().width(); - fragment.rect().set_width(justified_space_width); + if (fragment.width() != justified_space_width) { + float diff = justified_space_width - fragment.width(); + fragment.set_width(justified_space_width); // Shift subsequent sibling fragments to the right to adjust for change in width. for (size_t j = i + 1; j < line_box.fragments().size(); ++j) { - line_box.fragments()[j].rect().move_by(diff, 0); + auto offset = line_box.fragments()[j].offset(); + offset.move_by(diff, 0); + line_box.fragments()[j].set_offset(offset); } } } } - if (is(fragment.layout_node())) - const_cast(to(fragment.layout_node())).set_rect(fragment.rect()); - if (fragment.layout_node().is_inline_block()) { auto& inline_block = const_cast(to(fragment.layout_node())); - inline_block.set_rect(fragment.rect()); + inline_block.set_size(fragment.size()); inline_block.layout(layout_mode); } float final_line_box_width = 0; for (auto& fragment : line_box.fragments()) - final_line_box_width += fragment.rect().width(); + final_line_box_width += fragment.width(); line_box.m_width = final_line_box_width; max_linebox_width = max(max_linebox_width, final_line_box_width); @@ -210,10 +208,10 @@ void LayoutBlock::layout_inline_children(LayoutMode layout_mode) } if (layout_mode != LayoutMode::Default) { - rect().set_width(max_linebox_width); + set_width(max_linebox_width); } - rect().set_height(content_height); + set_height(content_height); } void LayoutBlock::compute_width() @@ -368,7 +366,7 @@ void LayoutBlock::compute_width() } } - rect().set_width(used_width.to_px(*this)); + set_width(used_width.to_px(*this)); box_model().margin().left = margin_left; box_model().margin().right = margin_right; box_model().border().left = border_left; @@ -404,11 +402,6 @@ void LayoutBlock::compute_position() + box_model().padding().left.to_px(*this) + box_model().offset().left.to_px(*this); - if (style.position() != CSS::Position::Absolute || containing_block.style().position() == CSS::Position::Absolute) - position_x += containing_block.x(); - - rect().set_x(position_x); - float position_y = box_model().full_margin(*this).top + box_model().offset().top.to_px(*this); @@ -420,17 +413,14 @@ void LayoutBlock::compute_position() relevant_sibling = relevant_sibling->previous_sibling(); } - if (relevant_sibling == nullptr) { - position_y += containing_block.y(); - } else { - auto& previous_sibling_rect = relevant_sibling->rect(); + if (relevant_sibling) { auto& previous_sibling_style = relevant_sibling->box_model(); - position_y += previous_sibling_rect.y() + previous_sibling_rect.height(); + position_y += relevant_sibling->effective_offset().y() + relevant_sibling->height(); position_y += previous_sibling_style.full_margin(*this).bottom; } } - rect().set_y(position_y); + set_offset({ position_x, position_y }); } void LayoutBlock::compute_height() @@ -438,7 +428,7 @@ void LayoutBlock::compute_height() auto& style = this->style(); auto height = style.length_or_fallback(CSS::PropertyID::Height, Length(), containing_block()->height()); if (height.is_absolute()) - rect().set_height(height.to_px(*this)); + set_height(height.to_px(*this)); } void LayoutBlock::render(RenderingContext& context) @@ -452,7 +442,7 @@ void LayoutBlock::render(RenderingContext& context) for (auto& line_box : m_line_boxes) { for (auto& fragment : line_box.fragments()) { if (context.should_show_line_box_borders()) - context.painter().draw_rect(enclosing_int_rect(fragment.rect()), Color::Green); + context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Green); fragment.render(context); } } @@ -467,7 +457,7 @@ HitTestResult LayoutBlock::hit_test(const Gfx::Point& position) const HitTestResult result; for (auto& line_box : m_line_boxes) { for (auto& fragment : line_box.fragments()) { - if (enclosing_int_rect(fragment.rect()).contains(position)) { + if (enclosing_int_rect(fragment.absolute_rect()).contains(position)) { if (fragment.layout_node().is_block()) return to(fragment.layout_node()).hit_test(position); return { fragment.layout_node(), fragment.text_index_at(position.x()) }; @@ -477,7 +467,7 @@ HitTestResult LayoutBlock::hit_test(const Gfx::Point& position) const // FIXME: This should be smarter about the text position if we're hitting a block // that has text inside it, but `position` is to the right of the text box. - return { rect().contains(position.x(), position.y()) ? this : nullptr }; + return { absolute_rect().contains(position.x(), position.y()) ? this : nullptr }; } NonnullRefPtr LayoutBlock::style_for_anonymous_block() const diff --git a/Libraries/LibWeb/Layout/LayoutBox.cpp b/Libraries/LibWeb/Layout/LayoutBox.cpp index 5aaefa3fee5..dc0e5138186 100644 --- a/Libraries/LibWeb/Layout/LayoutBox.cpp +++ b/Libraries/LibWeb/Layout/LayoutBox.cpp @@ -203,9 +203,9 @@ void LayoutBox::render(RenderingContext& context) #endif Gfx::FloatRect padded_rect; - padded_rect.set_x(x() - box_model().padding().left.to_px(*this)); + padded_rect.set_x(absolute_x() - box_model().padding().left.to_px(*this)); padded_rect.set_width(width() + box_model().padding().left.to_px(*this) + box_model().padding().right.to_px(*this)); - padded_rect.set_y(y() - box_model().padding().top.to_px(*this)); + padded_rect.set_y(absolute_y() - box_model().padding().top.to_px(*this)); padded_rect.set_height(height() + box_model().padding().top.to_px(*this) + box_model().padding().bottom.to_px(*this)); if (!is_body()) { @@ -237,7 +237,7 @@ void LayoutBox::render(RenderingContext& context) LayoutNodeWithStyleAndBoxModelMetrics::render(context); if (node() && document().inspected_node() == node()) - context.painter().draw_rect(enclosing_int_rect(m_rect), Color::Magenta); + context.painter().draw_rect(enclosing_int_rect(absolute_rect()), Color::Magenta); } HitTestResult LayoutBox::hit_test(const Gfx::Point& position) const @@ -245,7 +245,7 @@ HitTestResult LayoutBox::hit_test(const Gfx::Point& position) const // FIXME: It would be nice if we could confidently skip over hit testing // parts of the layout tree, but currently we can't just check // m_rect.contains() since inline text rects can't be trusted.. - HitTestResult result { m_rect.contains(position.x(), position.y()) ? this : nullptr }; + HitTestResult result { absolute_rect().contains(position.x(), position.y()) ? this : nullptr }; for_each_child([&](auto& child) { auto child_result = child.hit_test(position); if (child_result.layout_node) @@ -260,7 +260,7 @@ void LayoutBox::set_needs_display() ASSERT(frame); if (!is_inline()) { - const_cast(frame)->set_needs_display(enclosing_int_rect(rect())); + const_cast(frame)->set_needs_display(enclosing_int_rect(absolute_rect())); return; } @@ -272,12 +272,41 @@ bool LayoutBox::is_body() const return node() && node() == document().body(); } -void LayoutBox::set_rect(const Gfx::FloatRect& rect) +void LayoutBox::set_offset(const Gfx::FloatPoint& offset) { - if (m_rect == rect) + if (m_offset == offset) return; - m_rect = rect; + m_offset = offset; did_set_rect(); } +void LayoutBox::set_size(const Gfx::FloatSize& size) +{ + if (m_size == size) + return; + m_size = size; + did_set_rect(); +} + +Gfx::FloatPoint LayoutBox::effective_offset() const +{ + if (m_containing_line_box_fragment) + return m_containing_line_box_fragment->offset(); + return m_offset; +} + +const Gfx::FloatRect LayoutBox::absolute_rect() const +{ + Gfx::FloatRect rect { effective_offset(), size() }; + for (auto* block = containing_block(); block; block = block->containing_block()) { + rect.move_by(block->effective_offset()); + } + return rect; +} + +void LayoutBox::set_containing_line_box_fragment(LineBoxFragment& fragment) +{ + m_containing_line_box_fragment = fragment.make_weak_ptr(); +} + } diff --git a/Libraries/LibWeb/Layout/LayoutBox.h b/Libraries/LibWeb/Layout/LayoutBox.h index 6101d290775..d95155af7b9 100644 --- a/Libraries/LibWeb/Layout/LayoutBox.h +++ b/Libraries/LibWeb/Layout/LayoutBox.h @@ -33,22 +33,33 @@ namespace Web { class LayoutBox : public LayoutNodeWithStyleAndBoxModelMetrics { public: - const Gfx::FloatRect& rect() const { return m_rect; } - Gfx::FloatRect& rect() { return m_rect; } - void set_rect(const Gfx::FloatRect&); + const Gfx::FloatRect absolute_rect() const; - float x() const { return rect().x(); } - float y() const { return rect().y(); } - float width() const { return rect().width(); } - float height() const { return rect().height(); } - Gfx::FloatSize size() const { return rect().size(); } - Gfx::FloatPoint position() const { return rect().location(); } + Gfx::FloatPoint effective_offset() const; - virtual HitTestResult hit_test(const Gfx::Point& position) const override; + void set_offset(const Gfx::FloatPoint& offset); + void set_offset(float x, float y) { set_offset({ x, y }); } + + const Gfx::FloatSize& size() const { return m_size; } + void set_size(const Gfx::FloatSize&); + void set_size(float width, float height) { set_size({ width, height }); } + + void set_width(float width) { set_size(width, height()); } + void set_height(float height) { set_size(width(), height); } + float width() const { return m_size.width(); } + float height() const { return m_size.height(); } + + float absolute_x() const { return absolute_rect().x(); } + float absolute_y() const { return absolute_rect().y(); } + Gfx::FloatPoint absolute_position() const { return absolute_rect().location(); } + + virtual HitTestResult hit_test(const Gfx::Point& absolute_position) const override; virtual void set_needs_display() override; bool is_body() const; + void set_containing_line_box_fragment(LineBoxFragment&); + protected: LayoutBox(const Node* node, NonnullRefPtr style) : LayoutNodeWithStyleAndBoxModelMetrics(node, move(style)) @@ -57,7 +68,7 @@ protected: virtual void render(RenderingContext&) override; - virtual void did_set_rect() {} + virtual void did_set_rect() { } private: virtual bool is_box() const override { return true; } @@ -70,7 +81,11 @@ private: }; void paint_border(RenderingContext&, Edge, const Gfx::FloatRect&, CSS::PropertyID style_property_id, CSS::PropertyID color_property_id, CSS::PropertyID width_property_id); - Gfx::FloatRect m_rect; + Gfx::FloatPoint m_offset; + Gfx::FloatSize m_size; + + // Some boxes hang off of line box fragments. (inline-block, inline-table, replaced, etc) + WeakPtr m_containing_line_box_fragment; }; template<> diff --git a/Libraries/LibWeb/Layout/LayoutCanvas.cpp b/Libraries/LibWeb/Layout/LayoutCanvas.cpp index 8d0f9f248d4..011ed270585 100644 --- a/Libraries/LibWeb/Layout/LayoutCanvas.cpp +++ b/Libraries/LibWeb/Layout/LayoutCanvas.cpp @@ -55,11 +55,11 @@ void LayoutCanvas::render(RenderingContext& context) return; // FIXME: This should be done at a different level. Also rect() does not include padding etc! - if (!context.viewport_rect().intersects(enclosing_int_rect(rect()))) + if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect()))) return; if (node().bitmap()) - context.painter().draw_scaled_bitmap(enclosing_int_rect(rect()), *node().bitmap(), node().bitmap()->rect()); + context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *node().bitmap(), node().bitmap()->rect()); LayoutReplaced::render(context); } diff --git a/Libraries/LibWeb/Layout/LayoutDocument.cpp b/Libraries/LibWeb/Layout/LayoutDocument.cpp index bf6d8c33ec3..ca240dd1ff6 100644 --- a/Libraries/LibWeb/Layout/LayoutDocument.cpp +++ b/Libraries/LibWeb/Layout/LayoutDocument.cpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace Web { @@ -43,27 +44,33 @@ LayoutDocument::~LayoutDocument() void LayoutDocument::layout(LayoutMode layout_mode) { ASSERT(document().frame()); - rect().set_width(document().frame()->size().width()); + set_width(document().frame()->size().width()); LayoutNode::layout(layout_mode); ASSERT(!children_are_inline()); - int lowest_bottom = 0; + float lowest_bottom = 0; for_each_child([&](auto& child) { ASSERT(is(child)); auto& child_block = to(child); - if (child_block.rect().bottom() > lowest_bottom) - lowest_bottom = child_block.rect().bottom(); + lowest_bottom = max(lowest_bottom, child_block.absolute_rect().bottom()); + }); + set_height(lowest_bottom); + + // FIXME: This is a total hack. Make sure any GUI::Widgets are moved into place after layout. + // We should stop embedding GUI::Widgets entirely, since that won't work out-of-process. + for_each_in_subtree_of_type([&](auto& widget) { + widget.update_widget(); + return IterationDecision::Continue; }); - rect().set_bottom(lowest_bottom); } void LayoutDocument::did_set_viewport_rect(Badge, const Gfx::Rect& a_viewport_rect) { Gfx::FloatRect viewport_rect(a_viewport_rect.x(), a_viewport_rect.y(), a_viewport_rect.width(), a_viewport_rect.height()); for_each_in_subtree_of_type([&](auto& layout_image) { - const_cast(layout_image.node()).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.rect())); + const_cast(layout_image.node()).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.absolute_rect())); return IterationDecision::Continue; }); } diff --git a/Libraries/LibWeb/Layout/LayoutFrame.cpp b/Libraries/LibWeb/Layout/LayoutFrame.cpp index e71ecee7d25..7dbfb75730e 100644 --- a/Libraries/LibWeb/Layout/LayoutFrame.cpp +++ b/Libraries/LibWeb/Layout/LayoutFrame.cpp @@ -67,8 +67,8 @@ void LayoutFrame::render(RenderingContext& context) context.painter().save(); auto old_viewport_rect = context.viewport_rect(); - context.painter().add_clip_rect(enclosing_int_rect(rect())); - context.painter().translate(x(), y()); + context.painter().add_clip_rect(enclosing_int_rect(absolute_rect())); + context.painter().translate(absolute_x(), absolute_y()); context.set_viewport_rect({ {}, node().hosted_frame()->size() }); node().hosted_frame()->document()->layout_node()->render(context); @@ -82,7 +82,7 @@ void LayoutFrame::did_set_rect() LayoutReplaced::did_set_rect(); ASSERT(node().hosted_frame()); - node().hosted_frame()->set_size(Gfx::Size(rect().width(), rect().height())); + node().hosted_frame()->set_size(size().to_int_size()); } } diff --git a/Libraries/LibWeb/Layout/LayoutImage.cpp b/Libraries/LibWeb/Layout/LayoutImage.cpp index 5e5109233d5..961a5cf49a2 100644 --- a/Libraries/LibWeb/Layout/LayoutImage.cpp +++ b/Libraries/LibWeb/Layout/LayoutImage.cpp @@ -52,11 +52,11 @@ void LayoutImage::layout(LayoutMode layout_mode) auto alt = node().alt(); if (alt.is_empty()) alt = node().src(); - rect().set_width(font.width(alt) + 16); - rect().set_height(font.glyph_height() + 16); + set_width(font.width(alt) + 16); + set_height(font.glyph_height() + 16); } else { - rect().set_width(16); - rect().set_height(16); + set_width(16); + set_height(16); } LayoutReplaced::layout(layout_mode); @@ -68,18 +68,18 @@ void LayoutImage::render(RenderingContext& context) return; // FIXME: This should be done at a different level. Also rect() does not include padding etc! - if (!context.viewport_rect().intersects(enclosing_int_rect(rect()))) + if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect()))) return; if (renders_as_alt_text()) { context.painter().set_font(Gfx::Font::default_font()); - Gfx::StylePainter::paint_frame(context.painter(), enclosing_int_rect(rect()), context.palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2); + Gfx::StylePainter::paint_frame(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2); auto alt = node().alt(); if (alt.is_empty()) alt = node().src(); - context.painter().draw_text(enclosing_int_rect(rect()), alt, Gfx::TextAlignment::Center, style().color_or_fallback(CSS::PropertyID::Color, document(), Color::Black), Gfx::TextElision::Right); + context.painter().draw_text(enclosing_int_rect(absolute_rect()), alt, Gfx::TextAlignment::Center, style().color_or_fallback(CSS::PropertyID::Color, document(), Color::Black), Gfx::TextElision::Right); } else if (node().bitmap()) - context.painter().draw_scaled_bitmap(enclosing_int_rect(rect()), *node().bitmap(), node().bitmap()->rect()); + context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *node().bitmap(), node().bitmap()->rect()); LayoutReplaced::render(context); } diff --git a/Libraries/LibWeb/Layout/LayoutListItem.cpp b/Libraries/LibWeb/Layout/LayoutListItem.cpp index 2a46cae1d8c..877c095483e 100644 --- a/Libraries/LibWeb/Layout/LayoutListItem.cpp +++ b/Libraries/LibWeb/Layout/LayoutListItem.cpp @@ -58,8 +58,8 @@ void LayoutListItem::layout(LayoutMode layout_mode) append_child(*m_marker); } - Gfx::FloatRect marker_rect { x() - 8, y(), 4, height() }; - m_marker->set_rect(marker_rect); + m_marker->set_offset(-8, 0); + m_marker->set_size(4, height()); } } diff --git a/Libraries/LibWeb/Layout/LayoutListItemMarker.cpp b/Libraries/LibWeb/Layout/LayoutListItemMarker.cpp index 2188ce44aa4..fe7582985f1 100644 --- a/Libraries/LibWeb/Layout/LayoutListItemMarker.cpp +++ b/Libraries/LibWeb/Layout/LayoutListItemMarker.cpp @@ -41,7 +41,7 @@ LayoutListItemMarker::~LayoutListItemMarker() void LayoutListItemMarker::render(RenderingContext& context) { Gfx::Rect bullet_rect { 0, 0, 4, 4 }; - bullet_rect.center_within(enclosing_int_rect(rect())); + bullet_rect.center_within(enclosing_int_rect(absolute_rect())); // FIXME: It would be nicer to not have to go via the parent here to get our inherited style. auto color = parent()->style().color_or_fallback(CSS::PropertyID::Color, document(), context.palette().base_text()); context.painter().fill_rect(bullet_rect, color); diff --git a/Libraries/LibWeb/Layout/LayoutNode.cpp b/Libraries/LibWeb/Layout/LayoutNode.cpp index e4a21bb3c7e..b0133e210b7 100644 --- a/Libraries/LibWeb/Layout/LayoutNode.cpp +++ b/Libraries/LibWeb/Layout/LayoutNode.cpp @@ -155,7 +155,7 @@ void LayoutNode::set_needs_display() if (auto* block = containing_block()) { block->for_each_fragment([&](auto& fragment) { if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { - const_cast(frame)->set_needs_display(enclosing_int_rect(fragment.rect())); + const_cast(frame)->set_needs_display(enclosing_int_rect(fragment.absolute_rect())); } return IterationDecision::Continue; }); @@ -172,13 +172,13 @@ float LayoutNode::font_size() const Gfx::FloatPoint LayoutNode::box_type_agnostic_position() const { if (is_box()) - return to(*this).position(); + return to(*this).absolute_position(); ASSERT(is_inline()); Gfx::FloatPoint position; if (auto* block = containing_block()) { block->for_each_fragment([&](auto& fragment) { if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { - position = fragment.rect().location(); + position = fragment.absolute_rect().location(); return IterationDecision::Break; } return IterationDecision::Continue; diff --git a/Libraries/LibWeb/Layout/LayoutReplaced.cpp b/Libraries/LibWeb/Layout/LayoutReplaced.cpp index 4aa9b5a259a..8c17d33b218 100644 --- a/Libraries/LibWeb/Layout/LayoutReplaced.cpp +++ b/Libraries/LibWeb/Layout/LayoutReplaced.cpp @@ -141,27 +141,24 @@ Gfx::FloatPoint LayoutReplaced::calculate_position() box_model().padding().top = style.length_or_fallback(CSS::PropertyID::PaddingTop, zero_value, containing_block.width()); box_model().padding().bottom = style.length_or_fallback(CSS::PropertyID::PaddingBottom, zero_value, containing_block.width()); - float position_x = box_model().margin().left.to_px(*this) + float x = box_model().margin().left.to_px(*this) + box_model().border().left.to_px(*this) + box_model().padding().left.to_px(*this) + box_model().offset().left.to_px(*this); - if (style.position() != CSS::Position::Absolute || containing_block.style().position() == CSS::Position::Absolute) - position_x += containing_block.x(); + float y = box_model().full_margin(*this).top + box_model().offset().top.to_px(*this); - float position_y = box_model().full_margin(*this).top + box_model().offset().top.to_px(*this); - - return { position_x, position_y }; + return { x, y }; } void LayoutReplaced::layout(LayoutMode layout_mode) { - rect().set_width(calculate_width()); - rect().set_height(calculate_height()); + set_width(calculate_width()); + set_height(calculate_height()); LayoutBox::layout(layout_mode); - rect().set_location(calculate_position()); + set_offset(calculate_position()); } void LayoutReplaced::split_into_lines(LayoutBlock& container, LayoutMode layout_mode) diff --git a/Libraries/LibWeb/Layout/LayoutTableRowGroup.cpp b/Libraries/LibWeb/Layout/LayoutTableRowGroup.cpp index de1be0657f5..0f60749847a 100644 --- a/Libraries/LibWeb/Layout/LayoutTableRowGroup.cpp +++ b/Libraries/LibWeb/Layout/LayoutTableRowGroup.cpp @@ -53,7 +53,7 @@ void LayoutTableRowGroup::layout(LayoutMode layout_mode) content_height += row.height(); }); - rect().set_height(content_height); + set_height(content_height); } } diff --git a/Libraries/LibWeb/Layout/LayoutText.cpp b/Libraries/LibWeb/Layout/LayoutText.cpp index e4cdd4c4bd2..196ffb99e4f 100644 --- a/Libraries/LibWeb/Layout/LayoutText.cpp +++ b/Libraries/LibWeb/Layout/LayoutText.cpp @@ -72,17 +72,17 @@ void LayoutText::render_fragment(RenderingContext& context, const LineBoxFragmen auto background_color = style().property(CSS::PropertyID::BackgroundColor); if (background_color.has_value() && background_color.value()->is_color()) - painter.fill_rect(enclosing_int_rect(fragment.rect()), background_color.value()->to_color(document())); + painter.fill_rect(enclosing_int_rect(fragment.absolute_rect()), background_color.value()->to_color(document())); auto color = style().color_or_fallback(CSS::PropertyID::Color, document(), context.palette().base_text()); auto text_decoration = style().string_or_fallback(CSS::PropertyID::TextDecoration, "none"); if (document().inspected_node() == &node()) - context.painter().draw_rect(enclosing_int_rect(fragment.rect()), Color::Magenta); + context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Magenta); bool is_underline = text_decoration == "underline"; if (is_underline) - painter.draw_line(enclosing_int_rect(fragment.rect()).bottom_left().translated(0, 1), enclosing_int_rect(fragment.rect()).bottom_right().translated(0, 1), color); + painter.draw_line(enclosing_int_rect(fragment.absolute_rect()).bottom_left().translated(0, 1), enclosing_int_rect(fragment.absolute_rect()).bottom_right().translated(0, 1), color); auto text = m_text_for_rendering; auto text_transform = style().string_or_fallback(CSS::PropertyID::TextTransform, "none"); @@ -91,7 +91,7 @@ void LayoutText::render_fragment(RenderingContext& context, const LineBoxFragmen if (text_transform == "lowercase") text = m_text_for_rendering.to_lowercase(); - painter.draw_text(enclosing_int_rect(fragment.rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::TopLeft, color); + painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::TopLeft, color); } template diff --git a/Libraries/LibWeb/Layout/LayoutWidget.cpp b/Libraries/LibWeb/Layout/LayoutWidget.cpp index 852a36d218d..e6cda54b32f 100644 --- a/Libraries/LibWeb/Layout/LayoutWidget.cpp +++ b/Libraries/LibWeb/Layout/LayoutWidget.cpp @@ -59,7 +59,7 @@ void LayoutWidget::did_set_rect() void LayoutWidget::update_widget() { - auto adjusted_widget_position = rect().location().to_int_point(); + auto adjusted_widget_position = absolute_rect().location().to_int_point(); auto& page_view = static_cast(document().frame()->page().client()); adjusted_widget_position.move_by(-page_view.horizontal_scrollbar().value(), -page_view.vertical_scrollbar().value()); widget().move_to(adjusted_widget_position); diff --git a/Libraries/LibWeb/Layout/LineBox.cpp b/Libraries/LibWeb/Layout/LineBox.cpp index be73e3c5c5c..e9842e391ff 100644 --- a/Libraries/LibWeb/Layout/LineBox.cpp +++ b/Libraries/LibWeb/Layout/LineBox.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -39,18 +40,21 @@ void LineBox::add_fragment(const LayoutNode& layout_node, int start, int length, // The fragment we're adding is from the last LayoutNode on the line. // Expand the last fragment instead of adding a new one with the same LayoutNode. m_fragments.last().m_length = (start - m_fragments.last().m_start) + length; - m_fragments.last().m_rect.set_width(m_fragments.last().m_rect.width() + width); + m_fragments.last().set_width(m_fragments.last().width() + width); } else { - m_fragments.empend(layout_node, start, length, Gfx::FloatRect(m_width, 0, width, height)); + m_fragments.append(make(layout_node, start, length, Gfx::FloatPoint(m_width, 0), Gfx::FloatSize(width, height))); } m_width += width; + + if (is(layout_node)) + const_cast(to(layout_node)).set_containing_line_box_fragment(m_fragments.last()); } void LineBox::trim_trailing_whitespace() { while (!m_fragments.is_empty() && m_fragments.last().is_justifiable_whitespace()) { auto fragment = m_fragments.take_last(); - m_width -= fragment.width(); + m_width -= fragment->width(); } if (m_fragments.is_empty()) @@ -64,7 +68,7 @@ void LineBox::trim_trailing_whitespace() int space_width = last_fragment.layout_node().style().font().glyph_width(' '); while (last_fragment.length() && isspace(last_text[last_fragment.length() - 1])) { last_fragment.m_length -= 1; - last_fragment.m_rect.set_width(last_fragment.m_rect.width() - space_width); + last_fragment.set_width(last_fragment.width() - space_width); m_width -= space_width; } } diff --git a/Libraries/LibWeb/Layout/LineBox.h b/Libraries/LibWeb/Layout/LineBox.h index e5322c0e153..67333325aed 100644 --- a/Libraries/LibWeb/Layout/LineBox.h +++ b/Libraries/LibWeb/Layout/LineBox.h @@ -26,6 +26,7 @@ #pragma once +#include #include #include @@ -33,19 +34,20 @@ namespace Web { class LineBox { public: - LineBox() {} + LineBox() { } float width() const { return m_width; } void add_fragment(const LayoutNode& layout_node, int start, int length, int width, int height); - const Vector& fragments() const { return m_fragments; } - Vector& fragments() { return m_fragments; } + const NonnullOwnPtrVector& fragments() const { return m_fragments; } + NonnullOwnPtrVector& fragments() { return m_fragments; } void trim_trailing_whitespace(); + private: friend class LayoutBlock; - Vector m_fragments; + NonnullOwnPtrVector m_fragments; float m_width { 0 }; }; diff --git a/Libraries/LibWeb/Layout/LineBoxFragment.cpp b/Libraries/LibWeb/Layout/LineBoxFragment.cpp index 2d7a60c9179..858c0ebe686 100644 --- a/Libraries/LibWeb/Layout/LineBoxFragment.cpp +++ b/Libraries/LibWeb/Layout/LineBoxFragment.cpp @@ -57,6 +57,14 @@ StringView LineBoxFragment::text() const return to(layout_node()).text_for_rendering().substring_view(m_start, m_length); } +const Gfx::FloatRect LineBoxFragment::absolute_rect() const +{ + Gfx::FloatRect rect { {}, size() }; + rect.set_location(m_layout_node.containing_block()->absolute_position()); + rect.move_by(offset()); + return rect; +} + int LineBoxFragment::text_index_at(float x) const { if (!layout_node().is_text()) @@ -65,7 +73,7 @@ int LineBoxFragment::text_index_at(float x) const auto& font = layout_text.style().font(); Utf8View view(text()); - float relative_x = x - m_rect.location().x(); + float relative_x = x - absolute_x(); float glyph_spacing = font.glyph_spacing(); float width_so_far = 0; diff --git a/Libraries/LibWeb/Layout/LineBoxFragment.h b/Libraries/LibWeb/Layout/LineBoxFragment.h index c22cee77bdb..ba64d39468b 100644 --- a/Libraries/LibWeb/Layout/LineBoxFragment.h +++ b/Libraries/LibWeb/Layout/LineBoxFragment.h @@ -26,31 +26,39 @@ #pragma once +#include #include +#include namespace Web { -class LayoutNode; -class RenderingContext; - -class LineBoxFragment { +class LineBoxFragment : public Weakable { friend class LineBox; + public: - LineBoxFragment(const LayoutNode& layout_node, int start, int length, const Gfx::FloatRect& rect) + LineBoxFragment(const LayoutNode& layout_node, int start, int length, const Gfx::FloatPoint& offset, const Gfx::FloatSize& size) : m_layout_node(layout_node) , m_start(start) , m_length(length) - , m_rect(rect) + , m_offset(offset) + , m_size(size) { } const LayoutNode& layout_node() const { return m_layout_node; } int start() const { return m_start; } int length() const { return m_length; } - const Gfx::FloatRect& rect() const { return m_rect; } - Gfx::FloatRect& rect() { return m_rect; } + const Gfx::FloatRect absolute_rect() const; - float width() const { return m_rect.width(); } + const Gfx::FloatPoint& offset() const { return m_offset; } + void set_offset(const Gfx::FloatPoint& offset) { m_offset = offset; } + + const Gfx::FloatSize& size() const { return m_size; } + void set_width(float width) { m_size.set_width(width); } + float width() const { return m_size.width(); } + float height() const { return m_size.height(); } + + float absolute_x() const { return absolute_rect().x(); } void render(RenderingContext&); @@ -63,7 +71,8 @@ private: const LayoutNode& m_layout_node; int m_start { 0 }; int m_length { 0 }; - Gfx::FloatRect m_rect; + Gfx::FloatPoint m_offset; + Gfx::FloatSize m_size; }; } diff --git a/Libraries/LibWeb/PageView.cpp b/Libraries/LibWeb/PageView.cpp index ab85a1d4a55..68ec47dc17e 100644 --- a/Libraries/LibWeb/PageView.cpp +++ b/Libraries/LibWeb/PageView.cpp @@ -161,14 +161,14 @@ void PageView::layout_and_sync_size() page().main_frame().set_size(available_size()); document()->layout(); - set_content_size(enclosing_int_rect(layout_root()->rect()).size()); + set_content_size(layout_root()->size().to_int_size()); // NOTE: If layout caused us to gain or lose scrollbars, we have to lay out again // since the scrollbars now take up some of the available space. if (had_vertical_scrollbar != vertical_scrollbar().is_visible() || had_horizontal_scrollbar != horizontal_scrollbar().is_visible()) { page().main_frame().set_size(available_size()); document()->layout(); - set_content_size(enclosing_int_rect(layout_root()->rect()).size()); + set_content_size(layout_root()->size().to_int_size()); } page().main_frame().set_viewport_rect(viewport_rect_in_content_coordinates());