//! Helper functions for initialising GridTrack's from styles //! This mainly consists of evaluating GridAutoTracks use super::types::{GridTrack, TrackCounts}; use crate::geometry::AbsoluteAxis; use crate::style::{LengthPercentage, RepetitionCount, TrackSizingFunction}; use crate::style_helpers::TaffyAuto; use crate::util::sys::{ceil, floor, Vec}; use crate::util::MaybeMath; use crate::util::ResolveOrZero; use crate::{GenericGridTemplateComponent, GenericRepetition, GridContainerStyle}; /// The auto-repeat fit strategy to use pub(crate) enum AutoRepeatStrategy { /// If the grid container has a definite size or max size in the relevant axis: /// - then the number of repetitions is the largest possible positive integer that does not cause the grid to overflow the content /// box of its grid container. MaxRepetitionsThatDoNotOverflow, /// Otherwise, if the grid container has a definite min size in the relevant axis: /// - then the number of repetitions is the smallest possible positive integer that fulfills that minimum requirement MinRepetitionsThatDoOverflow, } /// Compute the number of rows and columns in the explicit grid pub(crate) fn compute_explicit_grid_size_in_axis( style: &impl GridContainerStyle, auto_fit_container_size: Option, auto_fit_strategy: AutoRepeatStrategy, resolve_calc_value: impl Fn(*const (), f32) -> f32, axis: AbsoluteAxis, ) -> (u16, u16) { let template = match axis { AbsoluteAxis::Horizontal => style.grid_template_columns(), AbsoluteAxis::Vertical => style.grid_template_rows(), }; let Some(template) = template else { return (0, 0); }; // If template contains no tracks, then there are trivially zero explicit tracks let track_count = template.len(); if track_count == 0 { return (0, 0); } // If there are any repetitions that contains no tracks, then the whole definition should be considered invalid // and we default to no explicit tracks let template_has_repetitions_with_zero_tracks = template.clone().any(|track_def| match track_def { GenericGridTemplateComponent::Single(_) => false, GenericGridTemplateComponent::Repeat(repeat) => repeat.track_count() == 0, }); if template_has_repetitions_with_zero_tracks { return (0, 0); } // Compute that number of track generated by single track definition and repetitions with a fixed repetition count let non_auto_repeating_track_count = template .clone() .map(|track_def| match track_def { GenericGridTemplateComponent::Single(_) => 1, GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() { RepetitionCount::Count(count) => count * repeat.track_count(), RepetitionCount::AutoFit | RepetitionCount::AutoFill => 0, }, }) .sum::(); let auto_repetition_count: u16 = template.clone().filter(|track_def| track_def.is_auto_repetition()).count() as u16; let all_track_defs_have_fixed_component = template.clone().all(|track_def| match track_def { GenericGridTemplateComponent::Single(sizing_function) => sizing_function.has_fixed_component(), GenericGridTemplateComponent::Repeat(repeat) => { repeat.tracks().all(|sizing_function| sizing_function.has_fixed_component()) } }); let template_is_valid = auto_repetition_count == 0 || (auto_repetition_count == 1 && all_track_defs_have_fixed_component); // If the template is invalid because it contains multiple auto-repetition definitions or it combines an auto-repetition // definition with non-fixed-size track sizing functions, then disregard it entirely and default to zero explicit tracks if !template_is_valid { return (0, 0); } // If there are no repetitions, then the number of explicit tracks is simply equal to the lengths of the track definition // vector (as each item in the Vec represents one track). if auto_repetition_count == 0 { return (0, non_auto_repeating_track_count); } let repetition_definition = template .clone() .find_map(|def| match def { GenericGridTemplateComponent::Single(_) => None, GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() { RepetitionCount::Count(_) => None, RepetitionCount::AutoFit | RepetitionCount::AutoFill => Some(repeat), }, }) .unwrap(); let repetition_definition_iter = repetition_definition.tracks(); let repetition_track_count = repetition_definition_iter.len() as u16; // Determine the number of repetitions let num_repetitions: u16 = match auto_fit_container_size { None => 1, Some(inner_container_size) => { let parent_size = Some(inner_container_size); /// ...treating each track as its max track sizing function if that is definite or as its minimum track sizing function /// otherwise, flooring the max track sizing function by the min track sizing function if both are definite fn track_definite_value( sizing_function: TrackSizingFunction, parent_size: Option, calc_resolver: impl Fn(*const (), f32) -> f32, ) -> f32 { let max_size = sizing_function.max.definite_value(parent_size, &calc_resolver); let min_size = sizing_function.min.definite_value(parent_size, &calc_resolver); max_size.map(|max| max.maybe_max(min_size)).or(min_size).unwrap() } let non_repeating_track_used_space: f32 = template .clone() .map(|track_def| match track_def { GenericGridTemplateComponent::Single(sizing_function) => { track_definite_value(sizing_function, parent_size, &resolve_calc_value) } GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() { RepetitionCount::Count(count) => { let sum = repeat .tracks() .map(|sizing_function| { track_definite_value(sizing_function, parent_size, &resolve_calc_value) }) .sum::(); sum * (count as f32) } RepetitionCount::AutoFit | RepetitionCount::AutoFill => 0.0, }, }) .sum(); let gap_size = style.gap().get_abs(axis).resolve_or_zero(Some(inner_container_size), &resolve_calc_value); // Compute the amount of space that a single repetition of the repeated track list takes let per_repetition_track_used_space: f32 = repetition_definition_iter .map(|sizing_function| track_definite_value(sizing_function, parent_size, &resolve_calc_value)) .sum::(); // We special case the first repetition here because the number of gaps in the first repetition // depends on the number of non-repeating tracks in the template let first_repetition_and_non_repeating_tracks_used_space = non_repeating_track_used_space + per_repetition_track_used_space + ((non_auto_repeating_track_count + repetition_track_count).saturating_sub(1) as f32 * gap_size); // If a single repetition already overflows the container then we return 1 as the repetition count // (the number of repetitions is floored at 1) if first_repetition_and_non_repeating_tracks_used_space > inner_container_size { 1u16 } else { let per_repetition_gap_used_space = (repetition_track_count as f32) * gap_size; let per_repetition_used_space = per_repetition_track_used_space + per_repetition_gap_used_space; let num_repetition_that_fit = (inner_container_size - first_repetition_and_non_repeating_tracks_used_space) / per_repetition_used_space; // If the container size is a preferred or maximum size: // Then we return the maximum number of repetitions that fit into the container without overflowing. // If the container size is a minimum size: // - Then we return the minimum number of repetitions required to overflow the size. // // In all cases we add the additional repetition that was already accounted for in the special-case computation above match auto_fit_strategy { AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow => (floor(num_repetition_that_fit) as u16) + 1, AutoRepeatStrategy::MinRepetitionsThatDoOverflow => (ceil(num_repetition_that_fit) as u16) + 1, } } } }; let grid_template_track_count = non_auto_repeating_track_count + (repetition_track_count * num_repetitions); (num_repetitions, grid_template_track_count) } /// Resolve the track sizing functions of explicit tracks, automatically created tracks, and gutters /// given a set of track counts and all of the relevant styles pub(super) fn initialize_grid_tracks( tracks: &mut Vec, counts: TrackCounts, style: &impl GridContainerStyle, axis: AbsoluteAxis, track_has_items: impl Fn(usize) -> bool, ) { // Extract styles let track_template; let auto_tracks; let gap; match axis { AbsoluteAxis::Horizontal => { track_template = style.grid_template_columns(); auto_tracks = style.grid_auto_columns(); gap = style.gap().width; } AbsoluteAxis::Vertical => { track_template = style.grid_template_rows(); auto_tracks = style.grid_auto_rows(); gap = style.gap().height; } }; // Clear vector (in case this is a re-layout), reserve space for all tracks ahead of time to reduce allocations, // and push the initial gutter tracks.clear(); tracks.reserve((counts.len() * 2) + 1); tracks.push(GridTrack::gutter(gap)); let auto_track_count = auto_tracks.len(); // Create negative implicit tracks if counts.negative_implicit > 0 { if auto_track_count == 0 { let iter = core::iter::repeat(TrackSizingFunction::AUTO); create_implicit_tracks(tracks, counts.negative_implicit, iter, gap) } else { let offset = auto_track_count - (counts.negative_implicit as usize % auto_track_count); let iter = auto_tracks.clone().cycle().skip(offset); create_implicit_tracks(tracks, counts.negative_implicit, iter, gap) } } let mut current_track_index = (counts.negative_implicit) as usize; // Create explicit tracks // An explicit check against the count (rather than just relying on track_template being empty) is required here // because a count of zero can result from the track_template being invalid, in which case it should be ignored. if counts.explicit > 0 { if let Some(track_template) = track_template { track_template.clone().for_each(|track_sizing_function| { match track_sizing_function { GenericGridTemplateComponent::Single(sizing_function) => { tracks.push(GridTrack::new( sizing_function.min_sizing_function(), sizing_function.max_sizing_function(), )); tracks.push(GridTrack::gutter(gap)); current_track_index += 1; } GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() { RepetitionCount::Count(count) => { let track_iter = repeat.tracks(); let track_iter = track_iter.cycle().take(repeat.track_count() as usize * count as usize); track_iter.for_each(|sizing_function| { tracks.push(GridTrack::new( sizing_function.min_sizing_function(), sizing_function.max_sizing_function(), )); tracks.push(GridTrack::gutter(gap)); current_track_index += 1; }); } RepetitionCount::AutoFit | RepetitionCount::AutoFill => { let auto_repeated_track_count = (counts.explicit - (track_template.len() as u16 - 1)) as usize; let iter = repeat.tracks().cycle(); for track_def in iter.take(auto_repeated_track_count) { let mut track = GridTrack::new(track_def.min_sizing_function(), track_def.max_sizing_function()); let mut gutter = GridTrack::gutter(gap); // Auto-fit tracks that don't contain should be collapsed. if repeat.count() == RepetitionCount::AutoFit && !track_has_items(current_track_index) { track.collapse(); gutter.collapse(); } tracks.push(track); tracks.push(gutter); current_track_index += 1; } } }, } }); } } let grid_area_tracks = (counts.negative_implicit + counts.explicit) - current_track_index as u16; // Create positive implicit tracks if auto_track_count == 0 { let iter = core::iter::repeat(TrackSizingFunction::AUTO); create_implicit_tracks(tracks, counts.positive_implicit + grid_area_tracks, iter, gap) } else { let iter = auto_tracks.clone().cycle(); create_implicit_tracks(tracks, counts.positive_implicit + grid_area_tracks, iter, gap) } // Mark first and last grid lines as collapsed tracks.first_mut().unwrap().collapse(); tracks.last_mut().unwrap().collapse(); } /// Utility function for repeating logic of creating implicit tracks fn create_implicit_tracks( tracks: &mut Vec, count: u16, mut auto_tracks_iter: impl Iterator, gap: LengthPercentage, ) { for _ in 0..count { let track_def = auto_tracks_iter.next().unwrap(); tracks.push(GridTrack::new(track_def.min_sizing_function(), track_def.max_sizing_function())); tracks.push(GridTrack::gutter(gap)); } } #[cfg(test)] mod test { use super::compute_explicit_grid_size_in_axis; use super::initialize_grid_tracks; use crate::compute::grid::explicit_grid::AutoRepeatStrategy; use crate::compute::grid::types::GridTrackKind; use crate::compute::grid::types::TrackCounts; use crate::compute::grid::util::*; use crate::geometry::AbsoluteAxis; use crate::prelude::*; use crate::sys::DefaultCheapStr; #[test] fn explicit_grid_sizing_no_repeats() { let grid_style = (600.0, 600.0, 2, 4).into_grid(); let preferred_size = grid_style.size.map(|s| s.into_option()); let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 2); assert_eq!(row_count, 4); assert_eq!(auto_col_reps, 0); assert_eq!(auto_row_reps, 0); } #[test] fn explicit_grid_sizing_auto_fill_exact_fit() { use RepetitionCount::AutoFill; let grid_style: Style = Style { display: Display::Grid, size: Size { width: length(120.0), height: length(80.0) }, grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])], grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])], ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 3); assert_eq!(row_count, 4); assert_eq!(auto_col_reps, 3); assert_eq!(auto_row_reps, 4); } #[test] fn explicit_grid_sizing_auto_fill_non_exact_fit() { use RepetitionCount::AutoFill; let grid_style: Style = Style { display: Display::Grid, size: Size { width: length(140.0), height: length(90.0) }, grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])], grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])], ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 3); assert_eq!(row_count, 4); assert_eq!(auto_col_reps, 3); assert_eq!(auto_row_reps, 4); } #[test] fn explicit_grid_sizing_auto_fill_min_size_exact_fit() { use RepetitionCount::AutoFill; let grid_style: Style = Style { display: Display::Grid, min_size: Size { width: length(120.0), height: length(80.0) }, grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])], grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])], ..Default::default() }; let inner_container_size = Size { width: Some(120.0), height: Some(80.0) }; let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, inner_container_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MinRepetitionsThatDoOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, inner_container_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MinRepetitionsThatDoOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 3); assert_eq!(row_count, 4); assert_eq!(auto_col_reps, 3); assert_eq!(auto_row_reps, 4); } #[test] fn explicit_grid_sizing_auto_fill_min_size_non_exact_fit() { use RepetitionCount::AutoFill; let grid_style: Style = Style { display: Display::Grid, min_size: Size { width: length(140.0), height: length(90.0) }, grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])], grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])], ..Default::default() }; let inner_container_size = Size { width: Some(140.0), height: Some(90.0) }; let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, inner_container_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MinRepetitionsThatDoOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, inner_container_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MinRepetitionsThatDoOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 4); assert_eq!(row_count, 5); assert_eq!(auto_col_reps, 4); assert_eq!(auto_row_reps, 5); } #[test] fn explicit_grid_sizing_auto_fill_multiple_repeated_tracks() { use RepetitionCount::AutoFill; let grid_style: Style = Style { display: Display::Grid, size: Size { width: length(140.0), height: length(100.0) }, grid_template_columns: vec![repeat(AutoFill, vec![length(40.0), length(20.0)])], grid_template_rows: vec![repeat(AutoFill, vec![length(20.0), length(10.0)])], ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 4); // 2 repetitions * 2 repeated tracks = 4 tracks in total assert_eq!(row_count, 6); // 3 repetitions * 2 repeated tracks = 4 tracks in total assert_eq!(auto_col_reps, 2); assert_eq!(auto_row_reps, 3); } #[test] fn explicit_grid_sizing_auto_fill_gap() { use RepetitionCount::AutoFill; let grid_style: Style = Style { display: Display::Grid, size: Size { width: length(140.0), height: length(100.0) }, grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])], grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])], gap: length(20.0), ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 2); // 2 tracks + 1 gap assert_eq!(row_count, 3); // 3 tracks + 2 gaps assert_eq!(auto_col_reps, 2); assert_eq!(auto_row_reps, 3); } #[test] fn explicit_grid_sizing_no_defined_size() { use RepetitionCount::AutoFill; let grid_style: Style = Style { display: Display::Grid, grid_template_columns: vec![repeat(AutoFill, vec![length(40.0), percent(0.5), length(20.0)])], grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])], gap: length(20.0), ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MinRepetitionsThatDoOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MinRepetitionsThatDoOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 3); assert_eq!(row_count, 1); assert_eq!(auto_col_reps, 1); assert_eq!(auto_row_reps, 1); } #[test] fn explicit_grid_sizing_mix_repeated_and_non_repeated() { use RepetitionCount::AutoFill; let grid_style: Style = Style { display: Display::Grid, size: Size { width: length(140.0), height: length(100.0) }, grid_template_columns: vec![length(20.0), repeat(AutoFill, vec![length(40.0)])], grid_template_rows: vec![length(40.0), repeat(AutoFill, vec![length(20.0)])], gap: length(20.0), ..Default::default() }; let preferred_size = grid_style.size.map(|s| s.into_option()); let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, preferred_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 3); // 3 tracks + 2 gaps assert_eq!(row_count, 2); // 2 tracks + 1 gap assert_eq!(auto_col_reps, 2); assert_eq!(auto_row_reps, 1); } #[test] fn explicit_grid_sizing_mix_with_padding() { use RepetitionCount::AutoFill; let grid_style: Style = Style { display: Display::Grid, size: Size { width: length(120.0), height: length(120.0) }, padding: Rect { left: length(10.0), right: length(10.0), top: length(20.0), bottom: length(20.0) }, grid_template_columns: vec![repeat(AutoFill, vec![length(20.0)])], grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])], ..Default::default() }; let inner_container_size = Size { width: Some(100.0), height: Some(80.0) }; let (auto_col_reps, col_count) = compute_explicit_grid_size_in_axis( &grid_style, inner_container_size.get_abs(AbsoluteAxis::Horizontal), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Horizontal, ); let (auto_row_reps, row_count) = compute_explicit_grid_size_in_axis( &grid_style, inner_container_size.get_abs(AbsoluteAxis::Vertical), AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow, |_, _| 42.42, AbsoluteAxis::Vertical, ); assert_eq!(col_count, 5); // 40px horizontal padding assert_eq!(row_count, 4); // 20px vertical padding assert_eq!(auto_col_reps, 5); assert_eq!(auto_row_reps, 4); } #[test] fn test_initialize_grid_tracks() { let minpx0 = MinTrackSizingFunction::from_length(0.0); let minpx20 = MinTrackSizingFunction::from_length(20.0); let minpx100 = MinTrackSizingFunction::from_length(100.0); let maxpx0 = MaxTrackSizingFunction::from_length(0.0); let maxpx20 = MaxTrackSizingFunction::from_length(20.0); let maxpx100 = MaxTrackSizingFunction::from_length(100.0); // Setup test let grid_style: Style = Style { display: Display::Grid, gap: length(20.0), grid_template_columns: vec![length(100.0), minmax(length(100.0), fr(2.0)), fr(1.0)], grid_auto_columns: vec![auto(), length(100.0)], ..Default::default() }; let track_counts = TrackCounts { negative_implicit: 3, explicit: grid_style.grid_template_columns.len() as u16, positive_implicit: 3, }; // Call function let mut tracks = Vec::new(); initialize_grid_tracks(&mut tracks, track_counts, &grid_style, AbsoluteAxis::Horizontal, |_| false); // Assertions let expected = vec![ // Gutter (GridTrackKind::Gutter, minpx0, maxpx0), // Negative implicit tracks (GridTrackKind::Track, minpx100, maxpx100), (GridTrackKind::Gutter, minpx20, maxpx20), (GridTrackKind::Track, auto(), auto()), (GridTrackKind::Gutter, minpx20, maxpx20), (GridTrackKind::Track, minpx100, maxpx100), (GridTrackKind::Gutter, minpx20, maxpx20), // Explicit tracks (GridTrackKind::Track, minpx100, maxpx100), (GridTrackKind::Gutter, minpx20, maxpx20), (GridTrackKind::Track, minpx100, MaxTrackSizingFunction::from_fr(2.0)), // Note: separate min-max functions (GridTrackKind::Gutter, minpx20, maxpx20), (GridTrackKind::Track, auto(), MaxTrackSizingFunction::from_fr(1.0)), // Note: min sizing function of flex sizing functions is AUTO (GridTrackKind::Gutter, minpx20, maxpx20), // Positive implicit tracks (GridTrackKind::Track, auto(), auto()), (GridTrackKind::Gutter, minpx20, maxpx20), (GridTrackKind::Track, minpx100, maxpx100), (GridTrackKind::Gutter, minpx20, maxpx20), (GridTrackKind::Track, auto(), auto()), (GridTrackKind::Gutter, minpx0, maxpx0), ]; assert_eq!(tracks.len(), expected.len(), "Number of tracks doesn't match"); for (idx, (actual, (kind, min, max))) in tracks.into_iter().zip(expected).enumerate() { assert_eq!(actual.kind, kind, "Track {idx} (0-based index)"); assert_eq!(actual.min_track_sizing_function, min, "Track {idx} (0-based index)"); assert_eq!(actual.max_track_sizing_function, max, "Track {idx} (0-based index)"); } } }