diff options
author | Andreas Kling <kling@serenityos.org> | 2022-03-22 19:18:05 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-03-22 19:26:51 +0100 |
commit | de6f7f0029db116e81867245b4d38ad6fe4825bb (patch) | |
tree | c897cf38bed97b42475ad6c37da74a768a513c32 /Userland/Libraries/LibWeb | |
parent | fa64a7f6cc9a2b89b03e26030fa294684bb8c243 (diff) | |
download | serenity-de6f7f0029db116e81867245b4d38ad6fe4825bb.zip |
LibWeb: Support CSS floats in inline flow
CSS floats are now emitted by the InlineLevelIterator. When this
happens, IFC coordinates with the parent BFC to float the box to the
side, using the current LineBuilder state for vertical placement.
This makes the "instructions" text on Acid3 render as a single
contiguous flow of inline content.
Diffstat (limited to 'Userland/Libraries/LibWeb')
7 files changed, 57 insertions, 9 deletions
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 <LibWeb/Layout/Box.h> #include <LibWeb/Layout/InitialContainingBlock.h> #include <LibWeb/Layout/InlineFormattingContext.h> +#include <LibWeb/Layout/LineBuilder.h> #include <LibWeb/Layout/ListItemBox.h> #include <LibWeb/Layout/ListItemMarkerBox.h> #include <LibWeb/Layout/ReplacedBox.h> @@ -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<Layout::Box const&>(*item.node)); break; + case InlineLevelIterator::Item::Type::FloatingElement: + if (is<Box>(*item.node)) + parent().layout_floating_box(static_cast<Layout::Box const&>(*item.node), containing_block(), layout_mode, &line_builder); + break; + case InlineLevelIterator::Item::Type::Text: { auto& text_node = verify_cast<Layout::TextNode>(*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<Box const&>(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<Layout::NodeWithStyleAndBoxModelMetrics>(*m_next_node) && !m_next_node->is_inline_block()) + if (m_next_node && is<Layout::NodeWithStyleAndBoxModelMetrics>(*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<Layout::NodeWithStyleAndBoxModelMetrics const&>(*m_next_node)); m_current_node = m_next_node; @@ -163,6 +165,15 @@ Optional<InlineLevelIterator::Item> 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<Layout::BreakNode>(*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<BlockFormattingContext>, 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<BlockFormattingContext>, CSS::Float, float space_used_by_float); + private: void begin_new_line(bool increment_y); |