⎇ 09c7a718b2eaee87aa9d0505d5a98bd1d33a32db
src/compute/grid/placement.rs
Historia
Quién tocó qué
crudo
Fix clippy lints (2024-03) (#630)
1//! Implements placing items in the grid and resolving the implicit grid.
2//! <https://www.w3.org/TR/css-grid-1/#placement>
3use super::types::{CellOccupancyMatrix, CellOccupancyState, GridItem};
4use super::OriginZeroLine;
5use crate::geometry::Line;
6use crate::geometry::{AbsoluteAxis, InBothAbsAxis};
7use crate::style::{AlignItems, GridAutoFlow, OriginZeroGridPlacement, Style};
8use crate::tree::NodeId;
9use crate::util::sys::Vec;
10
11/// 8.5. Grid Item Placement Algorithm
12/// Place items into the grid, generating new rows/column into the implicit grid as required
13///
14/// [Specification](https://www.w3.org/TR/css-grid-2/#auto-placement-algo)
15pub(super) fn place_grid_items<'a, ChildIter>(
16 cell_occupancy_matrix: &mut CellOccupancyMatrix,
17 items: &mut Vec<GridItem>,
18 children_iter: impl Fn() -> ChildIter,
19 grid_auto_flow: GridAutoFlow,
20 align_items: AlignItems,
21 justify_items: AlignItems,
22) where
23 ChildIter: Iterator<Item = (usize, NodeId, &'a Style)>,
24{
25 let primary_axis = grid_auto_flow.primary_axis();
26 let secondary_axis = primary_axis.other_axis();
27
28 let map_child_style_to_origin_zero_placement = {
29 let explicit_col_count = cell_occupancy_matrix.track_counts(AbsoluteAxis::Horizontal).explicit;
30 let explicit_row_count = cell_occupancy_matrix.track_counts(AbsoluteAxis::Vertical).explicit;
31 move |(index, node, style): (usize, NodeId, &'a Style)| -> (_, _, _, &'a Style) {
32 let origin_zero_placement = InBothAbsAxis {
33 horizontal: style.grid_column.map(|placement| placement.into_origin_zero_placement(explicit_col_count)),
34 vertical: style.grid_row.map(|placement| placement.into_origin_zero_placement(explicit_row_count)),
35 };
36 (index, node, origin_zero_placement, style)
37 }
38 };
39
40 // 1. Place children with definite positions
41 let mut idx = 0;
42 children_iter()
43 .filter(|(_, _, child_style)| child_style.grid_row.is_definite() && child_style.grid_column.is_definite())
44 .map(map_child_style_to_origin_zero_placement)
45 .for_each(|(index, child_node, child_placement, style)| {
46 idx += 1;
47 #[cfg(test)]
48 println!("Definite Item {idx}\n==============");
49
50 let (row_span, col_span) = place_definite_grid_item(child_placement, primary_axis);
51 record_grid_placement(
52 cell_occupancy_matrix,
53 items,
54 child_node,
55 index,
56 style,
57 align_items,
58 justify_items,
59 primary_axis,
60 row_span,
61 col_span,
62 CellOccupancyState::DefinitelyPlaced,
63 );
64 });
65
66 // 2. Place remaining children with definite secondary axis positions
67 let mut idx = 0;
68 children_iter()
69 .filter(|(_, _, child_style)| {
70 child_style.grid_placement(secondary_axis).is_definite()
71 && !child_style.grid_placement(primary_axis).is_definite()
72 })
73 .map(map_child_style_to_origin_zero_placement)
74 .for_each(|(index, child_node, child_placement, style)| {
75 idx += 1;
76 #[cfg(test)]
77 println!("Definite Secondary Item {idx}\n==============");
78
79 let (primary_span, secondary_span) =
80 place_definite_secondary_axis_item(&*cell_occupancy_matrix, child_placement, grid_auto_flow);
81
82 record_grid_placement(
83 cell_occupancy_matrix,
84 items,
85 child_node,
86 index,
87 style,
88 align_items,
89 justify_items,
90 primary_axis,
91 primary_span,
92 secondary_span,
93 CellOccupancyState::AutoPlaced,
94 );
95 });
96
97 // 3. Determine the number of columns in the implicit grid
98 // By the time we get to this point in the execution, this is actually already accounted for:
99 //
100 // 3.1 Start with the columns from the explicit grid
101 // => Handled by grid size estimate which is used to pre-size the GridOccupancyMatrix
102 //
103 // 3.2 Among all the items with a definite column position (explicitly positioned items, items positioned in the previous step,
104 // and items not yet positioned but with a definite column) add columns to the beginning and end of the implicit grid as necessary
105 // to accommodate those items.
106 // => Handled by expand_to_fit_range which expands the GridOccupancyMatrix as necessary
107 // -> Called by mark_area_as
108 // -> Called by record_grid_placement
109 //
110 // 3.3 If the largest column span among all the items without a definite column position is larger than the width of
111 // the implicit grid, add columns to the end of the implicit grid to accommodate that column span.
112 // => Handled by grid size estimate which is used to pre-size the GridOccupancyMatrix
113
114 // 4. Position the remaining grid items
115 // (which either have definite position only in the secondary axis or indefinite positions in both axis)
116 let primary_axis = grid_auto_flow.primary_axis();
117 let secondary_axis = primary_axis.other_axis();
118 let primary_neg_tracks = cell_occupancy_matrix.track_counts(primary_axis).negative_implicit as i16;
119 let secondary_neg_tracks = cell_occupancy_matrix.track_counts(secondary_axis).negative_implicit as i16;
120 let grid_start_position = (OriginZeroLine(-primary_neg_tracks), OriginZeroLine(-secondary_neg_tracks));
121 let mut grid_position = grid_start_position;
122 let mut idx = 0;
123 children_iter()
124 .filter(|(_, _, child_style)| !child_style.grid_placement(secondary_axis).is_definite())
125 .map(map_child_style_to_origin_zero_placement)
126 .for_each(|(index, child_node, child_placement, style)| {
127 idx += 1;
128 #[cfg(test)]
129 println!("\nAuto Item {idx}\n==============");
130
131 // Compute placement
132 let (primary_span, secondary_span) = place_indefinitely_positioned_item(
133 &*cell_occupancy_matrix,
134 child_placement,
135 grid_auto_flow,
136 grid_position,
137 );
138
139 // Record item
140 record_grid_placement(
141 cell_occupancy_matrix,
142 items,
143 child_node,
144 index,
145 style,
146 align_items,
147 justify_items,
148 primary_axis,
149 primary_span,
150 secondary_span,
151 CellOccupancyState::AutoPlaced,
152 );
153
154 // If using the "dense" placement algorithm then reset the grid position back to grid_start_position ready for the next item
155 // Otherwise set it to the position of the current item so that the next item it placed after it.
156 grid_position = match grid_auto_flow.is_dense() {
157 true => grid_start_position,
158 false => (primary_span.end, secondary_span.start),
159 }
160 });
161}
162
163/// 8.5. Grid Item Placement Algorithm
164/// Place a single definitely placed item into the grid
165fn place_definite_grid_item(
166 placement: InBothAbsAxis<Line<OriginZeroGridPlacement>>,
167 primary_axis: AbsoluteAxis,
168) -> (Line<OriginZeroLine>, Line<OriginZeroLine>) {
169 // Resolve spans to tracks
170 let primary_span = placement.get(primary_axis).resolve_definite_grid_lines();
171 let secondary_span = placement.get(primary_axis.other_axis()).resolve_definite_grid_lines();
172
173 (primary_span, secondary_span)
174}
175
176/// 8.5. Grid Item Placement Algorithm
177/// Step 2. Place remaining children with definite secondary axis positions
178fn place_definite_secondary_axis_item(
179 cell_occupancy_matrix: &CellOccupancyMatrix,
180 placement: InBothAbsAxis<Line<OriginZeroGridPlacement>>,
181 auto_flow: GridAutoFlow,
182) -> (Line<OriginZeroLine>, Line<OriginZeroLine>) {
183 let primary_axis = auto_flow.primary_axis();
184 let secondary_axis = primary_axis.other_axis();
185
186 let secondary_axis_placement = placement.get(secondary_axis).resolve_definite_grid_lines();
187 let primary_axis_grid_start_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_start_line();
188 let starting_position = match auto_flow.is_dense() {
189 true => primary_axis_grid_start_line,
190 false => cell_occupancy_matrix
191 .last_of_type(primary_axis, secondary_axis_placement.start, CellOccupancyState::AutoPlaced)
192 .unwrap_or(primary_axis_grid_start_line),
193 };
194
195 let mut position: OriginZeroLine = starting_position;
196 loop {
197 let primary_axis_placement = placement.get(primary_axis).resolve_indefinite_grid_tracks(position);
198
199 let does_fit = cell_occupancy_matrix.line_area_is_unoccupied(
200 primary_axis,
201 primary_axis_placement,
202 secondary_axis_placement,
203 );
204
205 if does_fit {
206 return (primary_axis_placement, secondary_axis_placement);
207 } else {
208 position += 1;
209 }
210 }
211}
212
213/// 8.5. Grid Item Placement Algorithm
214/// Step 4. Position the remaining grid items.
215fn place_indefinitely_positioned_item(
216 cell_occupancy_matrix: &CellOccupancyMatrix,
217 placement: InBothAbsAxis<Line<OriginZeroGridPlacement>>,
218 auto_flow: GridAutoFlow,
219 grid_position: (OriginZeroLine, OriginZeroLine),
220) -> (Line<OriginZeroLine>, Line<OriginZeroLine>) {
221 let primary_axis = auto_flow.primary_axis();
222
223 let primary_placement_style = placement.get(primary_axis);
224 let secondary_placement_style = placement.get(primary_axis.other_axis());
225
226 let primary_span = primary_placement_style.indefinite_span();
227 let secondary_span = secondary_placement_style.indefinite_span();
228 let has_definite_primary_axis_position = primary_placement_style.is_definite();
229 let primary_axis_grid_start_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_start_line();
230 let primary_axis_grid_end_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_end_line();
231 let secondary_axis_grid_start_line =
232 cell_occupancy_matrix.track_counts(primary_axis.other_axis()).implicit_start_line();
233
234 let line_area_is_occupied = |primary_span, secondary_span| {
235 !cell_occupancy_matrix.line_area_is_unoccupied(primary_axis, primary_span, secondary_span)
236 };
237
238 let (mut primary_idx, mut secondary_idx) = grid_position;
239
240 if has_definite_primary_axis_position {
241 let definite_primary_placement = primary_placement_style.resolve_definite_grid_lines();
242 let defined_primary_idx = definite_primary_placement.start;
243
244 // Compute starting position for search
245 if defined_primary_idx < primary_idx && secondary_idx != secondary_axis_grid_start_line {
246 secondary_idx = secondary_axis_grid_start_line;
247 primary_idx = defined_primary_idx + 1;
248 } else {
249 primary_idx = defined_primary_idx;
250 }
251
252 // Item has fixed primary axis position: so we simply increment the secondary axis position
253 // until we find a space that the item fits in
254 loop {
255 let primary_span = Line { start: primary_idx, end: primary_idx + primary_span };
256 let secondary_span = Line { start: secondary_idx, end: secondary_idx + secondary_span };
257
258 // If area is occupied, increment the index and try again
259 if line_area_is_occupied(primary_span, secondary_span) {
260 secondary_idx += 1;
261 continue;
262 }
263
264 // Once we find a free space, return that position
265 return (primary_span, secondary_span);
266 }
267 } else {
268 // Item does not have any fixed axis, so we search along the primary axis until we hit the end of the already
269 // existent tracks, and then we reset the primary axis back to zero and increment the secondary axis index.
270 // We continue in this vein until we find a space that the item fits in.
271 loop {
272 let primary_span = Line { start: primary_idx, end: primary_idx + primary_span };
273 let secondary_span = Line { start: secondary_idx, end: secondary_idx + secondary_span };
274
275 // If the primary index is out of bounds, then increment the secondary index and reset the primary
276 // index back to the start of the grid
277 let primary_out_of_bounds = primary_span.end > primary_axis_grid_end_line;
278 if primary_out_of_bounds {
279 secondary_idx += 1;
280 primary_idx = primary_axis_grid_start_line;
281 continue;
282 }
283
284 // If area is occupied, increment the primary index and try again
285 if line_area_is_occupied(primary_span, secondary_span) {
286 primary_idx += 1;
287 continue;
288 }
289
290 // Once we find a free space that's in bounds, return that position
291 return (primary_span, secondary_span);
292 }
293 }
294}
295
296/// Record the grid item in both CellOccupancyMatric and the GridItems list
297/// once a definite placement has been determined
298#[allow(clippy::too_many_arguments)]
299fn record_grid_placement(
300 cell_occupancy_matrix: &mut CellOccupancyMatrix,
301 items: &mut Vec<GridItem>,
302 node: NodeId,
303 index: usize,
304 style: &Style,
305 parent_align_items: AlignItems,
306 parent_justify_items: AlignItems,
307 primary_axis: AbsoluteAxis,
308 primary_span: Line<OriginZeroLine>,
309 secondary_span: Line<OriginZeroLine>,
310 placement_type: CellOccupancyState,
311) {
312 #[cfg(test)]
313 println!("BEFORE placement:");
314 #[cfg(test)]
315 println!("{cell_occupancy_matrix:?}");
316
317 // Mark area of grid as occupied
318 cell_occupancy_matrix.mark_area_as(primary_axis, primary_span, secondary_span, placement_type);
319
320 // Create grid item
321 let (col_span, row_span) = match primary_axis {
322 AbsoluteAxis::Horizontal => (primary_span, secondary_span),
323 AbsoluteAxis::Vertical => (secondary_span, primary_span),
324 };
325 items.push(GridItem::new_with_placement_style_and_order(
326 node,
327 col_span,
328 row_span,
329 style,
330 parent_align_items,
331 parent_justify_items,
332 index as u16,
333 ));
334
335 #[cfg(test)]
336 println!("AFTER placement:");
337 #[cfg(test)]
338 println!("{cell_occupancy_matrix:?}");
339 #[cfg(test)]
340 println!("\n");
341}
342
343#[cfg(test)]
344mod tests {
345
346 mod test_placement_algorithm {
347 use crate::compute::grid::implicit_grid::compute_grid_size_estimate;
348 use crate::compute::grid::types::TrackCounts;
349 use crate::compute::grid::util::*;
350 use crate::compute::grid::CellOccupancyMatrix;
351 use crate::prelude::*;
352 use crate::style::GridAutoFlow;
353
354 use super::super::place_grid_items;
355
356 type ExpectedPlacement = (i16, i16, i16, i16);
357
358 fn placement_test_runner(
359 explicit_col_count: u16,
360 explicit_row_count: u16,
361 children: Vec<(usize, Style, ExpectedPlacement)>,
362 expected_col_counts: TrackCounts,
363 expected_row_counts: TrackCounts,
364 flow: GridAutoFlow,
365 ) {
366 // Setup test
367 let children_iter = || children.iter().map(|(index, style, _)| (*index, NodeId::from(*index), style));
368 let child_styles_iter = children.iter().map(|(_, style, _)| style);
369 let estimated_sizes = compute_grid_size_estimate(explicit_col_count, explicit_row_count, child_styles_iter);
370 let mut items = Vec::new();
371 let mut cell_occupancy_matrix =
372 CellOccupancyMatrix::with_track_counts(estimated_sizes.0, estimated_sizes.1);
373
374 // Run placement algorithm
375 place_grid_items(
376 &mut cell_occupancy_matrix,
377 &mut items,
378 children_iter,
379 flow,
380 AlignSelf::Start,
381 AlignSelf::Start,
382 );
383
384 // Assert that each item has been placed in the right location
385 let mut sorted_children = children.clone();
386 sorted_children.sort_by_key(|child| child.0);
387 for (idx, ((id, _style, expected_placement), item)) in sorted_children.iter().zip(items.iter()).enumerate()
388 {
389 assert_eq!(item.node, NodeId::from(*id));
390 let actual_placement = (item.column.start, item.column.end, item.row.start, item.row.end);
391 assert_eq!(actual_placement, (*expected_placement).into_oz(), "Item {idx} (0-indexed)");
392 }
393
394 // Assert that the correct number of implicit rows have been generated
395 let actual_row_counts = *cell_occupancy_matrix.track_counts(crate::compute::grid::AbsoluteAxis::Vertical);
396 assert_eq!(actual_row_counts, expected_row_counts, "row track counts");
397 let actual_col_counts = *cell_occupancy_matrix.track_counts(crate::compute::grid::AbsoluteAxis::Horizontal);
398 assert_eq!(actual_col_counts, expected_col_counts, "column track counts");
399 }
400
401 #[test]
402 fn test_only_fixed_placement() {
403 let flow = GridAutoFlow::Row;
404 let explicit_col_count = 2;
405 let explicit_row_count = 2;
406 let children = {
407 vec![
408 // node, style (grid coords), expected_placement (oz coords)
409 (1, (line(1), auto(), line(1), auto()).into_grid_child(), (0, 1, 0, 1)),
410 (2, (line(-4), auto(), line(-3), auto()).into_grid_child(), (-1, 0, 0, 1)),
411 (3, (line(-3), auto(), line(-4), auto()).into_grid_child(), (0, 1, -1, 0)),
412 (4, (line(3), span(2), line(5), auto()).into_grid_child(), (2, 4, 4, 5)),
413 ]
414 };
415 let expected_cols = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 2 };
416 let expected_rows = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 3 };
417 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
418 }
419
420 #[test]
421 fn test_placement_spanning_origin() {
422 let flow = GridAutoFlow::Row;
423 let explicit_col_count = 2;
424 let explicit_row_count = 2;
425 let children = {
426 vec![
427 // node, style (grid coords), expected_placement (oz coords)
428 (1, (line(-1), line(-1), line(-1), line(-1)).into_grid_child(), (2, 3, 2, 3)),
429 (2, (line(-1), span(2), line(-1), span(2)).into_grid_child(), (2, 4, 2, 4)),
430 (3, (line(-4), line(-4), line(-4), line(-4)).into_grid_child(), (-1, 0, -1, 0)),
431 (4, (line(-4), span(2), line(-4), span(2)).into_grid_child(), (-1, 1, -1, 1)),
432 ]
433 };
434 let expected_cols = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 2 };
435 let expected_rows = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 2 };
436 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
437 }
438
439 #[test]
440 fn test_only_auto_placement_row_flow() {
441 let flow = GridAutoFlow::Row;
442 let explicit_col_count = 2;
443 let explicit_row_count = 2;
444 let children = {
445 let auto_child = (auto(), auto(), auto(), auto()).into_grid_child();
446 vec![
447 // output order, node, style (grid coords), expected_placement (oz coords)
448 (1, auto_child.clone(), (0, 1, 0, 1)),
449 (2, auto_child.clone(), (1, 2, 0, 1)),
450 (3, auto_child.clone(), (0, 1, 1, 2)),
451 (4, auto_child.clone(), (1, 2, 1, 2)),
452 (5, auto_child.clone(), (0, 1, 2, 3)),
453 (6, auto_child.clone(), (1, 2, 2, 3)),
454 (7, auto_child.clone(), (0, 1, 3, 4)),
455 (8, auto_child.clone(), (1, 2, 3, 4)),
456 ]
457 };
458 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
459 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 2 };
460 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
461 }
462
463 #[test]
464 fn test_only_auto_placement_column_flow() {
465 let flow = GridAutoFlow::Column;
466 let explicit_col_count = 2;
467 let explicit_row_count = 2;
468 let children = {
469 let auto_child = (auto(), auto(), auto(), auto()).into_grid_child();
470 vec![
471 // output order, node, style (grid coords), expected_placement (oz coords)
472 (1, auto_child.clone(), (0, 1, 0, 1)),
473 (2, auto_child.clone(), (0, 1, 1, 2)),
474 (3, auto_child.clone(), (1, 2, 0, 1)),
475 (4, auto_child.clone(), (1, 2, 1, 2)),
476 (5, auto_child.clone(), (2, 3, 0, 1)),
477 (6, auto_child.clone(), (2, 3, 1, 2)),
478 (7, auto_child.clone(), (3, 4, 0, 1)),
479 (8, auto_child.clone(), (3, 4, 1, 2)),
480 ]
481 };
482 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 2 };
483 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
484 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
485 }
486
487 #[test]
488 fn test_oversized_item() {
489 let flow = GridAutoFlow::Row;
490 let explicit_col_count = 2;
491 let explicit_row_count = 2;
492 let children = {
493 vec![
494 // output order, node, style (grid coords), expected_placement (oz coords)
495 (1, (span(5), auto(), auto(), auto()).into_grid_child(), (0, 5, 0, 1)),
496 ]
497 };
498 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 3 };
499 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
500 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
501 }
502
503 #[test]
504 fn test_fixed_in_secondary_axis() {
505 let flow = GridAutoFlow::Row;
506 let explicit_col_count = 2;
507 let explicit_row_count = 2;
508 let children = {
509 vec![
510 // output order, node, style (grid coords), expected_placement (oz coords)
511 (1, (span(2), auto(), line(1), auto()).into_grid_child(), (0, 2, 0, 1)),
512 (2, (auto(), auto(), line(2), auto()).into_grid_child(), (0, 1, 1, 2)),
513 (3, (auto(), auto(), line(1), auto()).into_grid_child(), (2, 3, 0, 1)),
514 (4, (auto(), auto(), line(4), auto()).into_grid_child(), (0, 1, 3, 4)),
515 ]
516 };
517 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 1 };
518 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 2 };
519 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
520 }
521
522 #[test]
523 fn test_definite_in_secondary_axis_with_fully_definite_negative() {
524 let flow = GridAutoFlow::Row;
525 let explicit_col_count = 2;
526 let explicit_row_count = 2;
527 let children = {
528 vec![
529 // output order, node, style (grid coords), expected_placement (oz coords)
530 (2, (auto(), auto(), line(2), auto()).into_grid_child(), (0, 1, 1, 2)),
531 (1, (line(-4), auto(), line(2), auto()).into_grid_child(), (-1, 0, 1, 2)),
532 (3, (auto(), auto(), line(1), auto()).into_grid_child(), (-1, 0, 0, 1)),
533 ]
534 };
535 let expected_cols = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 0 };
536 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
537 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
538 }
539
540 #[test]
541 fn test_dense_packing_algorithm() {
542 let flow = GridAutoFlow::RowDense;
543 let explicit_col_count = 4;
544 let explicit_row_count = 4;
545 let children = {
546 vec![
547 // output order, node, style (grid coords), expected_placement (oz coords)
548 (1, (line(2), auto(), line(1), auto()).into_grid_child(), (1, 2, 0, 1)), // Definitely positioned in column 2
549 (2, (span(2), auto(), auto(), auto()).into_grid_child(), (2, 4, 0, 1)), // Spans 2 columns, so positioned after item 1
550 (3, (auto(), auto(), auto(), auto()).into_grid_child(), (0, 1, 0, 1)), // Spans 1 column, so should be positioned before item 1
551 ]
552 };
553 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
554 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
555 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
556 }
557
558 #[test]
559 fn test_sparse_packing_algorithm() {
560 let flow = GridAutoFlow::Row;
561 let explicit_col_count = 4;
562 let explicit_row_count = 4;
563 let children = {
564 vec![
565 // output order, node, style (grid coords), expected_placement (oz coords)
566 (1, (auto(), span(3), auto(), auto()).into_grid_child(), (0, 3, 0, 1)), // Width 3
567 (2, (auto(), span(3), auto(), auto()).into_grid_child(), (0, 3, 1, 2)), // Width 3 (wraps to next row)
568 (3, (auto(), span(1), auto(), auto()).into_grid_child(), (3, 4, 1, 2)), // Width 1 (uses second row as we're already on it)
569 ]
570 };
571 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
572 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
573 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
574 }
575
576 #[test]
577 fn test_auto_placement_in_negative_tracks() {
578 let flow = GridAutoFlow::RowDense;
579 let explicit_col_count = 2;
580 let explicit_row_count = 2;
581 let children = {
582 vec![
583 // output order, node, style (grid coords), expected_placement (oz coords)
584 (1, (line(-5), auto(), line(1), auto()).into_grid_child(), (-2, -1, 0, 1)), // Row 1. Definitely positioned in column -2
585 (2, (auto(), auto(), line(2), auto()).into_grid_child(), (-2, -1, 1, 2)), // Row 2. Auto positioned in column -2
586 (3, (auto(), auto(), auto(), auto()).into_grid_child(), (-1, 0, 0, 1)), // Row 1. Auto positioned in column -1
587 ]
588 };
589 let expected_cols = TrackCounts { negative_implicit: 2, explicit: 2, positive_implicit: 0 };
590 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
591 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
592 }
593 }
594}