1use super::GridTrack;
3use crate::compute::grid::OriginZeroLine;
4use crate::geometry::AbstractAxis;
5use crate::geometry::{Line, Point, Rect, Size};
6use crate::style::{AlignItems, AlignSelf, AvailableSpace, Dimension, LengthPercentageAuto, Overflow};
7use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId, SizingMode};
8use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero};
9use crate::{BoxSizing, GridItemStyle, LengthPercentage};
10use core::ops::Range;
11
12#[derive(Debug)]
14pub(in super::super) struct GridItem {
15 pub node: NodeId,
17
18 pub source_order: u16,
23
24 pub row: Line<OriginZeroLine>,
27 pub column: Line<OriginZeroLine>,
30
31 pub is_compressible_replaced: bool,
34 pub overflow: Point<Overflow>,
36 pub box_sizing: BoxSizing,
38 pub size: Size<Dimension>,
40 pub min_size: Size<Dimension>,
42 pub max_size: Size<Dimension>,
44 pub aspect_ratio: Option<f32>,
46 pub padding: Rect<LengthPercentage>,
48 pub border: Rect<LengthPercentage>,
50 pub margin: Rect<LengthPercentageAuto>,
52 pub align_self: AlignSelf,
54 pub justify_self: AlignSelf,
56 pub baseline: Option<f32>,
58 pub baseline_shim: f32,
61
62 pub row_indexes: Line<u16>,
65 pub column_indexes: Line<u16>,
68
69 pub crosses_flexible_row: bool,
71 pub crosses_flexible_column: bool,
73 pub crosses_intrinsic_row: bool,
75 pub crosses_intrinsic_column: bool,
77
78 pub grid_area_size_cache: Option<Size<Option<f32>>>,
81 pub min_content_contribution_cache: Size<Option<f32>>,
83 pub minimum_contribution_cache: Size<Option<f32>>,
85 pub max_content_contribution_cache: Size<Option<f32>>,
87
88 pub y_position: f32,
90 pub height: f32,
92}
93
94impl GridItem {
95 pub fn new_with_placement_style_and_order<S: GridItemStyle>(
97 node: NodeId,
98 col_span: Line<OriginZeroLine>,
99 row_span: Line<OriginZeroLine>,
100 style: S,
101 parent_align_items: AlignItems,
102 parent_justify_items: AlignItems,
103 source_order: u16,
104 ) -> Self {
105 GridItem {
106 node,
107 source_order,
108 row: row_span,
109 column: col_span,
110 is_compressible_replaced: style.is_compressible_replaced(),
111 overflow: style.overflow(),
112 box_sizing: style.box_sizing(),
113 size: style.size(),
114 min_size: style.min_size(),
115 max_size: style.max_size(),
116 aspect_ratio: style.aspect_ratio(),
117 padding: style.padding(),
118 border: style.border(),
119 margin: style.margin(),
120 align_self: style.align_self().unwrap_or(parent_align_items),
121 justify_self: style.justify_self().unwrap_or(parent_justify_items),
122 baseline: None,
123 baseline_shim: 0.0,
124 row_indexes: Line { start: 0, end: 0 }, column_indexes: Line { start: 0, end: 0 }, crosses_flexible_row: false, crosses_flexible_column: false, crosses_intrinsic_row: false, crosses_intrinsic_column: false, grid_area_size_cache: None,
131 min_content_contribution_cache: Size::NONE,
132 max_content_contribution_cache: Size::NONE,
133 minimum_contribution_cache: Size::NONE,
134 y_position: 0.0,
135 height: 0.0,
136 }
137 }
138
139 pub fn placement(&self, axis: AbstractAxis) -> Line<OriginZeroLine> {
141 match axis {
142 AbstractAxis::Block => self.row,
143 AbstractAxis::Inline => self.column,
144 }
145 }
146
147 pub fn placement_indexes(&self, axis: AbstractAxis) -> Line<u16> {
149 match axis {
150 AbstractAxis::Block => self.row_indexes,
151 AbstractAxis::Inline => self.column_indexes,
152 }
153 }
154
155 pub fn track_range_excluding_lines(&self, axis: AbstractAxis) -> Range<usize> {
159 let indexes = self.placement_indexes(axis);
160 (indexes.start as usize + 1)..(indexes.end as usize)
161 }
162
163 pub fn span(&self, axis: AbstractAxis) -> u16 {
165 match axis {
166 AbstractAxis::Block => self.row.span(),
167 AbstractAxis::Inline => self.column.span(),
168 }
169 }
170
171 pub fn crosses_flexible_track(&self, axis: AbstractAxis) -> bool {
174 match axis {
175 AbstractAxis::Inline => self.crosses_flexible_column,
176 AbstractAxis::Block => self.crosses_flexible_row,
177 }
178 }
179
180 pub fn crosses_intrinsic_track(&self, axis: AbstractAxis) -> bool {
183 match axis {
184 AbstractAxis::Inline => self.crosses_intrinsic_column,
185 AbstractAxis::Block => self.crosses_intrinsic_row,
186 }
187 }
188
189 pub fn spanned_track_limit(
192 &mut self,
193 axis: AbstractAxis,
194 axis_tracks: &[GridTrack],
195 axis_parent_size: Option<f32>,
196 resolve_calc_value: &dyn Fn(*const (), f32) -> f32,
197 ) -> Option<f32> {
198 let spanned_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
199 let tracks_all_fixed = spanned_tracks.iter().all(|track| {
200 track.max_track_sizing_function.definite_limit(axis_parent_size, resolve_calc_value).is_some()
201 });
202 if tracks_all_fixed {
203 let limit: f32 = spanned_tracks
204 .iter()
205 .map(|track| {
206 track.max_track_sizing_function.definite_limit(axis_parent_size, resolve_calc_value).unwrap()
207 })
208 .sum();
209 Some(limit)
210 } else {
211 None
212 }
213 }
214
215 pub fn spanned_fixed_track_limit(
218 &mut self,
219 axis: AbstractAxis,
220 axis_tracks: &[GridTrack],
221 axis_parent_size: Option<f32>,
222 resolve_calc_value: &dyn Fn(*const (), f32) -> f32,
223 ) -> Option<f32> {
224 let spanned_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
225 let tracks_all_fixed = spanned_tracks.iter().all(|track| {
226 track.max_track_sizing_function.definite_value(axis_parent_size, resolve_calc_value).is_some()
227 });
228 if tracks_all_fixed {
229 let limit: f32 = spanned_tracks
230 .iter()
231 .map(|track| {
232 track.max_track_sizing_function.definite_value(axis_parent_size, resolve_calc_value).unwrap()
233 })
234 .sum();
235 Some(limit)
236 } else {
237 None
238 }
239 }
240
241 fn known_dimensions(
245 &self,
246 tree: &mut impl LayoutPartialTree,
247 grid_area_size: Size<Option<f32>>,
248 ) -> Size<Option<f32>> {
249 let margins = self.margins_axis_sums_with_baseline_shims(grid_area_size.width, tree);
250
251 let aspect_ratio = self.aspect_ratio;
252 let padding = self.padding.resolve_or_zero(grid_area_size.width, |val, basis| tree.calc(val, basis));
259 let border = self.border.resolve_or_zero(grid_area_size.width, |val, basis| tree.calc(val, basis));
260 let padding_border_size = (padding + border).sum_axes();
261 let box_sizing_adjustment =
262 if self.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
263 let inherent_size = self
264 .size
265 .maybe_resolve(grid_area_size, |val, basis| tree.calc(val, basis))
266 .maybe_apply_aspect_ratio(aspect_ratio)
267 .maybe_add(box_sizing_adjustment);
268 let min_size = self
269 .min_size
270 .maybe_resolve(grid_area_size, |val, basis| tree.calc(val, basis))
271 .maybe_apply_aspect_ratio(aspect_ratio)
272 .maybe_add(box_sizing_adjustment);
273 let max_size = self
274 .max_size
275 .maybe_resolve(grid_area_size, |val, basis| tree.calc(val, basis))
276 .maybe_apply_aspect_ratio(aspect_ratio)
277 .maybe_add(box_sizing_adjustment);
278
279 let grid_area_minus_item_margins_size = grid_area_size.maybe_sub(margins);
280
281 let width = inherent_size.width.or_else(|| {
284 if !self.margin.left.is_auto() && !self.margin.right.is_auto() && self.justify_self == AlignSelf::STRETCH {
289 return grid_area_minus_item_margins_size.width;
290 }
291
292 None
293 });
294 let Size { width, height } =
296 Size { width, height: inherent_size.height }.maybe_apply_aspect_ratio(aspect_ratio);
297
298 let height = height.or_else(|| {
299 if !self.margin.top.is_auto() && !self.margin.bottom.is_auto() && self.align_self == AlignSelf::STRETCH {
304 return grid_area_minus_item_margins_size.height;
305 }
306
307 None
308 });
309 let Size { width, height } = Size { width, height }.maybe_apply_aspect_ratio(aspect_ratio);
311
312 let Size { width, height } = Size { width, height }.maybe_clamp(min_size, max_size);
314
315 Size { width, height }
316 }
317
318 pub fn grid_area_size(
333 &self,
334 axis: AbstractAxis,
335 axis_tracks: &[GridTrack],
336 other_axis_tracks: &[GridTrack],
337 available_space: Size<Option<f32>>,
338 get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>,
339 resolve_calc_value: &impl Fn(*const (), f32) -> f32,
340 ) -> Size<Option<f32>> {
341 let mut size = Size::NONE;
342 size.set(
343 axis,
344 axis_tracks[self.track_range_excluding_lines(axis)]
345 .iter()
346 .map(|track| {
347 let min_size = track
348 .min_track_sizing_function
349 .definite_value(available_space.get(axis), resolve_calc_value)?;
350 let max_size = track
351 .max_track_sizing_function
352 .definite_value(available_space.get(axis), resolve_calc_value)?;
353
354 if min_size == max_size {
355 Some(track.base_size)
356 } else {
357 None
358 }
359 })
360 .sum::<Option<f32>>(),
361 );
362
363 size.set(
364 axis.other(),
365 other_axis_tracks[self.track_range_excluding_lines(axis.other())]
366 .iter()
367 .map(|track| {
368 get_track_size_estimate(track, available_space.get(axis.other()))
369 .map(|size| size + track.content_alignment_adjustment)
370 })
371 .sum::<Option<f32>>(),
372 );
373
374 size
375 }
376
377 pub fn grid_area_size_cached(
379 &mut self,
380 axis: AbstractAxis,
381 axis_tracks: &[GridTrack],
382 other_axis_tracks: &[GridTrack],
383 available_space: Size<Option<f32>>,
384 get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>,
385 resolve_calc_value: &impl Fn(*const (), f32) -> f32,
386 ) -> Size<Option<f32>> {
387 self.grid_area_size_cache.unwrap_or_else(|| {
388 let grid_area_size = self.grid_area_size(
389 axis,
390 axis_tracks,
391 other_axis_tracks,
392 available_space,
393 get_track_size_estimate,
394 resolve_calc_value,
395 );
396 self.grid_area_size_cache = Some(grid_area_size);
397 grid_area_size
398 })
399 }
400
401 #[inline(always)]
404 pub fn margins_axis_sums_with_baseline_shims(
405 &self,
406 inner_node_width: Option<f32>,
407 tree: &impl LayoutPartialTree,
408 ) -> Size<f32> {
409 Rect {
410 left: self.margin.left.resolve_or_zero(Some(0.0), |val, basis| tree.calc(val, basis)),
411 right: self.margin.right.resolve_or_zero(Some(0.0), |val, basis| tree.calc(val, basis)),
412 top: self.margin.top.resolve_or_zero(inner_node_width, |val, basis| tree.calc(val, basis))
413 + self.baseline_shim,
414 bottom: self.margin.bottom.resolve_or_zero(inner_node_width, |val, basis| tree.calc(val, basis)),
415 }
416 .sum_axes()
417 }
418
419 pub fn min_content_contribution(
421 &self,
422 axis: AbstractAxis,
423 tree: &mut impl LayoutPartialTree,
424 grid_area_size: Size<Option<f32>>,
425 available_space: Size<Option<f32>>,
426 ) -> f32 {
427 let known_dimensions = self.known_dimensions(tree, grid_area_size);
428 tree.measure_child_size(
434 self.node,
435 known_dimensions,
436 grid_area_size,
437 available_space.map(|opt| match opt {
438 Some(size) => AvailableSpace::Definite(size),
439 None => AvailableSpace::MinContent,
440 }),
441 SizingMode::InherentSize,
442 axis.as_abs_naive(),
443 Line::FALSE,
444 )
445 }
446
447 #[inline(always)]
449 pub fn min_content_contribution_cached(
450 &mut self,
451 axis: AbstractAxis,
452 tree: &mut impl LayoutPartialTree,
453 grid_area_size: Size<Option<f32>>,
454 available_space: Size<Option<f32>>,
455 ) -> f32 {
456 self.min_content_contribution_cache.get(axis).unwrap_or_else(|| {
457 let size = self.min_content_contribution(axis, tree, grid_area_size, available_space);
458 self.min_content_contribution_cache.set(axis, Some(size));
459 size
460 })
461 }
462
463 pub fn max_content_contribution(
465 &self,
466 axis: AbstractAxis,
467 tree: &mut impl LayoutPartialTree,
468 grid_area_size: Size<Option<f32>>,
469 available_space: Size<Option<f32>>,
470 ) -> f32 {
471 let known_dimensions = self.known_dimensions(tree, grid_area_size);
472 tree.measure_child_size(
476 self.node,
477 known_dimensions,
478 grid_area_size,
479 available_space.map(|opt| match opt {
480 Some(size) => AvailableSpace::Definite(size),
481 None => AvailableSpace::MaxContent,
482 }),
483 SizingMode::InherentSize,
484 axis.as_abs_naive(),
485 Line::FALSE,
486 )
487 }
488
489 #[inline(always)]
491 pub fn max_content_contribution_cached(
492 &mut self,
493 axis: AbstractAxis,
494 tree: &mut impl LayoutPartialTree,
495 grid_area_size: Size<Option<f32>>,
496 available_space: Size<Option<f32>>,
497 ) -> f32 {
498 self.max_content_contribution_cache.get(axis).unwrap_or_else(|| {
499 let size = self.max_content_contribution(axis, tree, grid_area_size, available_space);
500 self.max_content_contribution_cache.set(axis, Some(size));
501 size
502 })
503 }
504
505 pub fn minimum_contribution(
514 &mut self,
515 tree: &mut impl LayoutPartialTree,
516 axis: AbstractAxis,
517 axis_tracks: &[GridTrack],
518 grid_area_size: Size<Option<f32>>,
519 inner_node_size: Size<Option<f32>>,
520 ) -> f32 {
521 let padding = self.padding.resolve_or_zero(grid_area_size.width, |val, basis| tree.calc(val, basis));
522 let border = self.border.resolve_or_zero(grid_area_size.width, |val, basis| tree.calc(val, basis));
523 let padding_border_size = (padding + border).sum_axes();
524 let box_sizing_adjustment =
525 if self.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
526 let size = self
527 .size
528 .maybe_resolve(grid_area_size, |val, basis| tree.calc(val, basis))
529 .maybe_apply_aspect_ratio(self.aspect_ratio)
530 .maybe_add(box_sizing_adjustment)
531 .get(axis)
532 .or_else(|| {
533 self.min_size
534 .maybe_resolve(grid_area_size, |val, basis| tree.calc(val, basis))
535 .maybe_apply_aspect_ratio(self.aspect_ratio)
536 .maybe_add(box_sizing_adjustment)
537 .get(axis)
538 })
539 .or_else(|| self.overflow.get(axis).maybe_into_automatic_min_size())
540 .unwrap_or_else(|| {
541 let item_axis_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
546
547 let spans_auto_min_track = axis_tracks
552 .iter()
553 .any(|track| track.min_track_sizing_function.is_auto());
555
556 let only_span_one_track = item_axis_tracks.len() == 1;
558 let spans_a_flexible_track = axis_tracks.iter().any(|track| track.max_track_sizing_function.is_fr());
559
560 let use_content_based_minimum =
561 spans_auto_min_track && (only_span_one_track || !spans_a_flexible_track);
562
563 if use_content_based_minimum {
565 let mut minimum_contribution =
566 self.min_content_contribution_cached(axis, tree, grid_area_size, grid_area_size);
567
568 if self.is_compressible_replaced {
572 let size = self.size.get(axis).maybe_resolve(Some(0.0), |val, basis| tree.calc(val, basis));
573 let max_size =
574 self.max_size.get(axis).maybe_resolve(Some(0.0), |val, basis| tree.calc(val, basis));
575 minimum_contribution = minimum_contribution.maybe_min(size).maybe_min(max_size);
576 }
577
578 minimum_contribution
579 } else {
580 0.0
581 }
582 });
583
584 let limit = self.spanned_fixed_track_limit(axis, axis_tracks, inner_node_size.get(axis), &|val, basis| {
588 tree.resolve_calc_value(val, basis)
589 });
590 size.maybe_min(limit)
591 }
592
593 #[inline(always)]
595 pub fn minimum_contribution_cached(
596 &mut self,
597 tree: &mut impl LayoutPartialTree,
598 axis: AbstractAxis,
599 axis_tracks: &[GridTrack],
600 grid_area_size: Size<Option<f32>>,
601 inner_node_size: Size<Option<f32>>,
602 ) -> f32 {
603 self.minimum_contribution_cache.get(axis).unwrap_or_else(|| {
604 let size = self.minimum_contribution(tree, axis, axis_tracks, grid_area_size, inner_node_size);
605 self.minimum_contribution_cache.set(axis, Some(size));
606 size
607 })
608 }
609}