Skip to content

Commit 87dae0e

Browse files
CSS Grid auto-placement algorithm
Implements the CSS Grid item placement algorithm for grid-auto-flow: row with sparse packing, per the spec: https://www.w3.org/TR/css-grid-1/#auto-placement-algo - AutoPlacement: the four-step placement algorithm - LinePlacement: grid-line resolution, including invalid-input handling (line 0 is treated as auto; span 0 and negative spans clamp to 1) - OccupancyGrid: overlap tracking for placed items - C++ unit and integration tests Dense packing and column flow (grid-auto-flow: dense / column) are not yet implemented. optimise
1 parent 5ee00ec commit 87dae0e

8 files changed

Lines changed: 1355 additions & 1 deletion

File tree

tests/grid/AutoPlacementTest.cpp

Lines changed: 591 additions & 0 deletions
Large diffs are not rendered by default.

tests/grid/LinePlacementTest.cpp

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <gtest/gtest.h>
9+
#include <yoga/algorithm/grid/LinePlacement.h>
10+
11+
namespace facebook::yoga {
12+
13+
namespace {
14+
// explicitLineCount is the number of grid lines in the explicit grid, i.e. the
15+
// track count + 1. The examples below assume a 3-column grid (4 lines).
16+
constexpr int32_t kThreeColumnLineCount = 4;
17+
18+
void expectPlacement(
19+
const LinePlacement& placement,
20+
int32_t start,
21+
int32_t end,
22+
int32_t span) {
23+
EXPECT_EQ(placement.start, start);
24+
EXPECT_EQ(placement.end, end);
25+
EXPECT_EQ(placement.span, span);
26+
}
27+
} // namespace
28+
29+
// grid-column: 1 / 3 -> tracks [0, 2)
30+
TEST(LinePlacement, two_definite_lines) {
31+
expectPlacement(
32+
resolveLinePlacement(
33+
GridLine::fromInteger(1),
34+
GridLine::fromInteger(3),
35+
kThreeColumnLineCount),
36+
0,
37+
2,
38+
2);
39+
}
40+
41+
// grid-column: 3 / 1 -> reversed lines are swapped
42+
TEST(LinePlacement, reversed_lines_are_swapped) {
43+
expectPlacement(
44+
resolveLinePlacement(
45+
GridLine::fromInteger(3),
46+
GridLine::fromInteger(1),
47+
kThreeColumnLineCount),
48+
0,
49+
2,
50+
2);
51+
}
52+
53+
// grid-column: 2 / 2 -> equal lines, end line is dropped, spans one track
54+
TEST(LinePlacement, equal_lines_span_one_track) {
55+
expectPlacement(
56+
resolveLinePlacement(
57+
GridLine::fromInteger(2),
58+
GridLine::fromInteger(2),
59+
kThreeColumnLineCount),
60+
1,
61+
2,
62+
1);
63+
}
64+
65+
// grid-column: 2 / span 2
66+
TEST(LinePlacement, integer_start_with_span_end) {
67+
expectPlacement(
68+
resolveLinePlacement(
69+
GridLine::fromInteger(2), GridLine::span(2), kThreeColumnLineCount),
70+
1,
71+
3,
72+
2);
73+
}
74+
75+
// grid-column: span 2 / 3
76+
TEST(LinePlacement, span_start_with_integer_end) {
77+
expectPlacement(
78+
resolveLinePlacement(
79+
GridLine::span(2), GridLine::fromInteger(3), kThreeColumnLineCount),
80+
0,
81+
2,
82+
2);
83+
}
84+
85+
// grid-column: -1 -> the last line of a 3-column grid (line 4 -> index 3)
86+
TEST(LinePlacement, negative_line_counts_from_end) {
87+
expectPlacement(
88+
resolveLinePlacement(
89+
GridLine::fromInteger(-1),
90+
GridLine::auto_(),
91+
kThreeColumnLineCount),
92+
3,
93+
4,
94+
1);
95+
}
96+
97+
// grid-column: -5 / -4 -> resolves to the implicit track [-1, 0)
98+
TEST(LinePlacement, negative_range_before_explicit_grid) {
99+
expectPlacement(
100+
resolveLinePlacement(
101+
GridLine::fromInteger(-5),
102+
GridLine::fromInteger(-4),
103+
kThreeColumnLineCount),
104+
-1,
105+
0,
106+
1);
107+
}
108+
109+
// grid-column: auto -> placeholder, resolved later by auto-placement
110+
TEST(LinePlacement, auto_is_a_placeholder) {
111+
expectPlacement(
112+
resolveLinePlacement(
113+
GridLine::auto_(), GridLine::auto_(), kThreeColumnLineCount),
114+
-1,
115+
-1,
116+
1);
117+
}
118+
119+
// grid-column: span 0 is invalid; a span must be at least 1. The -1/-1 result
120+
// is the auto placeholder, so the item is auto-placed downstream (span 1).
121+
TEST(LinePlacement, zero_span_clamps_to_one) {
122+
expectPlacement(
123+
resolveLinePlacement(
124+
GridLine::span(0), GridLine::auto_(), kThreeColumnLineCount),
125+
-1,
126+
-1,
127+
1);
128+
}
129+
130+
// grid-column: span -2 is invalid; a span must be at least 1. The -1/-1 result
131+
// is the auto placeholder, so the item is auto-placed downstream (span 1).
132+
TEST(LinePlacement, negative_span_clamps_to_one) {
133+
expectPlacement(
134+
resolveLinePlacement(
135+
GridLine::span(-2), GridLine::auto_(), kThreeColumnLineCount),
136+
-1,
137+
-1,
138+
1);
139+
}
140+
141+
// grid-column: 0 is an invalid line; it resolves to the auto placeholder
142+
// (-1/-1), so the item is auto-placed downstream.
143+
TEST(LinePlacement, line_zero_is_treated_as_auto) {
144+
expectPlacement(
145+
resolveLinePlacement(
146+
GridLine::fromInteger(0),
147+
GridLine::auto_(),
148+
kThreeColumnLineCount),
149+
-1,
150+
-1,
151+
1);
152+
}
153+
154+
// grid-column: 0 / 2 -> the invalid 0 start becomes auto, leaving a definite end
155+
// at line 2, so the item resolves to track [0, 1).
156+
TEST(LinePlacement, line_zero_start_with_definite_end) {
157+
expectPlacement(
158+
resolveLinePlacement(
159+
GridLine::fromInteger(0),
160+
GridLine::fromInteger(2),
161+
kThreeColumnLineCount),
162+
0,
163+
1,
164+
1);
165+
}
166+
167+
} // namespace facebook::yoga

yoga/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ include(${YOGA_ROOT}/cmake/project-defaults.cmake)
2020

2121
file(GLOB SOURCES CONFIGURE_DEPENDS
2222
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
23-
${CMAKE_CURRENT_SOURCE_DIR}/**/*.cpp)
23+
${CMAKE_CURRENT_SOURCE_DIR}/**/*.cpp
24+
${CMAKE_CURRENT_SOURCE_DIR}/algorithm/grid/*.cpp)
2425

2526
add_library(yogacore STATIC ${SOURCES})
2627

0 commit comments

Comments
 (0)