From 4d8bc168120300da8b739466626883dcdada2d61 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Mon, 4 Mar 2024 14:04:15 +0100 Subject: [PATCH] LibWeb: Respect "auto flow" property in grid layout Before this change, we only considering `grid-auto-flow` to determine whether a row or column should be added when there was not enough space in the implicit grid to fit the next unplaced item. Now, we also choose the direction in which the "auto placement cursor" is moved, based on the auto flow property. --- .../Layout/expected/grid/placement-10.txt | 33 ++++++ .../Layout/expected/grid/placement-5.txt | 33 ++++++ .../Layout/expected/grid/placement-6.txt | 33 ++++++ .../Layout/expected/grid/placement-7.txt | 33 ++++++ .../Layout/expected/grid/placement-8.txt | 33 ++++++ .../Layout/expected/grid/placement-9.txt | 33 ++++++ .../Layout/input/grid/placement-10.html | 19 ++++ .../LibWeb/Layout/input/grid/placement-5.html | 12 ++ .../LibWeb/Layout/input/grid/placement-6.html | 12 ++ .../LibWeb/Layout/input/grid/placement-7.html | 17 +++ .../LibWeb/Layout/input/grid/placement-8.html | 18 +++ .../LibWeb/Layout/input/grid/placement-9.html | 19 ++++ .../LibWeb/Layout/GridFormattingContext.cpp | 103 +++++++++++------- .../LibWeb/Layout/GridFormattingContext.h | 7 ++ 14 files changed, 363 insertions(+), 42 deletions(-) create mode 100644 Tests/LibWeb/Layout/expected/grid/placement-10.txt create mode 100644 Tests/LibWeb/Layout/expected/grid/placement-5.txt create mode 100644 Tests/LibWeb/Layout/expected/grid/placement-6.txt create mode 100644 Tests/LibWeb/Layout/expected/grid/placement-7.txt create mode 100644 Tests/LibWeb/Layout/expected/grid/placement-8.txt create mode 100644 Tests/LibWeb/Layout/expected/grid/placement-9.txt create mode 100644 Tests/LibWeb/Layout/input/grid/placement-10.html create mode 100644 Tests/LibWeb/Layout/input/grid/placement-5.html create mode 100644 Tests/LibWeb/Layout/input/grid/placement-6.html create mode 100644 Tests/LibWeb/Layout/input/grid/placement-7.html create mode 100644 Tests/LibWeb/Layout/input/grid/placement-8.html create mode 100644 Tests/LibWeb/Layout/input/grid/placement-9.html diff --git a/Tests/LibWeb/Layout/expected/grid/placement-10.txt b/Tests/LibWeb/Layout/expected/grid/placement-10.txt new file mode 100644 index 00000000000..dca0802ac63 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/grid/placement-10.txt @@ -0,0 +1,33 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x516 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x500 children: not-inline + Box at (8,8) content-size 500x500 [GFC] children: not-inline + BlockContainer at (8,8) content-size 100x300 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [8,8 6.34375x17] baseline: 13.296875 + "1" + TextNode <#text> + BlockContainer at (108,8) content-size 100x100 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [108,8 8.8125x17] baseline: 13.296875 + "2" + TextNode <#text> + BlockContainer at (108,108) content-size 100x100 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [108,108 9.09375x17] baseline: 13.296875 + "3" + TextNode <#text> + BlockContainer at (108,208) content-size 100x100 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [108,208 7.75x17] baseline: 13.296875 + "4" + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x516] + PaintableWithLines (BlockContainer) [8,8 784x500] + PaintableBox (Box
.grid) [8,8 500x500] + PaintableWithLines (BlockContainer
#a) [8,8 100x300] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#b) [108,8 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#c) [108,108 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#d) [108,208 100x100] + TextPaintable (TextNode<#text>) diff --git a/Tests/LibWeb/Layout/expected/grid/placement-5.txt b/Tests/LibWeb/Layout/expected/grid/placement-5.txt new file mode 100644 index 00000000000..b74c249bc65 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/grid/placement-5.txt @@ -0,0 +1,33 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (1,1) content-size 798x220 [BFC] children: not-inline + BlockContainer at (10,10) content-size 780x202 children: not-inline + Box at (11,11) content-size 778x200 [GFC] children: not-inline + BlockContainer at (12,12) content-size 98x98 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [12,12 6.34375x17] baseline: 13.296875 + "1" + TextNode <#text> + BlockContainer at (12,112) content-size 98x98 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [12,112 8.8125x17] baseline: 13.296875 + "2" + TextNode <#text> + BlockContainer at (112,12) content-size 98x98 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [112,12 9.09375x17] baseline: 13.296875 + "3" + TextNode <#text> + BlockContainer at (112,112) content-size 98x98 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [112,112 7.75x17] baseline: 13.296875 + "4" + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x222] + PaintableWithLines (BlockContainer) [9,9 782x204] + PaintableBox (Box
.grid) [10,10 780x202] + PaintableWithLines (BlockContainer
#a) [11,11 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#b) [11,111 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#a) [111,11 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#b) [111,111 100x100] + TextPaintable (TextNode<#text>) diff --git a/Tests/LibWeb/Layout/expected/grid/placement-6.txt b/Tests/LibWeb/Layout/expected/grid/placement-6.txt new file mode 100644 index 00000000000..fe00cba465f --- /dev/null +++ b/Tests/LibWeb/Layout/expected/grid/placement-6.txt @@ -0,0 +1,33 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (1,1) content-size 798x220 [BFC] children: not-inline + BlockContainer at (10,10) content-size 780x202 children: not-inline + Box at (11,11) content-size 778x200 [GFC] children: not-inline + BlockContainer at (12,12) content-size 98x98 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [12,12 6.34375x17] baseline: 13.296875 + "1" + TextNode <#text> + BlockContainer at (112,12) content-size 98x98 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [112,12 8.8125x17] baseline: 13.296875 + "2" + TextNode <#text> + BlockContainer at (12,112) content-size 98x98 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [12,112 9.09375x17] baseline: 13.296875 + "3" + TextNode <#text> + BlockContainer at (112,112) content-size 98x98 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [112,112 7.75x17] baseline: 13.296875 + "4" + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x222] + PaintableWithLines (BlockContainer) [9,9 782x204] + PaintableBox (Box
.grid) [10,10 780x202] + PaintableWithLines (BlockContainer
#a) [11,11 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#b) [111,11 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#a) [11,111 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#b) [111,111 100x100] + TextPaintable (TextNode<#text>) diff --git a/Tests/LibWeb/Layout/expected/grid/placement-7.txt b/Tests/LibWeb/Layout/expected/grid/placement-7.txt new file mode 100644 index 00000000000..8bc68d82abb --- /dev/null +++ b/Tests/LibWeb/Layout/expected/grid/placement-7.txt @@ -0,0 +1,33 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x216 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x200 children: not-inline + Box at (8,8) content-size 784x200 [GFC] children: not-inline + BlockContainer at (8,8) content-size 100x100 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [8,8 6.34375x17] baseline: 13.296875 + "1" + TextNode <#text> + BlockContainer at (108,8) content-size 387.453125x200 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [108,8 8.8125x17] baseline: 13.296875 + "2" + TextNode <#text> + BlockContainer at (495.453125,8) content-size 296.546875x100 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [495.453125,8 9.09375x17] baseline: 13.296875 + "3" + TextNode <#text> + BlockContainer at (495.453125,108) content-size 296.546875x100 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [495.453125,108 7.75x17] baseline: 13.296875 + "4" + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x216] + PaintableWithLines (BlockContainer) [8,8 784x200] + PaintableBox (Box
.grid) [8,8 784x200] + PaintableWithLines (BlockContainer
#a) [8,8 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#b) [108,8 387.453125x200] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#c) [495.453125,8 296.546875x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#d) [495.453125,108 296.546875x100] + TextPaintable (TextNode<#text>) diff --git a/Tests/LibWeb/Layout/expected/grid/placement-8.txt b/Tests/LibWeb/Layout/expected/grid/placement-8.txt new file mode 100644 index 00000000000..59e8e2305d9 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/grid/placement-8.txt @@ -0,0 +1,33 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (1,1) content-size 798x520 [BFC] children: not-inline + BlockContainer at (10,10) content-size 780x502 children: not-inline + Box at (11,11) content-size 500x500 [GFC] children: not-inline + BlockContainer at (12,12) content-size 98x105.59375 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [12,12 6.34375x17] baseline: 13.296875 + "1" + TextNode <#text> + BlockContainer at (12,119.59375) content-size 498x282.78125 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [12,119.59375 8.8125x17] baseline: 13.296875 + "2" + TextNode <#text> + BlockContainer at (12,404.375) content-size 98x105.59375 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [12,404.375 9.09375x17] baseline: 13.296875 + "3" + TextNode <#text> + BlockContainer at (112,404.375) content-size 98x105.59375 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [112,404.375 7.75x17] baseline: 13.296875 + "4" + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x522] + PaintableWithLines (BlockContainer) [9,9 782x504] + PaintableBox (Box
.grid) [10,10 502x502] + PaintableWithLines (BlockContainer
#a) [11,11 100x107.59375] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#b) [11,118.59375 500x284.78125] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#c) [11,403.375 100x107.59375] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#d) [111,403.375 100x107.59375] + TextPaintable (TextNode<#text>) diff --git a/Tests/LibWeb/Layout/expected/grid/placement-9.txt b/Tests/LibWeb/Layout/expected/grid/placement-9.txt new file mode 100644 index 00000000000..dca0802ac63 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/grid/placement-9.txt @@ -0,0 +1,33 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x516 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x500 children: not-inline + Box at (8,8) content-size 500x500 [GFC] children: not-inline + BlockContainer at (8,8) content-size 100x300 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [8,8 6.34375x17] baseline: 13.296875 + "1" + TextNode <#text> + BlockContainer at (108,8) content-size 100x100 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [108,8 8.8125x17] baseline: 13.296875 + "2" + TextNode <#text> + BlockContainer at (108,108) content-size 100x100 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [108,108 9.09375x17] baseline: 13.296875 + "3" + TextNode <#text> + BlockContainer at (108,208) content-size 100x100 [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [108,208 7.75x17] baseline: 13.296875 + "4" + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x516] + PaintableWithLines (BlockContainer) [8,8 784x500] + PaintableBox (Box
.grid) [8,8 500x500] + PaintableWithLines (BlockContainer
#a) [8,8 100x300] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#b) [108,8 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#c) [108,108 100x100] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
#d) [108,208 100x100] + TextPaintable (TextNode<#text>) diff --git a/Tests/LibWeb/Layout/input/grid/placement-10.html b/Tests/LibWeb/Layout/input/grid/placement-10.html new file mode 100644 index 00000000000..ea9f464e58b --- /dev/null +++ b/Tests/LibWeb/Layout/input/grid/placement-10.html @@ -0,0 +1,19 @@ +
1
2
3
4
\ No newline at end of file diff --git a/Tests/LibWeb/Layout/input/grid/placement-5.html b/Tests/LibWeb/Layout/input/grid/placement-5.html new file mode 100644 index 00000000000..69ee3815dec --- /dev/null +++ b/Tests/LibWeb/Layout/input/grid/placement-5.html @@ -0,0 +1,12 @@ +
1
2
3
4
\ No newline at end of file diff --git a/Tests/LibWeb/Layout/input/grid/placement-6.html b/Tests/LibWeb/Layout/input/grid/placement-6.html new file mode 100644 index 00000000000..014b34299d4 --- /dev/null +++ b/Tests/LibWeb/Layout/input/grid/placement-6.html @@ -0,0 +1,12 @@ +
1
2
3
4
\ No newline at end of file diff --git a/Tests/LibWeb/Layout/input/grid/placement-7.html b/Tests/LibWeb/Layout/input/grid/placement-7.html new file mode 100644 index 00000000000..b0fb6ef7d61 --- /dev/null +++ b/Tests/LibWeb/Layout/input/grid/placement-7.html @@ -0,0 +1,17 @@ +
1
2
3
4
\ No newline at end of file diff --git a/Tests/LibWeb/Layout/input/grid/placement-8.html b/Tests/LibWeb/Layout/input/grid/placement-8.html new file mode 100644 index 00000000000..2affb2c2a13 --- /dev/null +++ b/Tests/LibWeb/Layout/input/grid/placement-8.html @@ -0,0 +1,18 @@ +
1
2
3
4
\ No newline at end of file diff --git a/Tests/LibWeb/Layout/input/grid/placement-9.html b/Tests/LibWeb/Layout/input/grid/placement-9.html new file mode 100644 index 00000000000..86da04fc7b3 --- /dev/null +++ b/Tests/LibWeb/Layout/input/grid/placement-9.html @@ -0,0 +1,19 @@ +
1
2
3
4
\ No newline at end of file diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp index 5f39fa00331..5d1b8b942a3 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.cpp @@ -558,17 +558,43 @@ void GridFormattingContext::place_item_with_column_position(Box const& child_box .column_span = column_span }); } +FoundUnoccupiedPlace OccupationGrid::find_unoccupied_place(GridDimension dimension, int& column_index, int& row_index, int column_span, int row_span) const +{ + if (dimension == GridDimension::Column) { + while (row_index <= max_row_index()) { + while (column_index <= max_column_index()) { + auto enough_span_for_span = column_index + column_span - 1 <= max_column_index(); + if (enough_span_for_span && !is_occupied(column_index, row_index)) + return FoundUnoccupiedPlace::Yes; + column_index++; + } + row_index++; + column_index = min_column_index(); + } + } else { + while (column_index <= max_column_index()) { + while (row_index <= max_row_index()) { + auto enough_span_for_span = row_index + row_span - 1 <= max_row_index(); + if (enough_span_for_span && !is_occupied(column_index, row_index)) + return FoundUnoccupiedPlace::Yes; + row_index++; + } + column_index++; + row_index = min_row_index(); + } + } + + return FoundUnoccupiedPlace::No; +} + void GridFormattingContext::place_item_with_no_declared_position(Box const& child_box, int& auto_placement_cursor_x, int& auto_placement_cursor_y) { - auto const& grid_row_start = child_box.computed_values().grid_row_start(); - auto const& grid_row_end = child_box.computed_values().grid_row_end(); - auto const& grid_column_start = child_box.computed_values().grid_column_start(); - auto const& grid_column_end = child_box.computed_values().grid_column_end(); + auto const& computed_values = child_box.computed_values(); + auto const& grid_row_start = computed_values.grid_row_start(); + auto const& grid_row_end = computed_values.grid_row_end(); + auto const& grid_column_start = computed_values.grid_column_start(); + auto const& grid_column_end = computed_values.grid_column_end(); - // 4.1.2.1. Increment the column position of the auto-placement cursor until either this item's grid - // area does not overlap any occupied grid cells, or the cursor's column position, plus the item's - // column span, overflow the number of columns in the implicit grid, as determined earlier in this - // algorithm. auto column_start = 0; size_t column_span = 1; if (grid_column_start.is_span()) @@ -581,47 +607,40 @@ void GridFormattingContext::place_item_with_no_declared_position(Box const& chil row_span = grid_row_start.span(); else if (grid_row_end.is_span()) row_span = grid_row_end.span(); - auto found_unoccupied_area = false; auto const& auto_flow = grid_container().computed_values().grid_auto_flow(); + auto dimension = auto_flow.row ? GridDimension::Column : GridDimension::Row; - while (true) { - while (auto_placement_cursor_x <= m_occupation_grid.max_column_index()) { - if (auto_placement_cursor_x + static_cast(column_span) <= m_occupation_grid.max_column_index() + 1) { - auto found_all_available = true; - for (size_t span_index = 0; span_index < column_span; span_index++) { - if (m_occupation_grid.is_occupied(auto_placement_cursor_x + span_index, auto_placement_cursor_y)) - found_all_available = false; - } - if (found_all_available) { - found_unoccupied_area = true; - column_start = auto_placement_cursor_x; - row_start = auto_placement_cursor_y; - break; - } - } + // 4.1.2.1. Increment the column position of the auto-placement cursor until either this item's grid + // area does not overlap any occupied grid cells, or the cursor's column position, plus the item's + // column span, overflow the number of columns in the implicit grid, as determined earlier in this + // algorithm. + auto found_unoccupied_area = m_occupation_grid.find_unoccupied_place(dimension, auto_placement_cursor_x, auto_placement_cursor_y, column_span, row_span); + // 4.1.2.2. If a non-overlapping position was found in the previous step, set the item's row-start + // and column-start lines to the cursor's position. Otherwise, increment the auto-placement cursor's + // row position (creating new rows in the implicit grid as necessary), set its column position to the + // start-most column line in the implicit grid, and return to the previous step. + if (found_unoccupied_area == FoundUnoccupiedPlace::Yes) { + column_start = auto_placement_cursor_x; + row_start = auto_placement_cursor_y; + + auto_placement_cursor_x += column_span - 1; + auto_placement_cursor_y += row_span - 1; + + if (dimension == GridDimension::Column) { auto_placement_cursor_x++; + auto_placement_cursor_y = m_occupation_grid.min_row_index(); + } else { + auto_placement_cursor_y++; + auto_placement_cursor_x = m_occupation_grid.min_column_index(); } + } else { + column_start = auto_placement_cursor_x; + row_start = auto_placement_cursor_y; - if (found_unoccupied_area) { - break; - } - - // 4.1.2.2. If a non-overlapping position was found in the previous step, set the item's row-start - // and column-start lines to the cursor's position. Otherwise, increment the auto-placement cursor's - // row position (creating new rows in the implicit grid as necessary), set its column position to the - // start-most column line in the implicit grid, and return to the previous step. - if (!found_unoccupied_area) { - if (auto_flow.row) { - auto_placement_cursor_x = m_occupation_grid.min_column_index(); - auto_placement_cursor_y++; - } else { - m_occupation_grid.set_max_column_index(auto_placement_cursor_x); - auto_placement_cursor_x = 0; - auto_placement_cursor_y = m_occupation_grid.min_row_index(); - } - } + auto_placement_cursor_x += column_span - 1; + auto_placement_cursor_y += row_span - 1; } m_occupation_grid.set_occupied(column_start, column_start + column_span, row_start, row_start + row_span); diff --git a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h index 1c93b3dcf94..298b9b53ce9 100644 --- a/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/GridFormattingContext.h @@ -53,6 +53,11 @@ struct GridItem { [[nodiscard]] int gap_adjusted_column(Box const& grid_box) const; }; +enum class FoundUnoccupiedPlace { + No, + Yes +}; + class OccupationGrid { public: OccupationGrid(size_t columns_count, size_t rows_count) @@ -83,6 +88,8 @@ public: bool is_occupied(int column_index, int row_index) const; + FoundUnoccupiedPlace find_unoccupied_place(GridDimension dimension, int& column_index, int& row_index, int column_span, int row_span) const; + private: HashTable m_occupation_grid;