diff options
author | Andreas Kling <kling@serenityos.org> | 2020-06-10 10:42:29 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-06-10 10:46:57 +0200 |
commit | 656b01eb0fb659fb2d3ee4e6e4413a82543414e3 (patch) | |
tree | bd07b588460ed9fccf6aadac0b8115e03200256b /Libraries/LibWeb/Layout | |
parent | e836f09094317c8eb1f9674d114cf34837b94e30 (diff) | |
download | serenity-656b01eb0fb659fb2d3ee4e6e4413a82543414e3.zip |
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.
Diffstat (limited to 'Libraries/LibWeb/Layout')
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutBlock.cpp | 60 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutBox.cpp | 45 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutBox.h | 43 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutCanvas.cpp | 4 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutDocument.cpp | 19 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutFrame.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutImage.cpp | 16 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutListItem.cpp | 4 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutListItemMarker.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutNode.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutReplaced.cpp | 15 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutTableRowGroup.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutText.cpp | 8 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutWidget.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LineBox.cpp | 12 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LineBox.h | 10 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LineBoxFragment.cpp | 10 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LineBoxFragment.h | 29 |
18 files changed, 177 insertions, 116 deletions
diff --git a/Libraries/LibWeb/Layout/LayoutBlock.cpp b/Libraries/LibWeb/Layout/LayoutBlock.cpp index e6422ef685..6b4816d74f 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<LayoutBox>(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<LayoutReplaced>(fragment.layout_node())) - const_cast<LayoutReplaced&>(to<LayoutReplaced>(fragment.layout_node())).set_rect(fragment.rect()); - if (fragment.layout_node().is_inline_block()) { auto& inline_block = const_cast<LayoutBlock&>(to<LayoutBlock>(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<LayoutBlock>(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<StyleProperties> LayoutBlock::style_for_anonymous_block() const diff --git a/Libraries/LibWeb/Layout/LayoutBox.cpp b/Libraries/LibWeb/Layout/LayoutBox.cpp index 5aaefa3fee..dc0e513818 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*>(frame)->set_needs_display(enclosing_int_rect(rect())); + const_cast<Frame*>(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 6101d29077..d95155af7b 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&); - - 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(); } - - virtual HitTestResult hit_test(const Gfx::Point& position) const override; + const Gfx::FloatRect absolute_rect() const; + + Gfx::FloatPoint effective_offset() const; + + 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<StyleProperties> 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<LineBoxFragment> m_containing_line_box_fragment; }; template<> diff --git a/Libraries/LibWeb/Layout/LayoutCanvas.cpp b/Libraries/LibWeb/Layout/LayoutCanvas.cpp index 8d0f9f248d..011ed27058 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 bf6d8c33ec..ca240dd1ff 100644 --- a/Libraries/LibWeb/Layout/LayoutDocument.cpp +++ b/Libraries/LibWeb/Layout/LayoutDocument.cpp @@ -28,6 +28,7 @@ #include <LibWeb/Frame/Frame.h> #include <LibWeb/Layout/LayoutDocument.h> #include <LibWeb/Layout/LayoutImage.h> +#include <LibWeb/Layout/LayoutWidget.h> 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<LayoutBlock>(child)); auto& child_block = to<LayoutBlock>(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<LayoutWidget>([&](auto& widget) { + widget.update_widget(); + return IterationDecision::Continue; }); - rect().set_bottom(lowest_bottom); } void LayoutDocument::did_set_viewport_rect(Badge<Frame>, 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<LayoutImage>([&](auto& layout_image) { - const_cast<HTMLImageElement&>(layout_image.node()).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.rect())); + const_cast<HTMLImageElement&>(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 e71ecee7d2..7dbfb75730 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 5e5109233d..961a5cf49a 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 2a46cae1d8..877c095483 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 2188ce44aa..fe7582985f 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 e4a21bb3c7..b0133e210b 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*>(frame)->set_needs_display(enclosing_int_rect(fragment.rect())); + const_cast<Frame*>(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<LayoutBox>(*this).position(); + return to<LayoutBox>(*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 4aa9b5a259..8c17d33b21 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 de1be0657f..0f60749847 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 e4cdd4c4bd..196ffb99e4 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<typename Callback> diff --git a/Libraries/LibWeb/Layout/LayoutWidget.cpp b/Libraries/LibWeb/Layout/LayoutWidget.cpp index 852a36d218..e6cda54b32 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<const PageView&>(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 be73e3c5c5..e9842e391f 100644 --- a/Libraries/LibWeb/Layout/LineBox.cpp +++ b/Libraries/LibWeb/Layout/LineBox.cpp @@ -27,6 +27,7 @@ #include <AK/Utf8View.h> #include <LibWeb/Layout/LayoutNode.h> #include <LibWeb/Layout/LayoutText.h> +#include <LibWeb/Layout/LayoutBox.h> #include <LibWeb/Layout/LineBox.h> #include <ctype.h> @@ -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<LineBoxFragment>(layout_node, start, length, Gfx::FloatPoint(m_width, 0), Gfx::FloatSize(width, height))); } m_width += width; + + if (is<LayoutBox>(layout_node)) + const_cast<LayoutBox&>(to<LayoutBox>(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 e5322c0e15..67333325ae 100644 --- a/Libraries/LibWeb/Layout/LineBox.h +++ b/Libraries/LibWeb/Layout/LineBox.h @@ -26,6 +26,7 @@ #pragma once +#include <AK/NonnullOwnPtrVector.h> #include <AK/Vector.h> #include <LibWeb/Layout/LineBoxFragment.h> @@ -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<LineBoxFragment>& fragments() const { return m_fragments; } - Vector<LineBoxFragment>& fragments() { return m_fragments; } + const NonnullOwnPtrVector<LineBoxFragment>& fragments() const { return m_fragments; } + NonnullOwnPtrVector<LineBoxFragment>& fragments() { return m_fragments; } void trim_trailing_whitespace(); + private: friend class LayoutBlock; - Vector<LineBoxFragment> m_fragments; + NonnullOwnPtrVector<LineBoxFragment> m_fragments; float m_width { 0 }; }; diff --git a/Libraries/LibWeb/Layout/LineBoxFragment.cpp b/Libraries/LibWeb/Layout/LineBoxFragment.cpp index 2d7a60c917..858c0ebe68 100644 --- a/Libraries/LibWeb/Layout/LineBoxFragment.cpp +++ b/Libraries/LibWeb/Layout/LineBoxFragment.cpp @@ -57,6 +57,14 @@ StringView LineBoxFragment::text() const return to<LayoutText>(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 c22cee77bd..ba64d39468 100644 --- a/Libraries/LibWeb/Layout/LineBoxFragment.h +++ b/Libraries/LibWeb/Layout/LineBoxFragment.h @@ -26,31 +26,39 @@ #pragma once +#include <AK/Weakable.h> #include <LibGfx/FloatRect.h> +#include <LibWeb/Forward.h> namespace Web { -class LayoutNode; -class RenderingContext; - -class LineBoxFragment { +class LineBoxFragment : public Weakable<LineBoxFragment> { 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; + + 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 width() const { return m_rect.width(); } + 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; }; } |