diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index cd255f3b9f..ecb453f417 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -551,7 +552,7 @@ void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_m } } -void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer const& containing_block, LayoutMode layout_mode) +void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer const& containing_block, LayoutMode layout_mode, LineBuilder* line_builder) { VERIFY(box.is_floating()); @@ -575,8 +576,16 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer compute_height(box, m_state); // First we place the box normally (to get the right y coordinate.) - place_block_level_element_in_normal_flow_vertically(box, containing_block); - place_block_level_element_in_normal_flow_horizontally(box, containing_block); + // If we have a LineBuilder, we're in the middle of inline layout, otherwise this is block layout. + if (line_builder) { + float y_offset = box_state.margin_box_top() + box_state.offset_top; + line_builder->break_if_needed(layout_mode, box_state.border_box_width(), false); + box_state.offset.set_y(line_builder->current_y() + y_offset); + line_builder->adjust_last_line_after_inserting_floating_box({}, box.computed_values().float_(), box_state.border_box_width()); + } else { + place_block_level_element_in_normal_flow_vertically(box, containing_block); + place_block_level_element_in_normal_flow_horizontally(box, containing_block); + } auto float_box = [&](FloatSide side, FloatSideData& side_data) { auto first_edge = [&](FormattingState::NodeState const& thing) { return side == FloatSide::Left ? thing.margin_left : thing.margin_right; }; diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h index f66378bd08..f9b4eaa78d 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -13,6 +13,8 @@ namespace Web::Layout { +class LineBuilder; + // https://www.w3.org/TR/css-display/#block-formatting-context class BlockFormattingContext : public FormattingContext { public: @@ -42,6 +44,8 @@ public: virtual float greatest_child_width(Box const&) override; + void layout_floating_box(Box const& child, BlockContainer const& containing_block, LayoutMode, LineBuilder* = nullptr); + private: virtual bool is_block_formatting_context() const final { return true; } @@ -58,8 +62,6 @@ private: void place_block_level_element_in_normal_flow_horizontally(Box const& child_box, BlockContainer const&); void place_block_level_element_in_normal_flow_vertically(Box const& child_box, BlockContainer const&); - void layout_floating_box(Box const& child, BlockContainer const& containing_block, LayoutMode); - void layout_list_item_marker(ListItemBox const&); enum class FloatSide { diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp index 4cf0baa192..a58f3d8b03 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp @@ -250,6 +250,11 @@ void InlineFormattingContext::generate_line_boxes(LayoutMode layout_mode) parent().add_absolutely_positioned_box(static_cast(*item.node)); break; + case InlineLevelIterator::Item::Type::FloatingElement: + if (is(*item.node)) + parent().layout_floating_box(static_cast(*item.node), containing_block(), layout_mode, &line_builder); + break; + case InlineLevelIterator::Item::Type::Text: { auto& text_node = verify_cast(*item.node); line_builder.break_if_needed(layout_mode, item.border_box_width(), item.should_force_break); diff --git a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp index 0088530355..c37aba6cf4 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp @@ -69,8 +69,10 @@ void InlineLevelIterator::exit_node_with_box_model_metrics() // This is similar to Layout::Node::next_in_pre_order() but will not descend into inline-block nodes. Layout::Node const* InlineLevelIterator::next_inline_node_in_pre_order(Layout::Node const& current, Layout::Node const* stay_within) { - if (current.first_child() && current.first_child()->is_inline() && !current.is_inline_block()) - return current.first_child(); + if (current.first_child() && current.first_child()->is_inline() && !current.is_inline_block()) { + if (!current.is_box() || !static_cast(current).is_out_of_flow(m_inline_formatting_context)) + return current.first_child(); + } Layout::Node const* node = ¤t; Layout::Node const* next = nullptr; @@ -101,12 +103,12 @@ void InlineLevelIterator::compute_next() return; do { m_next_node = next_inline_node_in_pre_order(*m_next_node, &m_container); - } while (m_next_node && !m_next_node->is_inline()); + } while (m_next_node && (!m_next_node->is_inline() && !m_next_node->is_out_of_flow(m_inline_formatting_context))); } void InlineLevelIterator::skip_to_next() { - if (m_next_node && is(*m_next_node) && !m_next_node->is_inline_block()) + if (m_next_node && is(*m_next_node) && !m_next_node->is_inline_block() && !m_next_node->is_out_of_flow(m_inline_formatting_context)) enter_node_with_box_model_metrics(static_cast(*m_next_node)); m_current_node = m_next_node; @@ -163,6 +165,15 @@ Optional InlineLevelIterator::next(float available_wi }; } + if (m_current_node->is_floating()) { + auto& node = *m_current_node; + skip_to_next(); + return Item { + .type = Item::Type::FloatingElement, + .node = &node, + }; + } + if (is(*m_current_node)) { skip_to_next(); return Item { diff --git a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.h b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.h index c4b6271293..e87c03005f 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.h +++ b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.h @@ -27,6 +27,7 @@ public: Element, ForcedBreak, AbsolutelyPositionedElement, + FloatingElement, }; Type type {}; Layout::Node const* node { nullptr }; diff --git a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp index d5bc9499c4..1aae5583b8 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp @@ -206,4 +206,20 @@ void LineBuilder::remove_last_line_if_empty() m_last_line_needs_update = false; } } + +void LineBuilder::adjust_last_line_after_inserting_floating_box(Badge, CSS::Float float_, float space_used_by_float) +{ + // NOTE: LineBuilder generates lines from left-to-right, so if we've just added a left-side float, + // that means every fragment already on this line has to move towards the right. + if (float_ == CSS::Float::Left && !m_containing_block_state.line_boxes.is_empty()) { + for (auto& fragment : m_containing_block_state.line_boxes.last().fragments()) + fragment.set_offset(fragment.offset().translated(space_used_by_float, 0)); + m_containing_block_state.line_boxes.last().m_width += space_used_by_float; + } + + m_available_width_for_current_line -= space_used_by_float; + if (m_available_width_for_current_line < 0) + m_available_width_for_current_line = 0; +} + } diff --git a/Userland/Libraries/LibWeb/Layout/LineBuilder.h b/Userland/Libraries/LibWeb/Layout/LineBuilder.h index cd0afca6a8..4c045400bc 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBuilder.h +++ b/Userland/Libraries/LibWeb/Layout/LineBuilder.h @@ -34,6 +34,10 @@ public: void remove_last_line_if_empty(); + float current_y() const { return m_current_y; } + + void adjust_last_line_after_inserting_floating_box(Badge, CSS::Float, float space_used_by_float); + private: void begin_new_line(bool increment_y);