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