1use super::types::{GridTrack, TrackCounts};
4use crate::geometry::{AbsoluteAxis, Size};
5use crate::style::{GridTrackRepetition, LengthPercentage, NonRepeatedTrackSizingFunction, Style, TrackSizingFunction};
6use crate::style_helpers::TaffyAuto;
7use crate::util::sys::{GridTrackVec, Vec};
8use crate::util::MaybeMath;
9use crate::util::ResolveOrZero;
10
11#[cfg(not(feature = "std"))]
12use num_traits::float::FloatCore;
13
14pub(crate) fn compute_explicit_grid_size_in_axis(
16 style: &Style,
17 preferred_size: Size<Option<f32>>,
18 axis: AbsoluteAxis,
19) -> u16 {
20 let template = style.grid_template_tracks(axis);
22
23 if template.is_empty() {
25 return 0;
26 }
27
28 let template_has_repetitions_with_zero_tracks = template.iter().any(|track_def| match track_def {
31 TrackSizingFunction::Single(_) => false,
32 TrackSizingFunction::Repeat(_, tracks) => tracks.is_empty(),
33 });
34 if template_has_repetitions_with_zero_tracks {
35 return 0;
36 }
37
38 let non_auto_repeating_track_count = template
40 .iter()
41 .map(|track_def| {
42 use GridTrackRepetition::{AutoFill, AutoFit, Count};
43 match track_def {
44 TrackSizingFunction::Single(_) => 1,
45 TrackSizingFunction::Repeat(Count(count), tracks) => count * tracks.len() as u16,
46 TrackSizingFunction::Repeat(AutoFit | AutoFill, _) => 0,
47 }
48 })
49 .sum::<u16>();
50
51 let auto_repetition_count = template.iter().filter(|track_def| track_def.is_auto_repetition()).count() as u16;
52 let all_track_defs_have_fixed_component = template.iter().all(|track_def| match track_def {
53 TrackSizingFunction::Single(sizing_function) => sizing_function.has_fixed_component(),
54 TrackSizingFunction::Repeat(_, tracks) => {
55 tracks.iter().all(|sizing_function| sizing_function.has_fixed_component())
56 }
57 });
58
59 let template_is_valid =
60 auto_repetition_count == 0 || (auto_repetition_count == 1 && all_track_defs_have_fixed_component);
61
62 if !template_is_valid {
65 return 0;
66 }
67
68 if auto_repetition_count == 0 {
71 return non_auto_repeating_track_count;
72 }
73
74 let repetition_definition = template
75 .iter()
76 .find_map(|def| {
77 use GridTrackRepetition::{AutoFill, AutoFit, Count};
78 match def {
79 TrackSizingFunction::Single(_) => None,
80 TrackSizingFunction::Repeat(Count(_), _) => None,
81 TrackSizingFunction::Repeat(AutoFit | AutoFill, tracks) => Some(tracks),
82 }
83 })
84 .unwrap();
85 let repetition_track_count = repetition_definition.len() as u16;
86
87 let style_size = preferred_size.get_abs(axis);
96 let style_min_size = style.min_size.get_abs(axis).into_option();
97 let style_max_size = style.max_size.get_abs(axis).into_option();
98
99 let outer_container_size = style_size.maybe_min(style_max_size).or(style_max_size).or(style_min_size);
100 let inner_container_size = outer_container_size.map(|size| {
101 let padding_sum = style.padding.resolve_or_zero(outer_container_size).grid_axis_sum(axis);
102 let border_sum = style.border.resolve_or_zero(outer_container_size).grid_axis_sum(axis);
103 size - padding_sum - border_sum
104 });
105 let size_is_maximum = style_size.is_some() || style_max_size.is_some();
106
107 let num_repetitions: u16 = match inner_container_size {
109 None => 1,
110 Some(inner_container_size) => {
111 let parent_size = Some(inner_container_size);
112
113 fn track_definite_value(sizing_function: &NonRepeatedTrackSizingFunction, parent_size: Option<f32>) -> f32 {
116 let max_size = sizing_function.max.definite_value(parent_size);
117 let min_size = sizing_function.max.definite_value(parent_size);
118 max_size.map(|max| max.maybe_min(min_size)).or(min_size).unwrap()
119 }
120
121 let non_repeating_track_used_space: f32 = template
122 .iter()
123 .map(|track_def| {
124 use GridTrackRepetition::{AutoFill, AutoFit, Count};
125 match track_def {
126 TrackSizingFunction::Single(sizing_function) => {
127 track_definite_value(sizing_function, parent_size)
128 }
129 TrackSizingFunction::Repeat(Count(count), repeated_tracks) => {
130 let sum = repeated_tracks
131 .iter()
132 .map(|sizing_function| track_definite_value(sizing_function, parent_size))
133 .sum::<f32>();
134 sum * (*count as f32)
135 }
136 TrackSizingFunction::Repeat(AutoFit | AutoFill, _) => 0.0,
137 }
138 })
139 .sum();
140 let gap_size = style.gap.get_abs(axis).resolve_or_zero(Some(inner_container_size));
141
142 let per_repetition_track_used_space: f32 = repetition_definition
144 .iter()
145 .map(|sizing_function| track_definite_value(sizing_function, parent_size))
146 .sum::<f32>();
147
148 let first_repetition_and_non_repeating_tracks_used_space = non_repeating_track_used_space
151 + per_repetition_track_used_space
152 + ((non_auto_repeating_track_count + repetition_track_count).saturating_sub(1) as f32 * gap_size);
153
154 if first_repetition_and_non_repeating_tracks_used_space > inner_container_size {
157 1u16
158 } else {
159 let per_repetition_gap_used_space = (repetition_definition.len() as f32) * gap_size;
160 let per_repetition_used_space = per_repetition_track_used_space + per_repetition_gap_used_space;
161 let num_repetition_that_fit = (inner_container_size
162 - first_repetition_and_non_repeating_tracks_used_space)
163 / per_repetition_used_space;
164
165 if size_is_maximum {
172 (num_repetition_that_fit.floor() as u16) + 1
173 } else {
174 (num_repetition_that_fit.ceil() as u16) + 1
175 }
176 }
177 }
178 };
179
180 non_auto_repeating_track_count + (repetition_track_count * num_repetitions)
181}
182
183pub(super) fn initialize_grid_tracks(
186 tracks: &mut Vec<GridTrack>,
187 counts: TrackCounts,
188 track_template: &GridTrackVec<TrackSizingFunction>,
189 auto_tracks: &Vec<NonRepeatedTrackSizingFunction>,
190 gap: LengthPercentage,
191 track_has_items: impl Fn(usize) -> bool,
192) {
193 tracks.clear();
196 tracks.reserve((counts.len() * 2) + 1);
197 tracks.push(GridTrack::gutter(gap));
198
199 if counts.negative_implicit > 0 {
201 if auto_tracks.is_empty() {
202 let iter = core::iter::repeat(NonRepeatedTrackSizingFunction::AUTO);
203 create_implicit_tracks(tracks, counts.negative_implicit, iter, gap)
204 } else {
205 let offset = auto_tracks.len() - (counts.negative_implicit as usize % auto_tracks.len());
206 let iter = auto_tracks.iter().copied().cycle().skip(offset);
207 create_implicit_tracks(tracks, counts.negative_implicit, iter, gap)
208 }
209 }
210
211 let mut current_track_index = (counts.negative_implicit) as usize;
212
213 if counts.explicit > 0 {
217 track_template.iter().for_each(|track_sizing_function| {
218 use GridTrackRepetition::{AutoFill, AutoFit, Count};
219 match track_sizing_function {
220 TrackSizingFunction::Single(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 TrackSizingFunction::Repeat(Count(count), repeated_tracks) => {
229 let track_iter = repeated_tracks.iter().cycle().take(repeated_tracks.len() * *count as usize);
230 track_iter.for_each(|sizing_function| {
231 tracks.push(GridTrack::new(
232 sizing_function.min_sizing_function(),
233 sizing_function.max_sizing_function(),
234 ));
235 tracks.push(GridTrack::gutter(gap));
236 current_track_index += 1;
237 });
238 }
239 TrackSizingFunction::Repeat(repetition_kind @ (AutoFit | AutoFill), repeated_tracks) => {
240 let auto_repeated_track_count = (counts.explicit - (track_template.len() as u16 - 1)) as usize;
241 let iter = repeated_tracks.iter().copied().cycle();
242 for track_def in iter.take(auto_repeated_track_count) {
243 let mut track =
244 GridTrack::new(track_def.min_sizing_function(), track_def.max_sizing_function());
245 let mut gutter = GridTrack::gutter(gap);
246
247 if *repetition_kind == AutoFit && !track_has_items(current_track_index) {
249 track.collapse();
250 gutter.collapse();
251 }
252
253 tracks.push(track);
254 tracks.push(gutter);
255
256 current_track_index += 1;
257 }
258 }
259 }
260 });
261 }
262
263 if auto_tracks.is_empty() {
265 let iter = core::iter::repeat(NonRepeatedTrackSizingFunction::AUTO);
266 create_implicit_tracks(tracks, counts.positive_implicit, iter, gap)
267 } else {
268 let iter = auto_tracks.iter().copied().cycle();
269 create_implicit_tracks(tracks, counts.positive_implicit, iter, gap)
270 }
271
272 tracks.first_mut().unwrap().collapse();
274 tracks.last_mut().unwrap().collapse();
275}
276
277fn create_implicit_tracks(
279 tracks: &mut Vec<GridTrack>,
280 count: u16,
281 mut auto_tracks_iter: impl Iterator<Item = NonRepeatedTrackSizingFunction>,
282 gap: LengthPercentage,
283) {
284 for _ in 0..count {
285 let track_def = auto_tracks_iter.next().unwrap();
286 tracks.push(GridTrack::new(track_def.min_sizing_function(), track_def.max_sizing_function()));
287 tracks.push(GridTrack::gutter(gap));
288 }
289}
290
291#[cfg(test)]
292mod test {
293 use super::compute_explicit_grid_size_in_axis;
294 use super::initialize_grid_tracks;
295 use crate::compute::grid::types::GridTrackKind;
296 use crate::compute::grid::types::TrackCounts;
297 use crate::compute::grid::util::*;
298 use crate::geometry::AbsoluteAxis;
299 use crate::prelude::*;
300
301 #[test]
302 fn explicit_grid_sizing_no_repeats() {
303 let grid_style = (600.0, 600.0, 2, 4).into_grid();
304 let preferred_size = grid_style.size.map(|s| s.into_option());
305 let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
306 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
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(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
323 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
324 assert_eq!(width, 3);
325 assert_eq!(height, 4);
326 }
327
328 #[test]
329 fn explicit_grid_sizing_auto_fill_non_exact_fit() {
330 use GridTrackRepetition::AutoFill;
331 let grid_style = Style {
332 display: Display::Grid,
333 size: Size { width: length(140.0), height: length(90.0) },
334 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
335 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
336 ..Default::default()
337 };
338 let preferred_size = grid_style.size.map(|s| s.into_option());
339 let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
340 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
341 assert_eq!(width, 3);
342 assert_eq!(height, 4);
343 }
344
345 #[test]
346 fn explicit_grid_sizing_auto_fill_min_size_exact_fit() {
347 use GridTrackRepetition::AutoFill;
348 let grid_style = Style {
349 display: Display::Grid,
350 min_size: Size { width: length(120.0), height: length(80.0) },
351 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
352 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
353 ..Default::default()
354 };
355 let preferred_size = grid_style.size.map(|s| s.into_option());
356 let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
357 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
358 assert_eq!(width, 3);
359 assert_eq!(height, 4);
360 }
361
362 #[test]
363 fn explicit_grid_sizing_auto_fill_min_size_non_exact_fit() {
364 use GridTrackRepetition::AutoFill;
365 let grid_style = Style {
366 display: Display::Grid,
367 min_size: Size { width: length(140.0), height: length(90.0) },
368 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
369 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
370 ..Default::default()
371 };
372 let preferred_size = grid_style.size.map(|s| s.into_option());
373 let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
374 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
375 assert_eq!(width, 4);
376 assert_eq!(height, 5);
377 }
378
379 #[test]
380 fn explicit_grid_sizing_auto_fill_multiple_repeated_tracks() {
381 use GridTrackRepetition::AutoFill;
382 let grid_style = Style {
383 display: Display::Grid,
384 size: Size { width: length(140.0), height: length(100.0) },
385 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0), length(20.0)])],
386 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0), length(10.0)])],
387 ..Default::default()
388 };
389 let preferred_size = grid_style.size.map(|s| s.into_option());
390 let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
391 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
392 assert_eq!(width, 4); assert_eq!(height, 6); }
395
396 #[test]
397 fn explicit_grid_sizing_auto_fill_gap() {
398 use GridTrackRepetition::AutoFill;
399 let grid_style = Style {
400 display: Display::Grid,
401 size: Size { width: length(140.0), height: length(100.0) },
402 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0)])],
403 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
404 gap: length(20.0),
405 ..Default::default()
406 };
407 let preferred_size = grid_style.size.map(|s| s.into_option());
408 let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
409 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
410 assert_eq!(width, 2); assert_eq!(height, 3); }
413
414 #[test]
415 fn explicit_grid_sizing_no_defined_size() {
416 use GridTrackRepetition::AutoFill;
417 let grid_style = Style {
418 display: Display::Grid,
419 grid_template_columns: vec![repeat(AutoFill, vec![length(40.0), percent(0.5), length(20.0)])],
420 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
421 gap: length(20.0),
422 ..Default::default()
423 };
424 let preferred_size = grid_style.size.map(|s| s.into_option());
425 let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
426 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
427 assert_eq!(width, 3);
428 assert_eq!(height, 1);
429 }
430
431 #[test]
432 fn explicit_grid_sizing_mix_repeated_and_non_repeated() {
433 use GridTrackRepetition::AutoFill;
434 let grid_style = Style {
435 display: Display::Grid,
436 size: Size { width: length(140.0), height: length(100.0) },
437 grid_template_columns: vec![length(20.0), repeat(AutoFill, vec![length(40.0)])],
438 grid_template_rows: vec![length(40.0), repeat(AutoFill, vec![length(20.0)])],
439 gap: length(20.0),
440 ..Default::default()
441 };
442 let preferred_size = grid_style.size.map(|s| s.into_option());
443 let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
444 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
445 assert_eq!(width, 3); assert_eq!(height, 2); }
448
449 #[test]
450 fn explicit_grid_sizing_mix_with_padding() {
451 use GridTrackRepetition::AutoFill;
452 let grid_style = Style {
453 display: Display::Grid,
454 size: Size { width: length(120.0), height: length(120.0) },
455 padding: Rect { left: length(10.0), right: length(10.0), top: length(20.0), bottom: length(20.0) },
456 grid_template_columns: vec![repeat(AutoFill, vec![length(20.0)])],
457 grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
458 ..Default::default()
459 };
460 let preferred_size = grid_style.size.map(|s| s.into_option());
461 let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
462 let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
463 assert_eq!(width, 5); assert_eq!(height, 4); }
466
467 #[test]
468 fn test_initialize_grid_tracks() {
469 let px0 = LengthPercentage::Length(0.0);
470 let px20 = LengthPercentage::Length(20.0);
471 let px100 = LengthPercentage::Length(100.0);
472
473 let track_template = vec![length(100.0), minmax(length(100.0), fr(2.0)), fr(1.0)];
475 let track_counts =
476 TrackCounts { negative_implicit: 3, explicit: track_template.len() as u16, positive_implicit: 3 };
477 let auto_tracks = vec![auto(), length(100.0)];
478 let gap = px20;
479
480 let mut tracks = Vec::new();
482 initialize_grid_tracks(&mut tracks, track_counts, &track_template, &auto_tracks, gap, |_| false);
483
484 let expected = vec![
486 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px0), MaxTrackSizingFunction::Fixed(px0)),
488 (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(px20), MaxTrackSizingFunction::Fixed(px20)),
493 (GridTrackKind::Track, MinTrackSizingFunction::Fixed(px100), MaxTrackSizingFunction::Fixed(px100)),
494 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
495 (GridTrackKind::Track, MinTrackSizingFunction::Fixed(px100), MaxTrackSizingFunction::Fixed(px100)),
497 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
498 (GridTrackKind::Track, MinTrackSizingFunction::Fixed(px100), MaxTrackSizingFunction::Fraction(2.0)), (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
500 (GridTrackKind::Track, MinTrackSizingFunction::Auto, MaxTrackSizingFunction::Fraction(1.0)), (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
502 (GridTrackKind::Track, MinTrackSizingFunction::Auto, MaxTrackSizingFunction::Auto),
504 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
505 (GridTrackKind::Track, MinTrackSizingFunction::Fixed(px100), MaxTrackSizingFunction::Fixed(px100)),
506 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px20), MaxTrackSizingFunction::Fixed(px20)),
507 (GridTrackKind::Track, MinTrackSizingFunction::Auto, MaxTrackSizingFunction::Auto),
508 (GridTrackKind::Gutter, MinTrackSizingFunction::Fixed(px0), MaxTrackSizingFunction::Fixed(px0)),
509 ];
510
511 assert_eq!(tracks.len(), expected.len(), "Number of tracks doesn't match");
512
513 for (idx, (actual, (kind, min, max))) in tracks.into_iter().zip(expected).enumerate() {
514 assert_eq!(actual.kind, kind, "Track {idx} (0-based index)");
515 assert_eq!(actual.min_track_sizing_function, min, "Track {idx} (0-based index)");
516 assert_eq!(actual.max_track_sizing_function, max, "Track {idx} (0-based index)");
517 }
518 }
519}