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.
This commit is contained in:
Andreas Kling 2023-04-14 10:35:26 +02:00
parent 7dd3c4a79c
commit 47c21cc349
Notes: sideshowbarker 2024-07-17 04:32:07 +09:00
4 changed files with 66 additions and 1 deletions

View file

@ -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

View file

@ -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>

View file

@ -852,6 +852,8 @@ void FlexFormattingContext::collect_flex_items_into_flex_lines()
} }
line.items.append(item); line.items.append(item);
line_main_size += outer_hypothetical_main_size; 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)); 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) { for (auto const& item : line.items) {
sum += item.outer_hypothetical_main_size(); 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())) if (sum < inner_main_size(flex_container()))
return FlexFactor::FlexGrowFactor; return FlexFactor::FlexGrowFactor;
return FlexFactor::FlexShrinkFactor; return FlexFactor::FlexShrinkFactor;
@ -917,6 +921,8 @@ void FlexFormattingContext::resolve_flexible_lengths_for_line(FlexLine& line)
else else
sum += item.outer_flex_base_size(); 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; return inner_main_size(flex_container()) - sum;
}; };
auto const initial_free_space = calculate_remaining_free_space(); 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; + 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) { if (flex_line.remaining_free_space > 0) {
CSSPixels size_per_auto_margin = flex_line.remaining_free_space / (float)auto_margins; CSSPixels size_per_auto_margin = flex_line.remaining_free_space / (float)auto_margins;
for (auto& item : flex_line.items) { for (auto& item : flex_line.items) {
@ -1242,7 +1251,8 @@ void FlexFormattingContext::distribute_any_remaining_free_space()
} }
// 12.2. // 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; CSSPixels initial_offset = 0;
auto number_of_items = flex_line.items.size(); auto number_of_items = flex_line.items.size();
@ -1466,6 +1476,9 @@ void FlexFormattingContext::align_all_flex_lines()
for (auto& line : m_flex_lines) for (auto& line : m_flex_lines)
sum_of_flex_line_cross_sizes += line.cross_size; 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 start_of_current_line = 0;
CSSPixels gap_size = 0; CSSPixels gap_size = 0;
switch (flex_container().computed_values().align_content()) { switch (flex_container().computed_values().align_content()) {
@ -1513,6 +1526,8 @@ void FlexFormattingContext::align_all_flex_lines()
item.cross_offset += center_of_current_line; item.cross_offset += center_of_current_line;
} }
start_of_current_line += flex_line.cross_size + gap_size; 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; 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); largest_sum = max(largest_sum, sum);
} }
// 5. The flex containers max-content size is the largest sum (among all the lines) of the afore-calculated sizes of all items within a single line. // 5. The flex containers 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) { for (auto& flex_line : m_flex_lines) {
sum_of_flex_line_cross_sizes += flex_line.cross_size; 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; return sum_of_flex_line_cross_sizes;
} }
@ -2138,4 +2157,18 @@ float FlexFormattingContext::FlexLine::sum_of_scaled_flex_shrink_factor_of_unfro
return sum; 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());
}
} }

View file

@ -112,6 +112,8 @@ private:
float sum_of_scaled_flex_shrink_factor_of_unfrozen_items() const; 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_main_size(Box const&) const;
bool has_definite_cross_size(Box const&) const; bool has_definite_cross_size(Box const&) const;
CSSPixels inner_main_size(Box const&) const; CSSPixels inner_main_size(Box const&) const;