summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2023-04-14 10:35:26 +0200
committerAndreas Kling <kling@serenityos.org>2023-04-14 13:22:07 +0200
commit47c21cc3497066c0227268879ac4351d1975d2db (patch)
tree968e1843977ac213f6d5082104222ac4338f8f0f
parent7dd3c4a79c2a1d937eff90729bcbb6b47cb007b1 (diff)
downloadserenity-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.
-rw-r--r--Tests/LibWeb/Layout/expected/flex/flex-gap-between-items-and-lines.txt12
-rw-r--r--Tests/LibWeb/Layout/input/flex/flex-gap-between-items-and-lines.html18
-rw-r--r--Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp35
-rw-r--r--Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h2
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;