⎇ 10b1e6f859c7dc232bb8239b42d55d35afd6be18
src/compute/grid/explicit_grid.rs
History
Who touched what
raw
1//! Helper functions for initialising GridTrack's from styles
2//! This mainly consists of evaluating GridAutoTracks
3use super::types::{GridTrack, TrackCounts};
4use crate::geometry::{AbsoluteAxis, Size};
5use crate::style::{GridTrackRepetition, LengthPercentage, NonRepeatedTrackSizingFunction, TrackSizingFunction};
6use crate::style_helpers::TaffyAuto;
7use crate::util::sys::Vec;
8use crate::util::MaybeMath;
9use crate::util::ResolveOrZero;
10use crate::{GridContainerStyle, MaybeResolve};
11
12#[cfg(not(feature = "std"))]
13use num_traits::float::FloatCore;
14
15/// Compute the number of rows and columns in the explicit grid
16pub(crate) fn compute_explicit_grid_size_in_axis(
17 style: &impl GridContainerStyle,
18 template: &[TrackSizingFunction],
19 inner_container_size: Size<Option<f32>>,
20 axis: AbsoluteAxis,
21) -> u16 {
22 // If template contains no tracks, then there are trivially zero explicit tracks
23 if template.is_empty() {
24 return 0;
25 }
26
27 // If there are any repetitions that contains no tracks, then the whole definition should be considered invalid
28 // and we default to no explicit tracks
29 let template_has_repetitions_with_zero_tracks = template.iter().any(|track_def| match track_def {
30 TrackSizingFunction::Single(_) => false,
31 TrackSizingFunction::Repeat(_, tracks) => tracks.is_empty(),
32 });
33 if template_has_repetitions_with_zero_tracks {
34 return 0;
35 }
36
37 // Compute that number of track generated by single track definition and repetitions with a fixed repetition count
38 let non_auto_repeating_track_count = template
39 .iter()
40 .map(|track_def| {
41 use GridTrackRepetition::{AutoFill, AutoFit, Count};
42 match track_def {
43 TrackSizingFunction::Single(_) => 1,
44 TrackSizingFunction::Repeat(Count(count), tracks) => count * tracks.len() as u16,
45 TrackSizingFunction::Repeat(AutoFit | AutoFill, _) => 0,
46 }
47 })
48 .sum::<u16>();
49
50 let auto_repetition_count = template.iter().filter(|track_def| track_def.is_auto_repetition()).count() as u16;
51 let all_track_defs_have_fixed_component = template.iter().all(|track_def| match track_def {
52 TrackSizingFunction::Single(sizing_function) => sizing_function.has_fixed_component(),
53 TrackSizingFunction::Repeat(_, tracks) => {
54 tracks.iter().all(|sizing_function| sizing_function.has_fixed_component())
55 }
56 });
57
58 let template_is_valid =
59 auto_repetition_count == 0 || (auto_repetition_count == 1 && all_track_defs_have_fixed_component);
60
61 // If the template is invalid because it contains multiple auto-repetition definitions or it combines an auto-repetition
62 // definition with non-fixed-size track sizing functions, then disregard it entirely and default to zero explicit tracks
63 if !template_is_valid {
64 return 0;
65 }
66
67 // If there are no repetitions, then the number of explicit tracks is simply equal to the lengths of the track definition
68 // vector (as each item in the Vec represents one track).
69 if auto_repetition_count == 0 {
70 return non_auto_repeating_track_count;
71 }
72
73 let repetition_definition = template
74 .iter()
75 .find_map(|def| {
76 use GridTrackRepetition::{AutoFill, AutoFit, Count};
77 match def {
78 TrackSizingFunction::Single(_) => None,
79 TrackSizingFunction::Repeat(Count(_), _) => None,
80 TrackSizingFunction::Repeat(AutoFit | AutoFill, tracks) => Some(tracks),
81 }
82 })
83 .unwrap();
84 let repetition_track_count = repetition_definition.len() as u16;
85
86 // Otherwise, run logic to resolve the auto-repeated track count:
87 //
88 // If the grid container has a definite size or max size in the relevant axis:
89 // - then the number of repetitions is the largest possible positive integer that does not cause the grid to overflow the content
90 // box of its grid container.
91 // Otherwise, if the grid container has a definite min size in the relevant axis:
92 // - then the number of repetitions is the smallest possible positive integer that fulfills that minimum requirement
93 // Otherwise, the specified track list repeats only once.
94 let style_size_is_definite = style.size().get_abs(axis).maybe_resolve(inner_container_size.get_abs(axis)).is_some();
95 let style_max_size_is_definite =
96 style.max_size().get_abs(axis).maybe_resolve(inner_container_size.get_abs(axis)).is_some();
97 let size_is_maximum = style_size_is_definite | style_max_size_is_definite;
98
99 // Determine the number of repetitions
100 let num_repetitions: u16 = match inner_container_size.get_abs(axis) {
101 None => 1,
102 Some(inner_container_size) => {
103 let parent_size = Some(inner_container_size);
104
105 /// ...treating each track as its max track sizing function if that is definite or as its minimum track sizing function
106 /// otherwise, flooring the max track sizing function by the min track sizing function if both are definite
107 fn track_definite_value(sizing_function: &NonRepeatedTrackSizingFunction, parent_size: Option<f32>) -> f32 {
108 let max_size = sizing_function.max.definite_value(parent_size);
109 let min_size = sizing_function.min.definite_value(parent_size);
110 max_size.map(|max| max.maybe_min(min_size)).or(min_size).unwrap()
111 }
112
113 let non_repeating_track_used_space: f32 = template
114 .iter()
115 .map(|track_def| {
116 use GridTrackRepetition::{AutoFill, AutoFit, Count};
117 match track_def {
118 TrackSizingFunction::Single(sizing_function) => {
119 track_definite_value(sizing_function, parent_size)
120 }
121 TrackSizingFunction::Repeat(Count(count), repeated_tracks) => {
122 let sum = repeated_tracks
123 .iter()
124 .map(|sizing_function| track_definite_value(sizing_function, parent_size))
125 .sum::<f32>();
126 sum * (*count as f32)
127 }
128 TrackSizingFunction::Repeat(AutoFit | AutoFill, _) => 0.0,
129 }
130 })
131 .sum();
132 let gap_size = style.gap().get_abs(axis).resolve_or_zero(Some(inner_container_size));
133
134 // Compute the amount of space that a single repetition of the repeated track list takes
135 let per_repetition_track_used_space: f32 = repetition_definition
136 .iter()
137 .map(|sizing_function| track_definite_value(sizing_function, parent_size))
138 .sum::<f32>();
139
140 // We special case the first repetition here because the number of gaps in the first repetition
141 // depends on the number of non-repeating tracks in the template
142 let first_repetition_and_non_repeating_tracks_used_space = non_repeating_track_used_space
143 + per_repetition_track_used_space
144 + ((non_auto_repeating_track_count + repetition_track_count).saturating_sub(1) as f32 * gap_size);
145
146 // If a single repetition already overflows the container then we return 1 as the repetition count
147 // (the number of repetitions is floored at 1)
148 if first_repetition_and_non_repeating_tracks_used_space > inner_container_size {
149 1u16
150 } else {
151 let per_repetition_gap_used_space = (repetition_definition.len() as f32) * gap_size;
152 let per_repetition_used_space = per_repetition_track_used_space + per_repetition_gap_used_space;
153 let num_repetition_that_fit = (inner_container_size
154 - first_repetition_and_non_repeating_tracks_used_space)
155 / per_repetition_used_space;
156
157 // If the container size is a preferred or maximum size:
158 // Then we return the maximum number of repetitions that fit into the container without overflowing.
159 // If the container size is a minimum size:
160 // - Then we return the minimum number of repetitions required to overflow the size.
161 //
162 // In all cases we add the additional repetition that was already accounted for in the special-case computation above
163 if size_is_maximum {
164 (num_repetition_that_fit.floor() as u16) + 1
165 } else {
166 (num_repetition_that_fit.ceil() as u16) + 1
167 }
168 }
169 }
170 };
171
172 non_auto_repeating_track_count + (repetition_track_count * num_repetitions)
173}
174
175/// Resolve the track sizing functions of explicit tracks, automatically created tracks, and gutters
176/// given a set of track counts and all of the relevant styles
177pub(super) fn initialize_grid_tracks(
178 tracks: &mut Vec<GridTrack>,
179 counts: TrackCounts,
180 track_template: &[TrackSizingFunction],
181 auto_tracks: &[NonRepeatedTrackSizingFunction],
182 gap: LengthPercentage,
183 track_has_items: impl Fn(usize) -> bool,
184) {
185 // Clear vector (in case this is a re-layout), reserve space for all tracks ahead of time to reduce allocations,
186 // and push the initial gutter
187 tracks.clear();
188 tracks.reserve((counts.len() * 2) + 1);
189 tracks.push(GridTrack::gutter(gap));
190
191 // Create negative implicit tracks
192 if counts.negative_implicit > 0 {
193 if auto_tracks.is_empty() {
194 let iter = core::iter::repeat(NonRepeatedTrackSizingFunction::AUTO);
195 create_implicit_tracks(tracks, counts.negative_implicit, iter, gap)
196 } else {
197 let offset = auto_tracks.len() - (counts.negative_implicit as usize % auto_tracks.len());
198 let iter = auto_tracks.iter().copied().cycle().skip(offset);
199 create_implicit_tracks(tracks, counts.negative_implicit, iter, gap)
200 }
201 }
202
203 let mut current_track_index = (counts.negative_implicit) as usize;
204
205 // Create explicit tracks
206 // An explicit check against the count (rather than just relying on track_template being empty) is required here
207 // because a count of zero can result from the track_template being invalid, in which case it should be ignored.
208 if counts.explicit > 0 {
209 track_template.iter().for_each(|track_sizing_function| {
210 use GridTrackRepetition::{AutoFill, AutoFit, Count};
211 match track_sizing_function {
212 TrackSizingFunction::Single(sizing_function) => {
213 tracks.push(GridTrack::new(
214 sizing_function.min_sizing_function(),
215 sizing_function.max_sizing_function(),
216 ));
217 tracks.push(GridTrack::gutter(gap));
218 current_track_index += 1;
219 }
220 TrackSizingFunction::Repeat(Count(count), repeated_tracks) => {
221 let track_iter = repeated_tracks.iter().cycle().take(repeated_tracks.len() * *count as usize);
222 track_iter.for_each(|sizing_function| {
223 tracks.push(GridTrack::new(
224 sizing_function.min_sizing_function(),
225 sizing_function.max_sizing_function(),
226 ));
227 tracks.push(GridTrack::gutter(gap));
228 current_track_index += 1;
229 });
230 }
231 TrackSizingFunction::Repeat(repetition_kind @ (AutoFit | AutoFill), repeated_tracks) => {
232 let auto_repeated_track_count = (counts.explicit - (track_template.len() as u16 - 1)) as usize;
233 let iter = repeated_tracks.iter().copied().cycle();
234 for track_def in iter.take(auto_repeated_track_count) {
235 let mut track =
236 GridTrack::new(track_def.min_sizing_function(), track_def.max_sizing_function());
237 let mut gutter = GridTrack::gutter(gap);
238
239 // Auto-fit tracks that don't contain should be collapsed.
240 if *repetition_kind == AutoFit && !track_has_items(current_track_index) {
241 track.collapse();
242 gutter.collapse();
243 }
244
245 tracks.push(track);
246 tracks.push(gutter);
247
248 current_track_index += 1;
249 }
250 }
251 }
252 });
253 }
254
255 // Create positive implicit tracks
256 if auto_tracks.is_empty() {
257 let iter = core::iter::repeat(NonRepeatedTrackSizingFunction::AUTO);
258 create_implicit_tracks(tracks, counts.positive_implicit, iter, gap)
259 } else {
260 let iter = auto_tracks.iter().copied().cycle();
261 create_implicit_tracks(tracks, counts.positive_implicit, iter, gap)
262 }
263
264 // Mark first and last grid lines as collapsed
265 tracks.first_mut().unwrap().collapse();
266 tracks.last_mut().unwrap().collapse();
267}
268
269/// Utility function for repeating logic of creating implicit tracks
270fn create_implicit_tracks(
271 tracks: &mut Vec<GridTrack>,
272 count: u16,
273 mut auto_tracks_iter: impl Iterator<Item = NonRepeatedTrackSizingFunction>,
274 gap: LengthPercentage,
275) {
276 for _ in 0..count {
277 let track_def = auto_tracks_iter.next().unwrap();
278 tracks.push(GridTrack::new(track_def.min_sizing_function(), track_def.max_sizing_function()));
279 tracks.push(GridTrack::gutter(gap));
280 }
281}
282
283#[cfg(test)]
284mod test {
285 use super::compute_explicit_grid_size_in_axis;
286 use super::initialize_grid_tracks;
287 use crate::compute::grid::types::GridTrackKind;
288 use crate::compute::grid::types::TrackCounts;
289 use crate::compute::grid::util::*;
290 use crate::geometry::AbsoluteAxis;
291 use crate::prelude::*;
292
293 #[test]
294 fn explicit_grid_sizing_no_repeats() {
295 let grid_style = (600.0, 600.0, 2, 4).into_grid();
296 let preferred_size = grid_style.size.map(|s| s.into_option());
297 let width = compute_explicit_grid_size_in_axis(
298 &grid_style,
299 &grid_style.grid_template_columns,
300 preferred_size,
301 AbsoluteAxis::Horizontal,
302 );
303 let height = compute_explicit_grid_size_in_axis(
304 &grid_style,
305 &grid_style.grid_template_rows,
306 preferred_size,
307 AbsoluteAxis::Vertical,
308 );
309 assert_eq!(width, 2);
310 assert_eq!(height, 4);
311 }
312
313 #[test]
314 fn explicit_grid_sizing_auto_fill_exact_fit() {
315 use GridTrackRepetition::AutoFill;
316 let grid_style = Style {
317 display: Display::Grid,
318 size: Size { width: length(120.0), height: length(80.0) },
319 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
320 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
321 ..Default::default()
322 };
323 let preferred_size = grid_style.size.map(|s| s.into_option());
324 let width = compute_explicit_grid_size_in_axis(
325 &grid_style,
326 &grid_style.grid_template_columns,
327 preferred_size,
328 AbsoluteAxis::Horizontal,
329 );
330 let height = compute_explicit_grid_size_in_axis(
331 &grid_style,
332 &grid_style.grid_template_rows,
333 preferred_size,
334 AbsoluteAxis::Vertical,
335 );
336 assert_eq!(width, 3);
337 assert_eq!(height, 4);
338 }
339
340 #[test]
341 fn explicit_grid_sizing_auto_fill_non_exact_fit() {
342 use GridTrackRepetition::AutoFill;
343 let grid_style = Style {
344 display: Display::Grid,
345 size: Size { width: length(140.0), height: length(90.0) },
346 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
347 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
348 ..Default::default()
349 };
350 let preferred_size = grid_style.size.map(|s| s.into_option());
351 let width = compute_explicit_grid_size_in_axis(
352 &grid_style,
353 &grid_style.grid_template_columns,
354 preferred_size,
355 AbsoluteAxis::Horizontal,
356 );
357 let height = compute_explicit_grid_size_in_axis(
358 &grid_style,
359 &grid_style.grid_template_rows,
360 preferred_size,
361 AbsoluteAxis::Vertical,
362 );
363 assert_eq!(width, 3);
364 assert_eq!(height, 4);
365 }
366
367 #[test]
368 fn explicit_grid_sizing_auto_fill_min_size_exact_fit() {
369 use GridTrackRepetition::AutoFill;
370 let grid_style = Style {
371 display: Display::Grid,
372 min_size: Size { width: length(120.0), height: length(80.0) },
373 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
374 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
375 ..Default::default()
376 };
377 let inner_container_size = Size { width: Some(120.0), height: Some(80.0) };
378 let width = compute_explicit_grid_size_in_axis(
379 &grid_style,
380 &grid_style.grid_template_columns,
381 inner_container_size,
382 AbsoluteAxis::Horizontal,
383 );
384 let height = compute_explicit_grid_size_in_axis(
385 &grid_style,
386 &grid_style.grid_template_rows,
387 inner_container_size,
388 AbsoluteAxis::Vertical,
389 );
390 assert_eq!(width, 3);
391 assert_eq!(height, 4);
392 }
393
394 #[test]
395 fn explicit_grid_sizing_auto_fill_min_size_non_exact_fit() {
396 use GridTrackRepetition::AutoFill;
397 let grid_style = Style {
398 display: Display::Grid,
399 min_size: Size { width: length(140.0), height: length(90.0) },
400 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
401 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
402 ..Default::default()
403 };
404 let inner_container_size = Size { width: Some(140.0), height: Some(90.0) };
405 let width = compute_explicit_grid_size_in_axis(
406 &grid_style,
407 &grid_style.grid_template_columns,
408 inner_container_size,
409 AbsoluteAxis::Horizontal,
410 );
411 let height = compute_explicit_grid_size_in_axis(
412 &grid_style,
413 &grid_style.grid_template_rows,
414 inner_container_size,
415 AbsoluteAxis::Vertical,
416 );
417 assert_eq!(width, 4);
418 assert_eq!(height, 5);
419 }
420
421 #[test]
422 fn explicit_grid_sizing_auto_fill_multiple_repeated_tracks() {
423 use GridTrackRepetition::AutoFill;
424 let grid_style = Style {
425 display: Display::Grid,
426 size: Size { width: length(140.0), height: length(100.0) },
427 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0), length(20.0)])],
428 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0), length(10.0)])],
429 ..Default::default()
430 };
431 let preferred_size = grid_style.size.map(|s| s.into_option());
432 let width = compute_explicit_grid_size_in_axis(
433 &grid_style,
434 &grid_style.grid_template_columns,
435 preferred_size,
436 AbsoluteAxis::Horizontal,
437 );
438 let height = compute_explicit_grid_size_in_axis(
439 &grid_style,
440 &grid_style.grid_template_rows,
441 preferred_size,
442 AbsoluteAxis::Vertical,
443 );
444 assert_eq!(width, 4); // 2 repetitions * 2 repeated tracks = 4 tracks in total
445 assert_eq!(height, 6); // 3 repetitions * 2 repeated tracks = 4 tracks in total
446 }
447
448 #[test]
449 fn explicit_grid_sizing_auto_fill_gap() {
450 use GridTrackRepetition::AutoFill;
451 let grid_style = Style {
452 display: Display::Grid,
453 size: Size { width: length(140.0), height: length(100.0) },
454 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
455 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
456 gap: length(20.0),
457 ..Default::default()
458 };
459 let preferred_size = grid_style.size.map(|s| s.into_option());
460 let width = compute_explicit_grid_size_in_axis(
461 &grid_style,
462 &grid_style.grid_template_columns,
463 preferred_size,
464 AbsoluteAxis::Horizontal,
465 );
466 let height = compute_explicit_grid_size_in_axis(
467 &grid_style,
468 &grid_style.grid_template_rows,
469 preferred_size,
470 AbsoluteAxis::Vertical,
471 );
472 assert_eq!(width, 2); // 2 tracks + 1 gap
473 assert_eq!(height, 3); // 3 tracks + 2 gaps
474 }
475
476 #[test]
477 fn explicit_grid_sizing_no_defined_size() {
478 use GridTrackRepetition::AutoFill;
479 let grid_style = Style {
480 display: Display::Grid,
481 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0), percent(0.5), length(20.0)])],
482 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
483 gap: length(20.0),
484 ..Default::default()
485 };
486 let preferred_size = grid_style.size.map(|s| s.into_option());
487 let width = compute_explicit_grid_size_in_axis(
488 &grid_style,
489 &grid_style.grid_template_columns,
490 preferred_size,
491 AbsoluteAxis::Horizontal,
492 );
493 let height = compute_explicit_grid_size_in_axis(
494 &grid_style,
495 &grid_style.grid_template_rows,
496 preferred_size,
497 AbsoluteAxis::Vertical,
498 );
499 assert_eq!(width, 3);
500 assert_eq!(height, 1);
501 }
502
503 #[test]
504 fn explicit_grid_sizing_mix_repeated_and_non_repeated() {
505 use GridTrackRepetition::AutoFill;
506 let grid_style = Style {
507 display: Display::Grid,
508 size: Size { width: length(140.0), height: length(100.0) },
509 grid_template_columns: vec![length(20.0), repeat(AutoFill, vec![length(40.0)])],
510 grid_template_rows: vec![length(40.0), repeat(AutoFill, vec![length(20.0)])],
511 gap: length(20.0),
512 ..Default::default()
513 };
514 let preferred_size = grid_style.size.map(|s| s.into_option());
515 let width = compute_explicit_grid_size_in_axis(
516 &grid_style,
517 &grid_style.grid_template_columns,
518 preferred_size,
519 AbsoluteAxis::Horizontal,
520 );
521 let height = compute_explicit_grid_size_in_axis(
522 &grid_style,
523 &grid_style.grid_template_rows,
524 preferred_size,
525 AbsoluteAxis::Vertical,
526 );
527 assert_eq!(width, 3); // 3 tracks + 2 gaps
528 assert_eq!(height, 2); // 2 tracks + 1 gap
529 }
530
531 #[test]
532 fn explicit_grid_sizing_mix_with_padding() {
533 use GridTrackRepetition::AutoFill;
534 let grid_style = Style {
535 display: Display::Grid,
536 size: Size { width: length(120.0), height: length(120.0) },
537 padding: Rect { left: length(10.0), right: length(10.0), top: length(20.0), bottom: length(20.0) },
538 grid_template_columns: vec![repeat(AutoFill, vec![length(20.0)])],
539 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
540 ..Default::default()
541 };
542 let inner_container_size = Size { width: Some(100.0), height: Some(80.0) };
543 let width = compute_explicit_grid_size_in_axis(
544 &grid_style,
545 &grid_style.grid_template_columns,
546 inner_container_size,
547 AbsoluteAxis::Horizontal,
548 );
549 let height = compute_explicit_grid_size_in_axis(
550 &grid_style,
551 &grid_style.grid_template_rows,
552 inner_container_size,
553 AbsoluteAxis::Vertical,
554 );
555 assert_eq!(width, 5); // 40px horizontal padding
556 assert_eq!(height, 4); // 20px vertical padding
557 }
558
559 #[test]
560 fn test_initialize_grid_tracks() {
561 let px0 = LengthPercentage::Length(0.0);
562 let px20 = LengthPercentage::Length(20.0);
563 let px100 = LengthPercentage::Length(100.0);
564
565 // Setup test
566 let track_template = vec![length(100.0), minmax(length(100.0), fr(2.0)), fr(1.0)];
567 let track_counts =
568 TrackCounts { negative_implicit: 3, explicit: track_template.len() as u16, positive_implicit: 3 };
569 let auto_tracks = vec![auto(), length(100.0)];
570 let gap = px20;
571
572 // Call function
573 let mut tracks = Vec::new();
574 initialize_grid_tracks(&mut tracks, track_counts, &track_template, &auto_tracks, gap, |_| false);
575
576 // Assertions
577 let expected = vec![
578 // Gutter
579 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px0), MaxTrackSizingFunction::Fixed(px0)),
580 // Negative implicit tracks
581 (GridTrackKind::Track, MinTrackSizingFunction::Fixed(px100), MaxTrackSizingFunction::Fixed(px100)),
582 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
583 (GridTrackKind::Track, MinTrackSizingFunction::Auto, MaxTrackSizingFunction::Auto),
584 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
585 (GridTrackKind::Track, MinTrackSizingFunction::Fixed(px100), MaxTrackSizingFunction::Fixed(px100)),
586 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
587 // Explicit tracks
588 (GridTrackKind::Track, MinTrackSizingFunction::Fixed(px100), MaxTrackSizingFunction::Fixed(px100)),
589 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
590 (GridTrackKind::Track, MinTrackSizingFunction::Fixed(px100), MaxTrackSizingFunction::Fraction(2.0)), // Note: separate min-max functions
591 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
592 (GridTrackKind::Track, MinTrackSizingFunction::Auto, MaxTrackSizingFunction::Fraction(1.0)), // Note: min sizing function of flex sizing functions is auto
593 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
594 // Positive implicit tracks
595 (GridTrackKind::Track, MinTrackSizingFunction::Auto, MaxTrackSizingFunction::Auto),
596 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
597 (GridTrackKind::Track, MinTrackSizingFunction::Fixed(px100), MaxTrackSizingFunction::Fixed(px100)),
598 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
599 (GridTrackKind::Track, MinTrackSizingFunction::Auto, MaxTrackSizingFunction::Auto),
600 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px0), MaxTrackSizingFunction::Fixed(px0)),
601 ];
602
603 assert_eq!(tracks.len(), expected.len(), "Number of tracks doesn't match");
604
605 for (idx, (actual, (kind, min, max))) in tracks.into_iter().zip(expected).enumerate() {
606 assert_eq!(actual.kind, kind, "Track {idx} (0-based index)");
607 assert_eq!(actual.min_track_sizing_function, min, "Track {idx} (0-based index)");
608 assert_eq!(actual.max_track_sizing_function, max, "Track {idx} (0-based index)");
609 }
610 }
611}