1use super::GridTrack;
3use crate::compute::grid::OriginZeroLine;
4use crate::geometry::AbstractAxis;
5use crate::geometry::{Line, Point, Rect, Size};
6use crate::style::{
7 AlignItems, AlignSelf, AvailableSpace, Dimension, LengthPercentageAuto, MaxTrackSizingFunction,
8 MinTrackSizingFunction, Overflow, Style,
9};
10use crate::tree::{NodeId, PartialLayoutTree, PartialLayoutTreeExt, SizingMode};
11use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero};
12use core::ops::Range;
13
14#[derive(Debug)]
16pub(in super::super) struct GridItem {
17 pub node: NodeId,
19
20 pub source_order: u16,
25
26 pub row: Line<OriginZeroLine>,
29 pub column: Line<OriginZeroLine>,
32
33 pub overflow: Point<Overflow>,
35 pub size: Size<Dimension>,
37 pub min_size: Size<Dimension>,
39 pub max_size: Size<Dimension>,
41 pub aspect_ratio: Option<f32>,
43 pub margin: Rect<LengthPercentageAuto>,
45 pub align_self: AlignSelf,
47 pub justify_self: AlignSelf,
49 pub baseline: Option<f32>,
51 pub baseline_shim: f32,
54
55 pub row_indexes: Line<u16>,
58 pub column_indexes: Line<u16>,
61
62 pub crosses_flexible_row: bool,
64 pub crosses_flexible_column: bool,
66 pub crosses_intrinsic_row: bool,
68 pub crosses_intrinsic_column: bool,
70
71 pub available_space_cache: Option<Size<Option<f32>>>,
74 pub min_content_contribution_cache: Size<Option<f32>>,
76 pub minimum_contribution_cache: Size<Option<f32>>,
78 pub max_content_contribution_cache: Size<Option<f32>>,
80
81 pub y_position: f32,
83 pub height: f32,
85}
86
87impl GridItem {
88 pub fn new_with_placement_style_and_order(
90 node: NodeId,
91 col_span: Line<OriginZeroLine>,
92 row_span: Line<OriginZeroLine>,
93 style: &Style,
94 parent_align_items: AlignItems,
95 parent_justify_items: AlignItems,
96 source_order: u16,
97 ) -> Self {
98 GridItem {
99 node,
100 source_order,
101 row: row_span,
102 column: col_span,
103 overflow: style.overflow,
104 size: style.size,
105 min_size: style.min_size,
106 max_size: style.max_size,
107 aspect_ratio: style.aspect_ratio,
108 margin: style.margin,
109 align_self: style.align_self.unwrap_or(parent_align_items),
110 justify_self: style.justify_self.unwrap_or(parent_justify_items),
111 baseline: None,
112 baseline_shim: 0.0,
113 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,
120 min_content_contribution_cache: Size::NONE,
121 max_content_contribution_cache: Size::NONE,
122 minimum_contribution_cache: Size::NONE,
123 y_position: 0.0,
124 height: 0.0,
125 }
126 }
127
128 pub fn placement(&self, axis: AbstractAxis) -> Line<OriginZeroLine> {
130 match axis {
131 AbstractAxis::Block => self.row,
132 AbstractAxis::Inline => self.column,
133 }
134 }
135
136 pub fn placement_indexes(&self, axis: AbstractAxis) -> Line<u16> {
138 match axis {
139 AbstractAxis::Block => self.row_indexes,
140 AbstractAxis::Inline => self.column_indexes,
141 }
142 }
143
144 pub fn track_range_excluding_lines(&self, axis: AbstractAxis) -> Range<usize> {
148 let indexes = self.placement_indexes(axis);
149 (indexes.start as usize + 1)..(indexes.end as usize)
150 }
151
152 pub fn span(&self, axis: AbstractAxis) -> u16 {
154 match axis {
155 AbstractAxis::Block => self.row.span(),
156 AbstractAxis::Inline => self.column.span(),
157 }
158 }
159
160 pub fn crosses_flexible_track(&self, axis: AbstractAxis) -> bool {
163 match axis {
164 AbstractAxis::Inline => self.crosses_flexible_column,
165 AbstractAxis::Block => self.crosses_flexible_row,
166 }
167 }
168
169 pub fn crosses_intrinsic_track(&self, axis: AbstractAxis) -> bool {
172 match axis {
173 AbstractAxis::Inline => self.crosses_intrinsic_column,
174 AbstractAxis::Block => self.crosses_intrinsic_row,
175 }
176 }
177
178 pub fn spanned_track_limit(
181 &mut self,
182 axis: AbstractAxis,
183 axis_tracks: &[GridTrack],
184 axis_parent_size: Option<f32>,
185 ) -> Option<f32> {
186 let spanned_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
187 let tracks_all_fixed = spanned_tracks
188 .iter()
189 .all(|track| track.max_track_sizing_function.definite_limit(axis_parent_size).is_some());
190 if tracks_all_fixed {
191 let limit: f32 = spanned_tracks
192 .iter()
193 .map(|track| track.max_track_sizing_function.definite_limit(axis_parent_size).unwrap())
194 .sum();
195 Some(limit)
196 } else {
197 None
198 }
199 }
200
201 pub fn spanned_fixed_track_limit(
204 &mut self,
205 axis: AbstractAxis,
206 axis_tracks: &[GridTrack],
207 axis_parent_size: Option<f32>,
208 ) -> Option<f32> {
209 let spanned_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
210 let tracks_all_fixed = spanned_tracks
211 .iter()
212 .all(|track| track.max_track_sizing_function.definite_value(axis_parent_size).is_some());
213 if tracks_all_fixed {
214 let limit: f32 = spanned_tracks
215 .iter()
216 .map(|track| track.max_track_sizing_function.definite_value(axis_parent_size).unwrap())
217 .sum();
218 Some(limit)
219 } else {
220 None
221 }
222 }
223
224 fn known_dimensions(
228 &self,
229 inner_node_size: Size<Option<f32>>,
230 grid_area_size: Size<Option<f32>>,
231 ) -> Size<Option<f32>> {
232 let margins = self.margins_axis_sums_with_baseline_shims(inner_node_size.width);
233
234 let aspect_ratio = self.aspect_ratio;
235 let inherent_size = self.size.maybe_resolve(grid_area_size).maybe_apply_aspect_ratio(aspect_ratio);
236 let min_size = self.min_size.maybe_resolve(grid_area_size).maybe_apply_aspect_ratio(aspect_ratio);
237 let max_size = self.max_size.maybe_resolve(grid_area_size).maybe_apply_aspect_ratio(aspect_ratio);
238
239 let grid_area_minus_item_margins_size = grid_area_size.maybe_sub(margins);
240
241 let width = inherent_size.width.or_else(|| {
244 if self.margin.left != LengthPercentageAuto::Auto
249 && self.margin.right != LengthPercentageAuto::Auto
250 && self.justify_self == AlignSelf::Stretch
251 {
252 return grid_area_minus_item_margins_size.width;
253 }
254
255 None
256 });
257 let Size { width, height } =
259 Size { width, height: inherent_size.height }.maybe_apply_aspect_ratio(aspect_ratio);
260
261 let height = height.or_else(|| {
262 if self.margin.top != LengthPercentageAuto::Auto
267 && self.margin.bottom != LengthPercentageAuto::Auto
268 && self.align_self == AlignSelf::Stretch
269 {
270 return grid_area_minus_item_margins_size.height;
271 }
272
273 None
274 });
275 let Size { width, height } = Size { width, height }.maybe_apply_aspect_ratio(aspect_ratio);
277
278 let Size { width, height } = Size { width, height }.maybe_clamp(min_size, max_size);
280
281 Size { width, height }
282 }
283
284 pub fn available_space(
289 &self,
290 axis: AbstractAxis,
291 other_axis_tracks: &[GridTrack],
292 other_axis_available_space: Option<f32>,
293 get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>,
294 ) -> Size<Option<f32>> {
295 let item_other_axis_size: Option<f32> = {
296 other_axis_tracks[self.track_range_excluding_lines(axis.other())]
297 .iter()
298 .map(|track| {
299 get_track_size_estimate(track, other_axis_available_space)
300 .map(|size| size + track.content_alignment_adjustment)
301 })
302 .sum::<Option<f32>>()
303 };
304
305 let mut size = Size::NONE;
306 size.set(axis.other(), item_other_axis_size);
307 size
308 }
309
310 pub fn available_space_cached(
312 &mut self,
313 axis: AbstractAxis,
314 other_axis_tracks: &[GridTrack],
315 other_axis_available_space: Option<f32>,
316 get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>,
317 ) -> Size<Option<f32>> {
318 self.available_space_cache.unwrap_or_else(|| {
319 let available_spaces =
320 self.available_space(axis, other_axis_tracks, other_axis_available_space, get_track_size_estimate);
321 self.available_space_cache = Some(available_spaces);
322 available_spaces
323 })
324 }
325
326 #[inline(always)]
329 pub fn margins_axis_sums_with_baseline_shims(&self, inner_node_width: Option<f32>) -> Size<f32> {
330 Rect {
331 left: self.margin.left.resolve_or_zero(Some(0.0)),
332 right: self.margin.right.resolve_or_zero(Some(0.0)),
333 top: self.margin.top.resolve_or_zero(inner_node_width) + self.baseline_shim,
334 bottom: self.margin.bottom.resolve_or_zero(inner_node_width),
335 }
336 .sum_axes()
337 }
338
339 pub fn min_content_contribution(
341 &self,
342 axis: AbstractAxis,
343 tree: &mut impl PartialLayoutTree,
344 available_space: Size<Option<f32>>,
345 inner_node_size: Size<Option<f32>>,
346 ) -> f32 {
347 let known_dimensions = self.known_dimensions(inner_node_size, available_space);
348 tree.measure_child_size(
349 self.node,
350 known_dimensions,
351 available_space,
352 available_space.map(|opt| match opt {
353 Some(size) => AvailableSpace::Definite(size),
354 None => AvailableSpace::MinContent,
355 }),
356 SizingMode::InherentSize,
357 axis.as_abs_naive(),
358 Line::FALSE,
359 )
360 }
361
362 #[inline(always)]
364 pub fn min_content_contribution_cached(
365 &mut self,
366 axis: AbstractAxis,
367 tree: &mut impl PartialLayoutTree,
368 available_space: Size<Option<f32>>,
369 inner_node_size: Size<Option<f32>>,
370 ) -> f32 {
371 self.min_content_contribution_cache.get(axis).unwrap_or_else(|| {
372 let size = self.min_content_contribution(axis, tree, available_space, inner_node_size);
373 self.min_content_contribution_cache.set(axis, Some(size));
374 size
375 })
376 }
377
378 pub fn max_content_contribution(
380 &self,
381 axis: AbstractAxis,
382 tree: &mut impl PartialLayoutTree,
383 available_space: Size<Option<f32>>,
384 inner_node_size: Size<Option<f32>>,
385 ) -> f32 {
386 let known_dimensions = self.known_dimensions(inner_node_size, available_space);
387 tree.measure_child_size(
388 self.node,
389 known_dimensions,
390 available_space,
391 available_space.map(|opt| match opt {
392 Some(size) => AvailableSpace::Definite(size),
393 None => AvailableSpace::MaxContent,
394 }),
395 SizingMode::InherentSize,
396 axis.as_abs_naive(),
397 Line::FALSE,
398 )
399 }
400
401 #[inline(always)]
403 pub fn max_content_contribution_cached(
404 &mut self,
405 axis: AbstractAxis,
406 tree: &mut impl PartialLayoutTree,
407 available_space: Size<Option<f32>>,
408 inner_node_size: Size<Option<f32>>,
409 ) -> f32 {
410 self.max_content_contribution_cache.get(axis).unwrap_or_else(|| {
411 let size = self.max_content_contribution(axis, tree, available_space, inner_node_size);
412 self.max_content_contribution_cache.set(axis, Some(size));
413 size
414 })
415 }
416
417 pub fn minimum_contribution(
425 &mut self,
426 tree: &mut impl PartialLayoutTree,
427 axis: AbstractAxis,
428 axis_tracks: &[GridTrack],
429 known_dimensions: Size<Option<f32>>,
430 inner_node_size: Size<Option<f32>>,
431 ) -> f32 {
432 let size = self
433 .size
434 .maybe_resolve(inner_node_size)
435 .maybe_apply_aspect_ratio(self.aspect_ratio)
436 .get(axis)
437 .or_else(|| {
438 self.min_size.maybe_resolve(inner_node_size).maybe_apply_aspect_ratio(self.aspect_ratio).get(axis)
439 })
440 .or_else(|| self.overflow.get(axis).maybe_into_automatic_min_size())
441 .unwrap_or_else(|| {
442 let item_axis_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
447
448 let spans_auto_min_track = axis_tracks
453 .iter()
454 .any(|track| track.min_track_sizing_function == MinTrackSizingFunction::Auto);
456
457 let only_span_one_track = item_axis_tracks.len() == 1;
459 let spans_a_flexible_track = axis_tracks
460 .iter()
461 .any(|track| matches!(track.max_track_sizing_function, MaxTrackSizingFunction::Fraction(_)));
462
463 let use_content_based_minimum =
464 spans_auto_min_track && (only_span_one_track || !spans_a_flexible_track);
465
466 if use_content_based_minimum {
468 self.min_content_contribution_cached(axis, tree, known_dimensions, inner_node_size)
469 } else {
470 0.0
471 }
472 });
473
474 let limit = self.spanned_fixed_track_limit(axis, axis_tracks, inner_node_size.get(axis));
478 size.maybe_min(limit)
479 }
480
481 #[inline(always)]
483 pub fn minimum_contribution_cached(
484 &mut self,
485 tree: &mut impl PartialLayoutTree,
486 axis: AbstractAxis,
487 axis_tracks: &[GridTrack],
488 known_dimensions: Size<Option<f32>>,
489 inner_node_size: Size<Option<f32>>,
490 ) -> f32 {
491 self.minimum_contribution_cache.get(axis).unwrap_or_else(|| {
492 let size = self.minimum_contribution(tree, axis, axis_tracks, known_dimensions, inner_node_size);
493 self.minimum_contribution_cache.set(axis, Some(size));
494 size
495 })
496 }
497}