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