gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 499HTML 254C 19JavaScript 81371 Definiciones

⎇ ebd376fbebd0ba73c79aa00d1835918680ef82a0 src/compute/grid/explicit_grid.rs Rust · 503 líneas · 24.6 KB Historia Quién tocó qué crudo

Grid: Add support for repeat() with an integer repetition count (#342) ebd376fbe · Nico Burns ·

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