diff options
author | Andreas Kling <kling@serenityos.org> | 2023-04-14 10:35:26 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2023-04-14 13:22:07 +0200 |
commit | 47c21cc3497066c0227268879ac4351d1975d2db (patch) | |
tree | 968e1843977ac213f6d5082104222ac4338f8f0f | |
parent | 7dd3c4a79c2a1d937eff90729bcbb6b47cb007b1 (diff) | |
download | serenity-47c21cc3497066c0227268879ac4351d1975d2db.zip |
LibWeb: Honor column-gap and row-gap CSS properties in flex layout
This isn't actually part of CSS-FLEXBOX-1, but all major engines honor
these properties in flex layout, and it's widely used on the web.
There's a bug open against the flexbox spec where fantasai says the
algorithm will be updated in CSS-FLEXBOX-2:
https://github.com/w3c/csswg-drafts/issues/2336
I've added comments to all the places where we adjust calculations for
gaps with "CSS-FLEXBOX-2" so we can find them easily. When that spec
becomes available, we can add proper spec links.
4 files changed, 66 insertions, 1 deletions
diff --git a/Tests/LibWeb/Layout/expected/flex/flex-gap-between-items-and-lines.txt b/Tests/LibWeb/Layout/expected/flex/flex-gap-between-items-and-lines.txt new file mode 100644 index 0000000000..d6b51ad414 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/flex/flex-gap-between-items-and-lines.txt @@ -0,0 +1,12 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer <html> at (1,1) content-size 798x268 children: not-inline + BlockContainer <body> at (10,10) content-size 780x250 children: not-inline + Box <div.flexbox> at (11,11) content-size 100x248 flex-container(row) children: not-inline + BlockContainer <div> at (12,12) content-size 30x30 flex-item children: not-inline + BlockContainer <div> at (64,12) content-size 30x30 flex-item children: not-inline + BlockContainer <div> at (12,84) content-size 30x30 flex-item children: not-inline + BlockContainer <div> at (64,84) content-size 30x30 flex-item children: not-inline + BlockContainer <div> at (12,156) content-size 30x30 flex-item children: not-inline + BlockContainer <div> at (64,156) content-size 30x30 flex-item children: not-inline + BlockContainer <div> at (12,228) content-size 30x30 flex-item children: not-inline + BlockContainer <div> at (64,228) content-size 30x30 flex-item children: not-inline diff --git a/Tests/LibWeb/Layout/input/flex/flex-gap-between-items-and-lines.html b/Tests/LibWeb/Layout/input/flex/flex-gap-between-items-and-lines.html new file mode 100644 index 0000000000..d2b66141fb --- /dev/null +++ b/Tests/LibWeb/Layout/input/flex/flex-gap-between-items-and-lines.html @@ -0,0 +1,18 @@ +<style> +* { + border: 1px solid black; + font: 16px SerenitySans; +} +.flexbox { + display: flex; + column-gap: 20px; + flex-wrap: wrap; + row-gap: 40px; + width: 100px; +} +.flexbox > div { + width: 30px; + height: 30px; +} +</style> +<div class=flexbox><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
\ No newline at end of file diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp index f2636de719..999e8185b3 100644 --- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp @@ -852,6 +852,8 @@ void FlexFormattingContext::collect_flex_items_into_flex_lines() } line.items.append(item); line_main_size += outer_hypothetical_main_size; + // CSS-FLEXBOX-2: Account for gap between flex items. + line_main_size += main_gap(); } m_flex_lines.append(move(line)); } @@ -873,6 +875,8 @@ void FlexFormattingContext::resolve_flexible_lengths_for_line(FlexLine& line) for (auto const& item : line.items) { sum += item.outer_hypothetical_main_size(); } + // CSS-FLEXBOX-2: Account for gap between flex items. + sum += main_gap() * (line.items.size() - 1); if (sum < inner_main_size(flex_container())) return FlexFactor::FlexGrowFactor; return FlexFactor::FlexShrinkFactor; @@ -917,6 +921,8 @@ void FlexFormattingContext::resolve_flexible_lengths_for_line(FlexLine& line) else sum += item.outer_flex_base_size(); } + // CSS-FLEXBOX-2: Account for gap between flex items. + sum += main_gap() * (line.items.size() - 1); return inner_main_size(flex_container()) - sum; }; auto const initial_free_space = calculate_remaining_free_space(); @@ -1224,6 +1230,9 @@ void FlexFormattingContext::distribute_any_remaining_free_space() + item.padding.main_before + item.padding.main_after; } + // CSS-FLEXBOX-2: Account for gap between flex items. + used_main_space += main_gap() * (flex_line.items.size() - 1); + if (flex_line.remaining_free_space > 0) { CSSPixels size_per_auto_margin = flex_line.remaining_free_space / (float)auto_margins; for (auto& item : flex_line.items) { @@ -1242,7 +1251,8 @@ void FlexFormattingContext::distribute_any_remaining_free_space() } // 12.2. - CSSPixels space_between_items = 0; + // CSS-FLEXBOX-2: Account for gap between items. + CSSPixels space_between_items = main_gap(); CSSPixels initial_offset = 0; auto number_of_items = flex_line.items.size(); @@ -1466,6 +1476,9 @@ void FlexFormattingContext::align_all_flex_lines() for (auto& line : m_flex_lines) sum_of_flex_line_cross_sizes += line.cross_size; + // CSS-FLEXBOX-2: Account for gap between flex lines. + sum_of_flex_line_cross_sizes += cross_gap() * (m_flex_lines.size() - 1); + CSSPixels start_of_current_line = 0; CSSPixels gap_size = 0; switch (flex_container().computed_values().align_content()) { @@ -1513,6 +1526,8 @@ void FlexFormattingContext::align_all_flex_lines() item.cross_offset += center_of_current_line; } start_of_current_line += flex_line.cross_size + gap_size; + // CSS-FLEXBOX-2: Account for gap between flex lines. + start_of_current_line += cross_gap(); } } } @@ -1672,6 +1687,8 @@ CSSPixels FlexFormattingContext::calculate_intrinsic_main_size_of_flex_container sum += result; } + // CSS-FLEXBOX-2: Account for gap between flex items. + sum += main_gap() * (flex_line.items.size() - 1); largest_sum = max(largest_sum, sum); } // 5. The flex container’s max-content size is the largest sum (among all the lines) of the afore-calculated sizes of all items within a single line. @@ -1747,6 +1764,8 @@ CSSPixels FlexFormattingContext::calculate_intrinsic_cross_size_of_flex_containe for (auto& flex_line : m_flex_lines) { sum_of_flex_line_cross_sizes += flex_line.cross_size; } + // CSS-FLEXBOX-2: Account for gap between flex lines. + sum_of_flex_line_cross_sizes += cross_gap() * (m_flex_lines.size() - 1); return sum_of_flex_line_cross_sizes; } @@ -2138,4 +2157,18 @@ float FlexFormattingContext::FlexLine::sum_of_scaled_flex_shrink_factor_of_unfro return sum; } +CSSPixels FlexFormattingContext::main_gap() const +{ + auto const& computed_values = flex_container().computed_values(); + auto gap = is_row_layout() ? computed_values.column_gap() : computed_values.row_gap(); + return gap.resolved(flex_container(), CSS::Length::make_px(inner_main_size(flex_container()))).to_px(flex_container()); +} + +CSSPixels FlexFormattingContext::cross_gap() const +{ + auto const& computed_values = flex_container().computed_values(); + auto gap = is_row_layout() ? computed_values.row_gap() : computed_values.column_gap(); + return gap.resolved(flex_container(), CSS::Length::make_px(inner_cross_size(flex_container()))).to_px(flex_container()); +} + } diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h index 383c2acd06..5d36ef3691 100644 --- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h @@ -112,6 +112,8 @@ private: float sum_of_scaled_flex_shrink_factor_of_unfrozen_items() const; }; + CSSPixels main_gap() const; + CSSPixels cross_gap() const; bool has_definite_main_size(Box const&) const; bool has_definite_cross_size(Box const&) const; CSSPixels inner_main_size(Box const&) const; |