1use super::types::{CellOccupancyMatrix, CellOccupancyState, GridItem};
4use super::{NamedLineResolver, OriginZeroLine, MAX_OZ_LINE, MIN_OZ_LINE};
5use crate::geometry::Line;
6use crate::geometry::{AbsoluteAxis, InBothAbsAxis};
7use crate::style::{AlignItems, GridAutoFlow, OriginZeroGridPlacement};
8use crate::tree::NodeId;
9use crate::util::sys::Vec;
10use crate::{CoreStyle, Direction, GridItemStyle};
11
12#[inline]
13fn axis_is_reversed(direction: Direction, axis: AbsoluteAxis) -> bool {
15 direction.is_rtl() && axis == AbsoluteAxis::Horizontal
16}
17
18#[inline]
19fn advance_position(position: OriginZeroLine, axis_is_reversed: bool) -> OriginZeroLine {
21 if axis_is_reversed {
22 OriginZeroLine(position.0.saturating_sub(1))
23 } else {
24 OriginZeroLine(position.0.saturating_add(1))
25 }
26}
27
28#[inline]
29fn search_start_line(
31 grid_start_line: OriginZeroLine,
32 grid_end_line: OriginZeroLine,
33 axis_is_reversed: bool,
34) -> OriginZeroLine {
35 if axis_is_reversed {
36 grid_end_line - 1
37 } else {
38 grid_start_line
39 }
40}
41
42#[inline]
43fn resolve_indefinite_grid_span(position: OriginZeroLine, span: u16, axis_is_reversed: bool) -> Line<OriginZeroLine> {
45 let position = position.0 as i32;
46 let span = span as i32;
47 let line = |value: i32| OriginZeroLine(value.clamp(i16::MIN as i32, i16::MAX as i32) as i16);
48 if axis_is_reversed {
49 Line { start: line(position - span + 1), end: line(position + 1) }
50 } else {
51 Line { start: line(position), end: line(position + span) }
52 }
53}
54
55#[inline]
56fn mirror_horizontal_span(span: Line<OriginZeroLine>, explicit_col_count: u16) -> Line<OriginZeroLine> {
58 let explicit_col_end_line = explicit_col_count as i16;
59 Line {
60 start: OriginZeroLine(explicit_col_end_line - span.end.0),
61 end: OriginZeroLine(explicit_col_end_line - span.start.0),
62 }
63}
64
65#[inline]
66fn maybe_mirror_span(
68 span: Line<OriginZeroLine>,
69 axis: AbsoluteAxis,
70 direction: Direction,
71 explicit_col_count: u16,
72) -> Line<OriginZeroLine> {
73 if axis == AbsoluteAxis::Horizontal && direction.is_rtl() {
74 mirror_horizontal_span(clamp_span_to_limited_grid(span, MIN_OZ_LINE, MAX_OZ_LINE), explicit_col_count)
77 } else {
78 span
79 }
80}
81
82#[allow(clippy::too_many_arguments)]
87pub(super) fn place_grid_items<'a, S, ChildIter>(
88 cell_occupancy_matrix: &mut CellOccupancyMatrix,
89 items: &mut Vec<GridItem>,
90 children_iter: impl Fn() -> ChildIter,
91 direction: Direction,
92 grid_auto_flow: GridAutoFlow,
93 align_items: AlignItems,
94 justify_items: AlignItems,
95 named_line_resolver: &NamedLineResolver<<S as CoreStyle>::CustomIdent>,
96) where
97 S: GridItemStyle + 'a,
98 ChildIter: Iterator<Item = (usize, NodeId, S)>,
99{
100 let primary_axis = grid_auto_flow.primary_axis();
101 let secondary_axis = primary_axis.other_axis();
102 let explicit_col_count = cell_occupancy_matrix.track_counts(AbsoluteAxis::Horizontal).explicit;
103
104 let map_child_style_to_origin_zero_placement = {
105 let explicit_row_count = cell_occupancy_matrix.track_counts(AbsoluteAxis::Vertical).explicit;
106 move |(index, node, style): (usize, NodeId, S)| -> (_, _, _, S) {
107 let origin_zero_placement = InBothAbsAxis {
108 horizontal: named_line_resolver
109 .resolve_column_names(&style.grid_column())
110 .map(|placement| placement.into_origin_zero_placement(explicit_col_count)),
111 vertical: named_line_resolver
112 .resolve_row_names(&style.grid_row())
113 .map(|placement| placement.into_origin_zero_placement(explicit_row_count)),
114 };
115 (index, node, origin_zero_placement, style)
116 }
117 };
118
119 let mut idx = 0;
121 children_iter()
122 .map(map_child_style_to_origin_zero_placement)
123 .filter(|(_, _, placement, _)| placement.horizontal.is_definite() && placement.vertical.is_definite())
124 .for_each(|(index, child_node, child_placement, style)| {
125 idx += 1;
126 #[cfg(test)]
127 println!("Definite Item {idx}\n==============");
128
129 let (row_span, col_span) =
130 place_definite_grid_item(child_placement, primary_axis, direction, explicit_col_count);
131 record_grid_placement(
132 cell_occupancy_matrix,
133 items,
134 child_node,
135 index,
136 style,
137 align_items,
138 justify_items,
139 primary_axis,
140 direction,
141 explicit_col_count,
142 row_span,
143 col_span,
144 CellOccupancyState::DefinitelyPlaced,
145 );
146 });
147
148 let mut idx = 0;
150 children_iter()
151 .map(map_child_style_to_origin_zero_placement)
152 .filter(|(_, _, placement, _)| {
153 placement.get(secondary_axis).is_definite() && !placement.get(primary_axis).is_definite()
154 })
155 .for_each(|(index, child_node, child_placement, style)| {
156 idx += 1;
157 #[cfg(test)]
158 println!("Definite Secondary Item {idx}\n==============");
159
160 let (primary_span, secondary_span) = place_definite_secondary_axis_item(
161 &*cell_occupancy_matrix,
162 child_placement,
163 grid_auto_flow,
164 direction,
165 explicit_col_count,
166 );
167
168 record_grid_placement(
169 cell_occupancy_matrix,
170 items,
171 child_node,
172 index,
173 style,
174 align_items,
175 justify_items,
176 primary_axis,
177 direction,
178 explicit_col_count,
179 primary_span,
180 secondary_span,
181 CellOccupancyState::AutoPlaced,
182 );
183 });
184
185 let primary_axis = grid_auto_flow.primary_axis();
205 let secondary_axis = primary_axis.other_axis();
206 let primary_axis_grid_start_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_start_line();
207 let primary_axis_grid_end_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_end_line();
208 let secondary_axis_grid_start_line = cell_occupancy_matrix.track_counts(secondary_axis).implicit_start_line();
209 let secondary_axis_grid_end_line = cell_occupancy_matrix.track_counts(secondary_axis).implicit_end_line();
210 let primary_axis_is_reversed = axis_is_reversed(direction, primary_axis);
211 let grid_start_position = (
212 search_start_line(primary_axis_grid_start_line, primary_axis_grid_end_line, primary_axis_is_reversed),
213 search_start_line(
214 secondary_axis_grid_start_line,
215 secondary_axis_grid_end_line,
216 axis_is_reversed(direction, secondary_axis),
217 ),
218 );
219 let mut grid_position = grid_start_position;
220 let mut idx = 0;
221 children_iter()
222 .map(map_child_style_to_origin_zero_placement)
223 .filter(|(_, _, placement, _)| !placement.get(secondary_axis).is_definite())
224 .for_each(|(index, child_node, child_placement, style)| {
225 idx += 1;
226 #[cfg(test)]
227 println!("\nAuto Item {idx}\n==============");
228
229 let (primary_span, secondary_span) = place_indefinitely_positioned_item(
231 &*cell_occupancy_matrix,
232 child_placement,
233 grid_auto_flow,
234 grid_position,
235 direction,
236 explicit_col_count,
237 );
238
239 record_grid_placement(
241 cell_occupancy_matrix,
242 items,
243 child_node,
244 index,
245 style,
246 align_items,
247 justify_items,
248 primary_axis,
249 direction,
250 explicit_col_count,
251 primary_span,
252 secondary_span,
253 CellOccupancyState::AutoPlaced,
254 );
255
256 grid_position = match (grid_auto_flow.is_dense(), primary_axis_is_reversed) {
259 (true, _) => grid_start_position,
260 (false, false) => (primary_span.end, secondary_span.start),
261 (false, true) => (primary_span.start, secondary_span.start),
262 };
263 });
264}
265
266fn place_definite_grid_item(
269 placement: InBothAbsAxis<Line<OriginZeroGridPlacement>>,
270 primary_axis: AbsoluteAxis,
271 direction: Direction,
272 explicit_col_count: u16,
273) -> (Line<OriginZeroLine>, Line<OriginZeroLine>) {
274 let primary_span = maybe_mirror_span(
276 placement.get(primary_axis).resolve_definite_grid_lines(),
277 primary_axis,
278 direction,
279 explicit_col_count,
280 );
281 let secondary_span = maybe_mirror_span(
282 placement.get(primary_axis.other_axis()).resolve_definite_grid_lines(),
283 primary_axis.other_axis(),
284 direction,
285 explicit_col_count,
286 );
287
288 (primary_span, secondary_span)
289}
290
291fn place_definite_secondary_axis_item(
294 cell_occupancy_matrix: &CellOccupancyMatrix,
295 placement: InBothAbsAxis<Line<OriginZeroGridPlacement>>,
296 auto_flow: GridAutoFlow,
297 direction: Direction,
298 explicit_col_count: u16,
299) -> (Line<OriginZeroLine>, Line<OriginZeroLine>) {
300 let primary_axis = auto_flow.primary_axis();
301 let secondary_axis = primary_axis.other_axis();
302 let primary_axis_is_reversed = axis_is_reversed(direction, primary_axis);
303 let primary_axis_grid_start_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_start_line();
304 let primary_axis_grid_end_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_end_line();
305
306 let secondary_axis_placement = maybe_mirror_span(
307 placement.get(secondary_axis).resolve_definite_grid_lines(),
308 secondary_axis,
309 direction,
310 explicit_col_count,
311 );
312 let starting_position = match auto_flow.is_dense() {
313 true => search_start_line(primary_axis_grid_start_line, primary_axis_grid_end_line, primary_axis_is_reversed),
314 false => {
315 let lookup_result = if primary_axis_is_reversed {
316 cell_occupancy_matrix.first_of_type(
317 primary_axis,
318 secondary_axis_placement.start,
319 CellOccupancyState::AutoPlaced,
320 )
321 } else {
322 cell_occupancy_matrix.last_of_type(
323 primary_axis,
324 secondary_axis_placement.start,
325 CellOccupancyState::AutoPlaced,
326 )
327 };
328 lookup_result.unwrap_or(search_start_line(
329 primary_axis_grid_start_line,
330 primary_axis_grid_end_line,
331 primary_axis_is_reversed,
332 ))
333 }
334 };
335 let primary_axis_span = placement.get(primary_axis).indefinite_span();
336
337 let mut position: OriginZeroLine = starting_position;
338 loop {
339 let primary_axis_placement =
340 resolve_indefinite_grid_span(position, primary_axis_span, primary_axis_is_reversed);
341
342 let collision = cell_occupancy_matrix.line_area_collision_jump(
343 primary_axis,
344 primary_axis_placement,
345 secondary_axis_placement,
346 primary_axis_is_reversed,
347 );
348
349 match collision {
350 None => return (primary_axis_placement, secondary_axis_placement),
351 Some(next_position) => position = next_position,
352 }
353 }
354}
355
356fn place_indefinitely_positioned_item(
359 cell_occupancy_matrix: &CellOccupancyMatrix,
360 placement: InBothAbsAxis<Line<OriginZeroGridPlacement>>,
361 auto_flow: GridAutoFlow,
362 grid_position: (OriginZeroLine, OriginZeroLine),
363 direction: Direction,
364 explicit_col_count: u16,
365) -> (Line<OriginZeroLine>, Line<OriginZeroLine>) {
366 let primary_axis = auto_flow.primary_axis();
367 let secondary_axis = primary_axis.other_axis();
368 let primary_axis_is_reversed = axis_is_reversed(direction, primary_axis);
369 let secondary_axis_is_reversed = axis_is_reversed(direction, secondary_axis);
370
371 let primary_placement_style = placement.get(primary_axis);
372 let secondary_placement_style = placement.get(secondary_axis);
373
374 let secondary_span = secondary_placement_style.indefinite_span();
375 let has_definite_primary_axis_position = primary_placement_style.is_definite();
376 let primary_axis_grid_start_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_start_line();
377 let primary_axis_grid_end_line = cell_occupancy_matrix.track_counts(primary_axis).implicit_end_line();
378 let secondary_axis_grid_start_line = cell_occupancy_matrix.track_counts(secondary_axis).implicit_start_line();
379 let secondary_axis_grid_end_line = cell_occupancy_matrix.track_counts(secondary_axis).implicit_end_line();
380 let primary_start_position =
381 search_start_line(primary_axis_grid_start_line, primary_axis_grid_end_line, primary_axis_is_reversed);
382 let secondary_start_position =
383 search_start_line(secondary_axis_grid_start_line, secondary_axis_grid_end_line, secondary_axis_is_reversed);
384
385 let (mut primary_idx, mut secondary_idx) = grid_position;
386
387 if has_definite_primary_axis_position {
388 let primary_span = maybe_mirror_span(
389 primary_placement_style.resolve_definite_grid_lines(),
390 primary_axis,
391 direction,
392 explicit_col_count,
393 );
394
395 secondary_idx = match auto_flow.is_dense() {
397 true => secondary_start_position,
399 false => {
400 let should_advance_secondary = if primary_axis_is_reversed {
401 primary_span.start > primary_idx
402 } else {
403 primary_span.start < primary_idx
404 };
405 if should_advance_secondary {
406 advance_position(secondary_idx, secondary_axis_is_reversed)
407 } else {
408 secondary_idx
409 }
410 }
411 };
412
413 loop {
416 let secondary_span =
417 resolve_indefinite_grid_span(secondary_idx, secondary_span, secondary_axis_is_reversed);
418
419 let collision = cell_occupancy_matrix.line_area_collision_jump(
421 secondary_axis,
422 secondary_span,
423 primary_span,
424 secondary_axis_is_reversed,
425 );
426 if let Some(next_position) = collision {
427 secondary_idx = next_position;
428 continue;
429 }
430
431 return (primary_span, secondary_span);
433 }
434 } else {
435 let primary_span = primary_placement_style.indefinite_span();
436
437 let spans_all_primary_tracks = primary_span as usize >= cell_occupancy_matrix.track_counts(primary_axis).len();
440
441 loop {
445 let primary_span = resolve_indefinite_grid_span(primary_idx, primary_span, primary_axis_is_reversed);
446 let secondary_span =
447 resolve_indefinite_grid_span(secondary_idx, secondary_span, secondary_axis_is_reversed);
448
449 let primary_out_of_bounds = if primary_axis_is_reversed {
452 primary_span.start < primary_axis_grid_start_line
453 } else {
454 primary_span.end > primary_axis_grid_end_line
455 };
456 if primary_out_of_bounds {
457 secondary_idx = advance_position(secondary_idx, secondary_axis_is_reversed);
458 primary_idx = primary_start_position;
459 continue;
460 }
461
462 if spans_all_primary_tracks {
466 match cell_occupancy_matrix.occupied_track_jump(
467 secondary_axis,
468 secondary_span,
469 secondary_axis_is_reversed,
470 ) {
471 Some(next_position) => {
472 secondary_idx = next_position;
473 primary_idx = primary_start_position;
474 continue;
475 }
476 None => return (primary_span, secondary_span),
477 }
478 }
479
480 let collision = cell_occupancy_matrix.line_area_collision_jump(
482 primary_axis,
483 primary_span,
484 secondary_span,
485 primary_axis_is_reversed,
486 );
487 if let Some(next_position) = collision {
488 primary_idx = next_position;
489 continue;
490 }
491
492 return (primary_span, secondary_span);
494 }
495 }
496}
497
498fn clamp_span_to_limited_grid(span: Line<OriginZeroLine>, min_line: i16, max_line: i16) -> Line<OriginZeroLine> {
503 let start = span.start.0.clamp(min_line, max_line - 1);
504 let end = span.end.0.clamp(start + 1, max_line);
505 Line { start: OriginZeroLine(start), end: OriginZeroLine(end) }
506}
507
508fn clamp_span_for_axis(
510 span: Line<OriginZeroLine>,
511 axis: AbsoluteAxis,
512 direction: Direction,
513 explicit_col_count: u16,
514) -> Line<OriginZeroLine> {
515 let (min_line, max_line) = if axis == AbsoluteAxis::Horizontal && direction.is_rtl() {
516 let explicit_end = explicit_col_count as i16;
517 (explicit_end - MAX_OZ_LINE, explicit_end - MIN_OZ_LINE)
518 } else {
519 (MIN_OZ_LINE, MAX_OZ_LINE)
520 };
521 clamp_span_to_limited_grid(span, min_line, max_line)
522}
523
524#[allow(clippy::too_many_arguments)]
527fn record_grid_placement<S: GridItemStyle>(
528 cell_occupancy_matrix: &mut CellOccupancyMatrix,
529 items: &mut Vec<GridItem>,
530 node: NodeId,
531 index: usize,
532 style: S,
533 parent_align_items: AlignItems,
534 parent_justify_items: AlignItems,
535 primary_axis: AbsoluteAxis,
536 direction: Direction,
537 explicit_col_count: u16,
538 primary_span: Line<OriginZeroLine>,
539 secondary_span: Line<OriginZeroLine>,
540 placement_type: CellOccupancyState,
541) {
542 #[cfg(test)]
543 println!("BEFORE placement:");
544 #[cfg(test)]
545 println!("{cell_occupancy_matrix:?}");
546
547 let primary_span = clamp_span_for_axis(primary_span, primary_axis, direction, explicit_col_count);
550 let secondary_span = clamp_span_for_axis(secondary_span, primary_axis.other_axis(), direction, explicit_col_count);
551
552 cell_occupancy_matrix.mark_area_as(primary_axis, primary_span, secondary_span, placement_type);
554
555 let (col_span, row_span) = match primary_axis {
557 AbsoluteAxis::Horizontal => (primary_span, secondary_span),
558 AbsoluteAxis::Vertical => (secondary_span, primary_span),
559 };
560 items.push(GridItem::new_with_placement_style_and_order(
561 node,
562 col_span,
563 row_span,
564 style,
565 parent_align_items,
566 parent_justify_items,
567 index as u16,
568 ));
569
570 #[cfg(test)]
571 println!("AFTER placement:");
572 #[cfg(test)]
573 println!("{cell_occupancy_matrix:?}");
574 #[cfg(test)]
575 println!("\n");
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581
582 mod test_placement_algorithm {
583 use crate::compute::grid::implicit_grid::compute_grid_size_estimate;
584 use crate::compute::grid::types::TrackCounts;
585 use crate::compute::grid::util::*;
586 use crate::compute::grid::CellOccupancyMatrix;
587 use crate::compute::grid::NamedLineResolver;
588 use crate::compute::grid::OriginZeroLine;
589 use crate::prelude::*;
590 use crate::style::GridAutoFlow;
591 use crate::Direction;
592
593 use super::super::place_grid_items;
594
595 type ExpectedPlacement = (i16, i16, i16, i16);
596
597 fn placement_test_runner(
598 explicit_col_count: u16,
599 explicit_row_count: u16,
600 children: Vec<(usize, Style, ExpectedPlacement)>,
601 expected_col_counts: TrackCounts,
602 expected_row_counts: TrackCounts,
603 flow: GridAutoFlow,
604 ) {
605 let children_iter = || children.iter().map(|(index, style, _)| (*index, NodeId::from(*index), style));
607 let child_styles_iter = children.iter().map(|(_, style, _)| style);
608 let estimated_sizes =
609 compute_grid_size_estimate(explicit_col_count, explicit_row_count, Direction::Ltr, child_styles_iter);
610 let mut items = Vec::new();
611 let mut cell_occupancy_matrix =
612 CellOccupancyMatrix::with_track_counts(estimated_sizes.0, estimated_sizes.1);
613 let mut name_resolver = NamedLineResolver::new(&Style::DEFAULT, 0, 0);
614 name_resolver.set_explicit_column_count(explicit_col_count);
615 name_resolver.set_explicit_row_count(explicit_row_count);
616
617 place_grid_items(
619 &mut cell_occupancy_matrix,
620 &mut items,
621 children_iter,
622 Direction::Ltr,
623 flow,
624 AlignSelf::START,
625 AlignSelf::START,
626 &name_resolver,
628 );
629
630 let mut sorted_children = children.clone();
632 sorted_children.sort_by_key(|child| child.0);
633 for (idx, ((id, _style, expected_placement), item)) in sorted_children.iter().zip(items.iter()).enumerate()
634 {
635 assert_eq!(item.node, NodeId::from(*id));
636 let actual_placement = (item.column.start, item.column.end, item.row.start, item.row.end);
637 assert_eq!(actual_placement, (*expected_placement).into_oz(), "Item {idx} (0-indexed)");
638 }
639
640 let actual_row_counts = *cell_occupancy_matrix.track_counts(crate::compute::grid::AbsoluteAxis::Vertical);
642 assert_eq!(actual_row_counts, expected_row_counts, "row track counts");
643 let actual_col_counts = *cell_occupancy_matrix.track_counts(crate::compute::grid::AbsoluteAxis::Horizontal);
644 assert_eq!(actual_col_counts, expected_col_counts, "column track counts");
645 }
646
647 #[test]
648 fn test_only_fixed_placement() {
649 let flow = GridAutoFlow::Row;
650 let explicit_col_count = 2;
651 let explicit_row_count = 2;
652 let children = {
653 vec![
654 (1, (line(1), auto(), line(1), auto()).into_grid_child(), (0, 1, 0, 1)),
656 (2, (line(-4), auto(), line(-3), auto()).into_grid_child(), (-1, 0, 0, 1)),
657 (3, (line(-3), auto(), line(-4), auto()).into_grid_child(), (0, 1, -1, 0)),
658 (4, (line(3), span(2), line(5), auto()).into_grid_child(), (2, 4, 4, 5)),
659 ]
660 };
661 let expected_cols = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 2 };
662 let expected_rows = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 3 };
663 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
664 }
665
666 #[test]
667 fn test_placement_spanning_origin() {
668 let flow = GridAutoFlow::Row;
669 let explicit_col_count = 2;
670 let explicit_row_count = 2;
671 let children = {
672 vec![
673 (1, (line(-1), line(-1), line(-1), line(-1)).into_grid_child(), (2, 3, 2, 3)),
675 (2, (line(-1), span(2), line(-1), span(2)).into_grid_child(), (2, 4, 2, 4)),
676 (3, (line(-4), line(-4), line(-4), line(-4)).into_grid_child(), (-1, 0, -1, 0)),
677 (4, (line(-4), span(2), line(-4), span(2)).into_grid_child(), (-1, 1, -1, 1)),
678 ]
679 };
680 let expected_cols = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 2 };
681 let expected_rows = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 2 };
682 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
683 }
684
685 #[test]
686 fn test_only_auto_placement_row_flow() {
687 let flow = GridAutoFlow::Row;
688 let explicit_col_count = 2;
689 let explicit_row_count = 2;
690 let children = {
691 let auto_child = (auto(), auto(), auto(), auto()).into_grid_child();
692 vec![
693 (1, auto_child.clone(), (0, 1, 0, 1)),
695 (2, auto_child.clone(), (1, 2, 0, 1)),
696 (3, auto_child.clone(), (0, 1, 1, 2)),
697 (4, auto_child.clone(), (1, 2, 1, 2)),
698 (5, auto_child.clone(), (0, 1, 2, 3)),
699 (6, auto_child.clone(), (1, 2, 2, 3)),
700 (7, auto_child.clone(), (0, 1, 3, 4)),
701 (8, auto_child.clone(), (1, 2, 3, 4)),
702 ]
703 };
704 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
705 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 2 };
706 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
707 }
708
709 #[test]
710 fn test_only_auto_placement_column_flow() {
711 let flow = GridAutoFlow::Column;
712 let explicit_col_count = 2;
713 let explicit_row_count = 2;
714 let children = {
715 let auto_child = (auto(), auto(), auto(), auto()).into_grid_child();
716 vec![
717 (1, auto_child.clone(), (0, 1, 0, 1)),
719 (2, auto_child.clone(), (0, 1, 1, 2)),
720 (3, auto_child.clone(), (1, 2, 0, 1)),
721 (4, auto_child.clone(), (1, 2, 1, 2)),
722 (5, auto_child.clone(), (2, 3, 0, 1)),
723 (6, auto_child.clone(), (2, 3, 1, 2)),
724 (7, auto_child.clone(), (3, 4, 0, 1)),
725 (8, auto_child.clone(), (3, 4, 1, 2)),
726 ]
727 };
728 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 2 };
729 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
730 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
731 }
732
733 #[test]
734 fn test_oversized_item() {
735 let flow = GridAutoFlow::Row;
736 let explicit_col_count = 2;
737 let explicit_row_count = 2;
738 let children = {
739 vec![
740 (1, (span(5), auto(), auto(), auto()).into_grid_child(), (0, 5, 0, 1)),
742 ]
743 };
744 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 3 };
745 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
746 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
747 }
748
749 #[test]
750 fn test_fixed_in_secondary_axis() {
751 let flow = GridAutoFlow::Row;
752 let explicit_col_count = 2;
753 let explicit_row_count = 2;
754 let children = {
755 vec![
756 (1, (span(2), auto(), line(1), auto()).into_grid_child(), (0, 2, 0, 1)),
758 (2, (auto(), auto(), line(2), auto()).into_grid_child(), (0, 1, 1, 2)),
759 (3, (auto(), auto(), line(1), auto()).into_grid_child(), (2, 3, 0, 1)),
760 (4, (auto(), auto(), line(4), auto()).into_grid_child(), (0, 1, 3, 4)),
761 ]
762 };
763 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 1 };
764 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 2 };
765 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
766 }
767
768 #[test]
769 fn test_definite_in_secondary_axis_with_fully_definite_negative() {
770 let flow = GridAutoFlow::Row;
771 let explicit_col_count = 2;
772 let explicit_row_count = 2;
773 let children = {
774 vec![
775 (2, (auto(), auto(), line(2), auto()).into_grid_child(), (0, 1, 1, 2)),
777 (1, (line(-4), auto(), line(2), auto()).into_grid_child(), (-1, 0, 1, 2)),
778 (3, (auto(), auto(), line(1), auto()).into_grid_child(), (-1, 0, 0, 1)),
779 ]
780 };
781 let expected_cols = TrackCounts { negative_implicit: 1, explicit: 2, positive_implicit: 0 };
782 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
783 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
784 }
785
786 #[test]
787 fn test_dense_packing_algorithm() {
788 let flow = GridAutoFlow::RowDense;
789 let explicit_col_count = 4;
790 let explicit_row_count = 4;
791 let children = {
792 vec![
793 (1, (line(2), auto(), line(1), auto()).into_grid_child(), (1, 2, 0, 1)), (2, (span(2), auto(), auto(), auto()).into_grid_child(), (2, 4, 0, 1)), (3, (auto(), auto(), auto(), auto()).into_grid_child(), (0, 1, 0, 1)), ]
798 };
799 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
800 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
801 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
802 }
803
804 #[test]
805 fn test_sparse_packing_algorithm() {
806 let flow = GridAutoFlow::Row;
807 let explicit_col_count = 4;
808 let explicit_row_count = 4;
809 let children = {
810 vec![
811 (1, (auto(), span(3), auto(), auto()).into_grid_child(), (0, 3, 0, 1)), (2, (auto(), span(3), auto(), auto()).into_grid_child(), (0, 3, 1, 2)), (3, (auto(), span(1), auto(), auto()).into_grid_child(), (3, 4, 1, 2)), ]
816 };
817 let expected_cols = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
818 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 4, positive_implicit: 0 };
819 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
820 }
821
822 #[test]
823 fn test_auto_placement_in_negative_tracks() {
824 let flow = GridAutoFlow::RowDense;
825 let explicit_col_count = 2;
826 let explicit_row_count = 2;
827 let children = {
828 vec![
829 (1, (line(-5), auto(), line(1), auto()).into_grid_child(), (-2, -1, 0, 1)), (2, (auto(), auto(), line(2), auto()).into_grid_child(), (-2, -1, 1, 2)), (3, (auto(), auto(), auto(), auto()).into_grid_child(), (-1, 0, 0, 1)), ]
834 };
835 let expected_cols = TrackCounts { negative_implicit: 2, explicit: 2, positive_implicit: 0 };
836 let expected_rows = TrackCounts { negative_implicit: 0, explicit: 2, positive_implicit: 0 };
837 placement_test_runner(explicit_col_count, explicit_row_count, children, expected_cols, expected_rows, flow);
838 }
839
840 #[test]
841 fn test_rtl_overlarge_placement_uses_mirrored_limits() {
842 let explicit_col_count = 9_000;
843 let explicit_row_count = 0;
844 let style = (line(-19_005), auto(), auto(), auto()).into_grid_child();
845 let children = [(0, style)];
846 let estimated_sizes = compute_grid_size_estimate(
847 explicit_col_count,
848 explicit_row_count,
849 Direction::Rtl,
850 children.iter().map(|(_, style)| style),
851 );
852 let mut items = Vec::new();
853 let mut cell_occupancy_matrix =
854 CellOccupancyMatrix::with_track_counts(estimated_sizes.0, estimated_sizes.1);
855 let mut name_resolver = NamedLineResolver::new(&Style::DEFAULT, 0, 0);
856 name_resolver.set_explicit_column_count(explicit_col_count);
857 name_resolver.set_explicit_row_count(explicit_row_count);
858 place_grid_items(
859 &mut cell_occupancy_matrix,
860 &mut items,
861 || children.iter().map(|(index, style)| (*index, NodeId::from(*index), style)),
862 Direction::Rtl,
863 GridAutoFlow::Row,
864 AlignSelf::START,
865 AlignSelf::START,
866 &name_resolver,
867 );
868 assert_eq!(items[0].column, Line { start: OriginZeroLine(18_999), end: OriginZeroLine(19_000) });
869 }
870 }
871
872 #[test]
873 fn auto_placement_cursor_saturates_at_integer_bounds() {
874 assert_eq!(advance_position(OriginZeroLine(i16::MAX), false), OriginZeroLine(i16::MAX));
875 assert_eq!(advance_position(OriginZeroLine(i16::MIN), true), OriginZeroLine(i16::MIN));
876 }
877
878 #[test]
879 fn indefinite_spans_saturate_at_integer_bounds() {
880 assert_eq!(
881 resolve_indefinite_grid_span(OriginZeroLine(i16::MAX), 1, false),
882 Line { start: OriginZeroLine(i16::MAX), end: OriginZeroLine(i16::MAX) }
883 );
884 assert_eq!(
885 resolve_indefinite_grid_span(OriginZeroLine(i16::MIN), 1, true),
886 Line { start: OriginZeroLine(i16::MIN), end: OriginZeroLine(i16::MIN + 1) }
887 );
888 }
889
890 #[test]
891 fn rtl_spans_are_clamped_in_mirrored_coordinates() {
892 let logical_span = Line { start: OriginZeroLine(-10_000), end: OriginZeroLine(-9_999) };
893 let mirrored_span = maybe_mirror_span(logical_span, AbsoluteAxis::Horizontal, Direction::Rtl, 9_000);
894 assert_eq!(mirrored_span, Line { start: OriginZeroLine(18_999), end: OriginZeroLine(19_000) });
895 assert_eq!(clamp_span_for_axis(mirrored_span, AbsoluteAxis::Horizontal, Direction::Rtl, 9_000), mirrored_span);
896 }
897}