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 available_space_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, available_space_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 inner_node_size: Size<Option<f32>>,
248 grid_area_size: Size<Option<f32>>,
249 ) -> Size<Option<f32>> {
250 let margins = self.margins_axis_sums_with_baseline_shims(inner_node_size.width, tree);
251
252 let aspect_ratio = self.aspect_ratio;
253 let padding = self.padding.resolve_or_zero(grid_area_size, |val, basis| tree.calc(val, basis));
254 let border = self.border.resolve_or_zero(grid_area_size, |val, basis| tree.calc(val, basis));
255 let padding_border_size = (padding + border).sum_axes();
256 let box_sizing_adjustment =
257 if self.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
258 let inherent_size = self
259 .size
260 .maybe_resolve(grid_area_size, |val, basis| tree.calc(val, basis))
261 .maybe_apply_aspect_ratio(aspect_ratio)
262 .maybe_add(box_sizing_adjustment);
263 let min_size = self
264 .min_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 max_size = self
269 .max_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
274 let grid_area_minus_item_margins_size = grid_area_size.maybe_sub(margins);
275
276 let width = inherent_size.width.or_else(|| {
279 if !self.margin.left.is_auto() && !self.margin.right.is_auto() && self.justify_self == AlignSelf::Stretch {
284 return grid_area_minus_item_margins_size.width;
285 }
286
287 None
288 });
289 let Size { width, height } =
291 Size { width, height: inherent_size.height }.maybe_apply_aspect_ratio(aspect_ratio);
292
293 let height = height.or_else(|| {
294 if !self.margin.top.is_auto() && !self.margin.bottom.is_auto() && self.align_self == AlignSelf::Stretch {
299 return grid_area_minus_item_margins_size.height;
300 }
301
302 None
303 });
304 let Size { width, height } = Size { width, height }.maybe_apply_aspect_ratio(aspect_ratio);
306
307 let Size { width, height } = Size { width, height }.maybe_clamp(min_size, max_size);
309
310 Size { width, height }
311 }
312
313 pub fn available_space(
318 &self,
319 axis: AbstractAxis,
320 other_axis_tracks: &[GridTrack],
321 other_axis_available_space: Option<f32>,
322 get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>,
323 ) -> Size<Option<f32>> {
324 let item_other_axis_size: Option<f32> = {
325 other_axis_tracks[self.track_range_excluding_lines(axis.other())]
326 .iter()
327 .map(|track| {
328 get_track_size_estimate(track, other_axis_available_space)
329 .map(|size| size + track.content_alignment_adjustment)
330 })
331 .sum::<Option<f32>>()
332 };
333
334 let mut size = Size::NONE;
335 size.set(axis.other(), item_other_axis_size);
336 size
337 }
338
339 pub fn available_space_cached(
341 &mut self,
342 axis: AbstractAxis,
343 other_axis_tracks: &[GridTrack],
344 other_axis_available_space: Option<f32>,
345 get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>,
346 ) -> Size<Option<f32>> {
347 self.available_space_cache.unwrap_or_else(|| {
348 let available_spaces =
349 self.available_space(axis, other_axis_tracks, other_axis_available_space, get_track_size_estimate);
350 self.available_space_cache = Some(available_spaces);
351 available_spaces
352 })
353 }
354
355 #[inline(always)]
358 pub fn margins_axis_sums_with_baseline_shims(
359 &self,
360 inner_node_width: Option<f32>,
361 tree: &impl LayoutPartialTree,
362 ) -> Size<f32> {
363 Rect {
364 left: self.margin.left.resolve_or_zero(Some(0.0), |val, basis| tree.calc(val, basis)),
365 right: self.margin.right.resolve_or_zero(Some(0.0), |val, basis| tree.calc(val, basis)),
366 top: self.margin.top.resolve_or_zero(inner_node_width, |val, basis| tree.calc(val, basis))
367 + self.baseline_shim,
368 bottom: self.margin.bottom.resolve_or_zero(inner_node_width, |val, basis| tree.calc(val, basis)),
369 }
370 .sum_axes()
371 }
372
373 pub fn min_content_contribution(
375 &self,
376 axis: AbstractAxis,
377 tree: &mut impl LayoutPartialTree,
378 available_space: Size<Option<f32>>,
379 inner_node_size: Size<Option<f32>>,
380 ) -> f32 {
381 let known_dimensions = self.known_dimensions(tree, inner_node_size, available_space);
382 tree.measure_child_size(
383 self.node,
384 known_dimensions,
385 inner_node_size,
386 available_space.map(|opt| match opt {
387 Some(size) => AvailableSpace::Definite(size),
388 None => AvailableSpace::MinContent,
389 }),
390 SizingMode::InherentSize,
391 axis.as_abs_naive(),
392 Line::FALSE,
393 )
394 }
395
396 #[inline(always)]
398 pub fn min_content_contribution_cached(
399 &mut self,
400 axis: AbstractAxis,
401 tree: &mut impl LayoutPartialTree,
402 available_space: Size<Option<f32>>,
403 inner_node_size: Size<Option<f32>>,
404 ) -> f32 {
405 self.min_content_contribution_cache.get(axis).unwrap_or_else(|| {
406 let size = self.min_content_contribution(axis, tree, available_space, inner_node_size);
407 self.min_content_contribution_cache.set(axis, Some(size));
408 size
409 })
410 }
411
412 pub fn max_content_contribution(
414 &self,
415 axis: AbstractAxis,
416 tree: &mut impl LayoutPartialTree,
417 available_space: Size<Option<f32>>,
418 inner_node_size: Size<Option<f32>>,
419 ) -> f32 {
420 let known_dimensions = self.known_dimensions(tree, inner_node_size, available_space);
421 tree.measure_child_size(
422 self.node,
423 known_dimensions,
424 inner_node_size,
425 available_space.map(|opt| match opt {
426 Some(size) => AvailableSpace::Definite(size),
427 None => AvailableSpace::MaxContent,
428 }),
429 SizingMode::InherentSize,
430 axis.as_abs_naive(),
431 Line::FALSE,
432 )
433 }
434
435 #[inline(always)]
437 pub fn max_content_contribution_cached(
438 &mut self,
439 axis: AbstractAxis,
440 tree: &mut impl LayoutPartialTree,
441 available_space: Size<Option<f32>>,
442 inner_node_size: Size<Option<f32>>,
443 ) -> f32 {
444 self.max_content_contribution_cache.get(axis).unwrap_or_else(|| {
445 let size = self.max_content_contribution(axis, tree, available_space, inner_node_size);
446 self.max_content_contribution_cache.set(axis, Some(size));
447 size
448 })
449 }
450
451 pub fn minimum_contribution(
460 &mut self,
461 tree: &mut impl LayoutPartialTree,
462 axis: AbstractAxis,
463 axis_tracks: &[GridTrack],
464 known_dimensions: Size<Option<f32>>,
465 inner_node_size: Size<Option<f32>>,
466 ) -> f32 {
467 let padding = self.padding.resolve_or_zero(inner_node_size, |val, basis| tree.calc(val, basis));
468 let border = self.border.resolve_or_zero(inner_node_size, |val, basis| tree.calc(val, basis));
469 let padding_border_size = (padding + border).sum_axes();
470 let box_sizing_adjustment =
471 if self.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
472 let size = self
473 .size
474 .maybe_resolve(inner_node_size, |val, basis| tree.calc(val, basis))
475 .maybe_apply_aspect_ratio(self.aspect_ratio)
476 .maybe_add(box_sizing_adjustment)
477 .get(axis)
478 .or_else(|| {
479 self.min_size
480 .maybe_resolve(inner_node_size, |val, basis| tree.calc(val, basis))
481 .maybe_apply_aspect_ratio(self.aspect_ratio)
482 .maybe_add(box_sizing_adjustment)
483 .get(axis)
484 })
485 .or_else(|| self.overflow.get(axis).maybe_into_automatic_min_size())
486 .unwrap_or_else(|| {
487 let item_axis_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
492
493 let spans_auto_min_track = axis_tracks
498 .iter()
499 .any(|track| track.min_track_sizing_function.is_auto());
501
502 let only_span_one_track = item_axis_tracks.len() == 1;
504 let spans_a_flexible_track = axis_tracks.iter().any(|track| track.max_track_sizing_function.is_fr());
505
506 let use_content_based_minimum =
507 spans_auto_min_track && (only_span_one_track || !spans_a_flexible_track);
508
509 if use_content_based_minimum {
511 let mut minimum_contribution =
512 self.min_content_contribution_cached(axis, tree, known_dimensions, inner_node_size);
513
514 if self.is_compressible_replaced {
518 let size = self.size.get(axis).maybe_resolve(Some(0.0), |val, basis| tree.calc(val, basis));
519 let max_size =
520 self.max_size.get(axis).maybe_resolve(Some(0.0), |val, basis| tree.calc(val, basis));
521 minimum_contribution = minimum_contribution.maybe_min(size).maybe_min(max_size);
522 }
523
524 minimum_contribution
525 } else {
526 0.0
527 }
528 });
529
530 let limit = self.spanned_fixed_track_limit(axis, axis_tracks, inner_node_size.get(axis), &|val, basis| {
534 tree.resolve_calc_value(val, basis)
535 });
536 size.maybe_min(limit)
537 }
538
539 #[inline(always)]
541 pub fn minimum_contribution_cached(
542 &mut self,
543 tree: &mut impl LayoutPartialTree,
544 axis: AbstractAxis,
545 axis_tracks: &[GridTrack],
546 known_dimensions: Size<Option<f32>>,
547 inner_node_size: Size<Option<f32>>,
548 ) -> f32 {
549 self.minimum_contribution_cache.get(axis).unwrap_or_else(|| {
550 let size = self.minimum_contribution(tree, axis, axis_tracks, known_dimensions, inner_node_size);
551 self.minimum_contribution_cache.set(axis, Some(size));
552 size
553 })
554 }
555}