1 //! Contains GridItem used to represent a single grid item during layout 2 use super::GridTrack; 3 use crate::compute::grid::OriginZeroLine; 4 use crate::geometry::AbstractAxis; 5 use crate::geometry::{Line, Point, Rect, Size}; 6 use crate::style::{ 7 AlignItems, AlignSelf, AvailableSpace, Dimension, LengthPercentageAuto, MaxTrackSizingFunction, 8 MinTrackSizingFunction, Overflow, 9 }; 10 use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId, SizingMode}; 11 use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero}; 12 use crate::{BoxSizing, GridItemStyle, LengthPercentage}; 13 use core::ops::Range; 14 15 /// Represents a single grid item 16 #[derive(Debug)] 17 pub(in super::super) struct GridItem { 18 /// The id of the node that this item represents 19 pub node: NodeId, 20 21 /// The order of the item in the children array 22 /// 23 /// We sort the list of grid items during track sizing. This field allows us to sort back the original order 24 /// for final positioning 25 pub source_order: u16, 26 27 /// The item's definite row-start and row-end, as resolved by the placement algorithm 28 /// (in origin-zero coordinates) 29 pub row: Line<OriginZeroLine>, 30 /// The items definite column-start and column-end, as resolved by the placement algorithm 31 /// (in origin-zero coordinates) 32 pub column: Line<OriginZeroLine>, 33 34 /// The item's overflow style 35 pub overflow: Point<Overflow>, 36 /// The item's box_sizing style 37 pub box_sizing: BoxSizing, 38 /// The item's size style 39 pub size: Size<Dimension>, 40 /// The item's min_size style 41 pub min_size: Size<Dimension>, 42 /// The item's max_size style 43 pub max_size: Size<Dimension>, 44 /// The item's aspect_ratio style 45 pub aspect_ratio: Option<f32>, 46 /// The item's padding style 47 pub padding: Rect<LengthPercentage>, 48 /// The item's border style 49 pub border: Rect<LengthPercentage>, 50 /// The item's margin style 51 pub margin: Rect<LengthPercentageAuto>, 52 /// The item's align_self property, or the parent's align_items property is not set 53 pub align_self: AlignSelf, 54 /// The item's justify_self property, or the parent's justify_items property is not set 55 pub justify_self: AlignSelf, 56 /// The items first baseline (horizontal) 57 pub baseline: Option<f32>, 58 /// Shim for baseline alignment that acts like an extra top margin 59 /// TODO: Support last baseline and vertical text baselines 60 pub baseline_shim: f32, 61 62 /// The item's definite row-start and row-end (same as `row` field, except in a different coordinate system) 63 /// (as indexes into the Vec<GridTrack> stored in a grid's AbstractAxisTracks) 64 pub row_indexes: Line<u16>, 65 /// The items definite column-start and column-end (same as `column` field, except in a different coordinate system) 66 /// (as indexes into the Vec<GridTrack> stored in a grid's AbstractAxisTracks) 67 pub column_indexes: Line<u16>, 68 69 /// Whether the item crosses a flexible row 70 pub crosses_flexible_row: bool, 71 /// Whether the item crosses a flexible column 72 pub crosses_flexible_column: bool, 73 /// Whether the item crosses a intrinsic row 74 pub crosses_intrinsic_row: bool, 75 /// Whether the item crosses a intrinsic column 76 pub crosses_intrinsic_column: bool, 77 78 // Caches for intrinsic size computation. These caches are only valid for a single run of the track-sizing algorithm. 79 /// Cache for the known_dimensions input to intrinsic sizing computation 80 pub available_space_cache: Option<Size<Option<f32>>>, 81 /// Cache for the min-content size 82 pub min_content_contribution_cache: Size<Option<f32>>, 83 /// Cache for the minimum contribution 84 pub minimum_contribution_cache: Size<Option<f32>>, 85 /// Cache for the max-content size 86 pub max_content_contribution_cache: Size<Option<f32>>, 87 88 /// Final y position. Used to compute baseline alignment for the container. 89 pub y_position: f32, 90 /// Final height. Used to compute baseline alignment for the container. 91 pub height: f32, 92 } 93 94 impl GridItem { 95 /// Create a new item given a concrete placement in both axes new_with_placement_style_and_order<S: GridItemStyle>( node: NodeId, col_span: Line<OriginZeroLine>, row_span: Line<OriginZeroLine>, style: S, parent_align_items: AlignItems, parent_justify_items: AlignItems, source_order: u16, ) -> Self96 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 overflow: style.overflow(), 111 box_sizing: style.box_sizing(), 112 size: style.size(), 113 min_size: style.min_size(), 114 max_size: style.max_size(), 115 aspect_ratio: style.aspect_ratio(), 116 padding: style.padding(), 117 border: style.border(), 118 margin: style.margin(), 119 align_self: style.align_self().unwrap_or(parent_align_items), 120 justify_self: style.justify_self().unwrap_or(parent_justify_items), 121 baseline: None, 122 baseline_shim: 0.0, 123 row_indexes: Line { start: 0, end: 0 }, // Properly initialised later 124 column_indexes: Line { start: 0, end: 0 }, // Properly initialised later 125 crosses_flexible_row: false, // Properly initialised later 126 crosses_flexible_column: false, // Properly initialised later 127 crosses_intrinsic_row: false, // Properly initialised later 128 crosses_intrinsic_column: false, // Properly initialised later 129 available_space_cache: None, 130 min_content_contribution_cache: Size::NONE, 131 max_content_contribution_cache: Size::NONE, 132 minimum_contribution_cache: Size::NONE, 133 y_position: 0.0, 134 height: 0.0, 135 } 136 } 137 138 /// This item's placement in the specified axis in OriginZero coordinates placement(&self, axis: AbstractAxis) -> Line<OriginZeroLine>139 pub fn placement(&self, axis: AbstractAxis) -> Line<OriginZeroLine> { 140 match axis { 141 AbstractAxis::Block => self.row, 142 AbstractAxis::Inline => self.column, 143 } 144 } 145 146 /// This item's placement in the specified axis as GridTrackVec indices placement_indexes(&self, axis: AbstractAxis) -> Line<u16>147 pub fn placement_indexes(&self, axis: AbstractAxis) -> Line<u16> { 148 match axis { 149 AbstractAxis::Block => self.row_indexes, 150 AbstractAxis::Inline => self.column_indexes, 151 } 152 } 153 154 /// Returns a range which can be used as an index into the GridTrackVec in the specified axis 155 /// which will produce a sub-slice of covering all the tracks and lines that this item spans 156 /// excluding the lines that bound it. track_range_excluding_lines(&self, axis: AbstractAxis) -> Range<usize>157 pub fn track_range_excluding_lines(&self, axis: AbstractAxis) -> Range<usize> { 158 let indexes = self.placement_indexes(axis); 159 (indexes.start as usize + 1)..(indexes.end as usize) 160 } 161 162 /// Returns the number of tracks that this item spans in the specified axis span(&self, axis: AbstractAxis) -> u16163 pub fn span(&self, axis: AbstractAxis) -> u16 { 164 match axis { 165 AbstractAxis::Block => self.row.span(), 166 AbstractAxis::Inline => self.column.span(), 167 } 168 } 169 170 /// Returns the pre-computed value indicating whether the grid item crosses a flexible track in 171 /// the specified axis crosses_flexible_track(&self, axis: AbstractAxis) -> bool172 pub fn crosses_flexible_track(&self, axis: AbstractAxis) -> bool { 173 match axis { 174 AbstractAxis::Inline => self.crosses_flexible_column, 175 AbstractAxis::Block => self.crosses_flexible_row, 176 } 177 } 178 179 /// Returns the pre-computed value indicating whether the grid item crosses an intrinsic track in 180 /// the specified axis crosses_intrinsic_track(&self, axis: AbstractAxis) -> bool181 pub fn crosses_intrinsic_track(&self, axis: AbstractAxis) -> bool { 182 match axis { 183 AbstractAxis::Inline => self.crosses_intrinsic_column, 184 AbstractAxis::Block => self.crosses_intrinsic_row, 185 } 186 } 187 188 /// For an item spanning multiple tracks, the upper limit used to calculate its limited min-/max-content contribution is the 189 /// sum of the fixed max track sizing functions of any tracks it spans, and is applied if it only spans such tracks. spanned_track_limit( &mut self, axis: AbstractAxis, axis_tracks: &[GridTrack], axis_parent_size: Option<f32>, ) -> Option<f32>190 pub fn spanned_track_limit( 191 &mut self, 192 axis: AbstractAxis, 193 axis_tracks: &[GridTrack], 194 axis_parent_size: Option<f32>, 195 ) -> Option<f32> { 196 let spanned_tracks = &axis_tracks[self.track_range_excluding_lines(axis)]; 197 let tracks_all_fixed = spanned_tracks 198 .iter() 199 .all(|track| track.max_track_sizing_function.definite_limit(axis_parent_size).is_some()); 200 if tracks_all_fixed { 201 let limit: f32 = spanned_tracks 202 .iter() 203 .map(|track| track.max_track_sizing_function.definite_limit(axis_parent_size).unwrap()) 204 .sum(); 205 Some(limit) 206 } else { 207 None 208 } 209 } 210 211 /// Similar to the spanned_track_limit, but excludes FitContent arguments from the limit. 212 /// Used to clamp the automatic minimum contributions of an item spanned_fixed_track_limit( &mut self, axis: AbstractAxis, axis_tracks: &[GridTrack], axis_parent_size: Option<f32>, ) -> Option<f32>213 pub fn spanned_fixed_track_limit( 214 &mut self, 215 axis: AbstractAxis, 216 axis_tracks: &[GridTrack], 217 axis_parent_size: Option<f32>, 218 ) -> Option<f32> { 219 let spanned_tracks = &axis_tracks[self.track_range_excluding_lines(axis)]; 220 let tracks_all_fixed = spanned_tracks 221 .iter() 222 .all(|track| track.max_track_sizing_function.definite_value(axis_parent_size).is_some()); 223 if tracks_all_fixed { 224 let limit: f32 = spanned_tracks 225 .iter() 226 .map(|track| track.max_track_sizing_function.definite_value(axis_parent_size).unwrap()) 227 .sum(); 228 Some(limit) 229 } else { 230 None 231 } 232 } 233 234 /// Compute the known_dimensions to be passed to the child sizing functions 235 /// The key thing that is being done here is applying stretch alignment, which is necessary to 236 /// allow percentage sizes further down the tree to resolve properly in some cases known_dimensions( &self, inner_node_size: Size<Option<f32>>, grid_area_size: Size<Option<f32>>, ) -> Size<Option<f32>>237 fn known_dimensions( 238 &self, 239 inner_node_size: Size<Option<f32>>, 240 grid_area_size: Size<Option<f32>>, 241 ) -> Size<Option<f32>> { 242 let margins = self.margins_axis_sums_with_baseline_shims(inner_node_size.width); 243 244 let aspect_ratio = self.aspect_ratio; 245 let padding = self.padding.resolve_or_zero(grid_area_size); 246 let border = self.border.resolve_or_zero(grid_area_size); 247 let padding_border_size = (padding + border).sum_axes(); 248 let box_sizing_adjustment = 249 if self.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; 250 let inherent_size = self 251 .size 252 .maybe_resolve(grid_area_size) 253 .maybe_apply_aspect_ratio(aspect_ratio) 254 .maybe_add(box_sizing_adjustment); 255 let min_size = self 256 .min_size 257 .maybe_resolve(grid_area_size) 258 .maybe_apply_aspect_ratio(aspect_ratio) 259 .maybe_add(box_sizing_adjustment); 260 let max_size = self 261 .max_size 262 .maybe_resolve(grid_area_size) 263 .maybe_apply_aspect_ratio(aspect_ratio) 264 .maybe_add(box_sizing_adjustment); 265 266 let grid_area_minus_item_margins_size = grid_area_size.maybe_sub(margins); 267 268 // If node is absolutely positioned and width is not set explicitly, then deduce it 269 // from left, right and container_content_box if both are set. 270 let width = inherent_size.width.or_else(|| { 271 // Apply width based on stretch alignment if: 272 // - Alignment style is "stretch" 273 // - The node is not absolutely positioned 274 // - The node does not have auto margins in this axis. 275 if self.margin.left != LengthPercentageAuto::Auto 276 && self.margin.right != LengthPercentageAuto::Auto 277 && self.justify_self == AlignSelf::Stretch 278 { 279 return grid_area_minus_item_margins_size.width; 280 } 281 282 None 283 }); 284 // Reapply aspect ratio after stretch and absolute position width adjustments 285 let Size { width, height } = 286 Size { width, height: inherent_size.height }.maybe_apply_aspect_ratio(aspect_ratio); 287 288 let height = height.or_else(|| { 289 // Apply height based on stretch alignment if: 290 // - Alignment style is "stretch" 291 // - The node is not absolutely positioned 292 // - The node does not have auto margins in this axis. 293 if self.margin.top != LengthPercentageAuto::Auto 294 && self.margin.bottom != LengthPercentageAuto::Auto 295 && self.align_self == AlignSelf::Stretch 296 { 297 return grid_area_minus_item_margins_size.height; 298 } 299 300 None 301 }); 302 // Reapply aspect ratio after stretch and absolute position height adjustments 303 let Size { width, height } = Size { width, height }.maybe_apply_aspect_ratio(aspect_ratio); 304 305 // Clamp size by min and max width/height 306 let Size { width, height } = Size { width, height }.maybe_clamp(min_size, max_size); 307 308 Size { width, height } 309 } 310 311 /// Compute the available_space to be passed to the child sizing functions 312 /// These are estimates based on either the max track sizing function or the provisional base size in the opposite 313 /// axis to the one currently being sized. 314 /// https://www.w3.org/TR/css-grid-1/#algo-overview available_space( &self, axis: AbstractAxis, other_axis_tracks: &[GridTrack], other_axis_available_space: Option<f32>, get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>, ) -> Size<Option<f32>>315 pub fn available_space( 316 &self, 317 axis: AbstractAxis, 318 other_axis_tracks: &[GridTrack], 319 other_axis_available_space: Option<f32>, 320 get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>, 321 ) -> Size<Option<f32>> { 322 let item_other_axis_size: Option<f32> = { 323 other_axis_tracks[self.track_range_excluding_lines(axis.other())] 324 .iter() 325 .map(|track| { 326 get_track_size_estimate(track, other_axis_available_space) 327 .map(|size| size + track.content_alignment_adjustment) 328 }) 329 .sum::<Option<f32>>() 330 }; 331 332 let mut size = Size::NONE; 333 size.set(axis.other(), item_other_axis_size); 334 size 335 } 336 337 /// Retrieve the available_space from the cache or compute them using the passed parameters available_space_cached( &mut self, axis: AbstractAxis, other_axis_tracks: &[GridTrack], other_axis_available_space: Option<f32>, get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>, ) -> Size<Option<f32>>338 pub fn available_space_cached( 339 &mut self, 340 axis: AbstractAxis, 341 other_axis_tracks: &[GridTrack], 342 other_axis_available_space: Option<f32>, 343 get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>, 344 ) -> Size<Option<f32>> { 345 self.available_space_cache.unwrap_or_else(|| { 346 let available_spaces = 347 self.available_space(axis, other_axis_tracks, other_axis_available_space, get_track_size_estimate); 348 self.available_space_cache = Some(available_spaces); 349 available_spaces 350 }) 351 } 352 353 /// Compute the item's resolved margins for size contributions. Horizontal percentage margins always resolve 354 /// to zero if the container size is indefinite as otherwise this would introduce a cyclic dependency. 355 #[inline(always)] margins_axis_sums_with_baseline_shims(&self, inner_node_width: Option<f32>) -> Size<f32>356 pub fn margins_axis_sums_with_baseline_shims(&self, inner_node_width: Option<f32>) -> Size<f32> { 357 Rect { 358 left: self.margin.left.resolve_or_zero(Some(0.0)), 359 right: self.margin.right.resolve_or_zero(Some(0.0)), 360 top: self.margin.top.resolve_or_zero(inner_node_width) + self.baseline_shim, 361 bottom: self.margin.bottom.resolve_or_zero(inner_node_width), 362 } 363 .sum_axes() 364 } 365 366 /// Compute the item's min content contribution from the provided parameters min_content_contribution( &self, axis: AbstractAxis, tree: &mut impl LayoutPartialTree, available_space: Size<Option<f32>>, inner_node_size: Size<Option<f32>>, ) -> f32367 pub fn min_content_contribution( 368 &self, 369 axis: AbstractAxis, 370 tree: &mut impl LayoutPartialTree, 371 available_space: Size<Option<f32>>, 372 inner_node_size: Size<Option<f32>>, 373 ) -> f32 { 374 let known_dimensions = self.known_dimensions(inner_node_size, available_space); 375 tree.measure_child_size( 376 self.node, 377 known_dimensions, 378 inner_node_size, 379 available_space.map(|opt| match opt { 380 Some(size) => AvailableSpace::Definite(size), 381 None => AvailableSpace::MinContent, 382 }), 383 SizingMode::InherentSize, 384 axis.as_abs_naive(), 385 Line::FALSE, 386 ) 387 } 388 389 /// Retrieve the item's min content contribution from the cache or compute it using the provided parameters 390 #[inline(always)] min_content_contribution_cached( &mut self, axis: AbstractAxis, tree: &mut impl LayoutPartialTree, available_space: Size<Option<f32>>, inner_node_size: Size<Option<f32>>, ) -> f32391 pub fn min_content_contribution_cached( 392 &mut self, 393 axis: AbstractAxis, 394 tree: &mut impl LayoutPartialTree, 395 available_space: Size<Option<f32>>, 396 inner_node_size: Size<Option<f32>>, 397 ) -> f32 { 398 self.min_content_contribution_cache.get(axis).unwrap_or_else(|| { 399 let size = self.min_content_contribution(axis, tree, available_space, inner_node_size); 400 self.min_content_contribution_cache.set(axis, Some(size)); 401 size 402 }) 403 } 404 405 /// Compute the item's max content contribution from the provided parameters max_content_contribution( &self, axis: AbstractAxis, tree: &mut impl LayoutPartialTree, available_space: Size<Option<f32>>, inner_node_size: Size<Option<f32>>, ) -> f32406 pub fn max_content_contribution( 407 &self, 408 axis: AbstractAxis, 409 tree: &mut impl LayoutPartialTree, 410 available_space: Size<Option<f32>>, 411 inner_node_size: Size<Option<f32>>, 412 ) -> f32 { 413 let known_dimensions = self.known_dimensions(inner_node_size, available_space); 414 tree.measure_child_size( 415 self.node, 416 known_dimensions, 417 inner_node_size, 418 available_space.map(|opt| match opt { 419 Some(size) => AvailableSpace::Definite(size), 420 None => AvailableSpace::MaxContent, 421 }), 422 SizingMode::InherentSize, 423 axis.as_abs_naive(), 424 Line::FALSE, 425 ) 426 } 427 428 /// Retrieve the item's max content contribution from the cache or compute it using the provided parameters 429 #[inline(always)] max_content_contribution_cached( &mut self, axis: AbstractAxis, tree: &mut impl LayoutPartialTree, available_space: Size<Option<f32>>, inner_node_size: Size<Option<f32>>, ) -> f32430 pub fn max_content_contribution_cached( 431 &mut self, 432 axis: AbstractAxis, 433 tree: &mut impl LayoutPartialTree, 434 available_space: Size<Option<f32>>, 435 inner_node_size: Size<Option<f32>>, 436 ) -> f32 { 437 self.max_content_contribution_cache.get(axis).unwrap_or_else(|| { 438 let size = self.max_content_contribution(axis, tree, available_space, inner_node_size); 439 self.max_content_contribution_cache.set(axis, Some(size)); 440 size 441 }) 442 } 443 444 /// The minimum contribution of an item is the smallest outer size it can have. 445 /// Specifically: 446 /// - If the item’s computed preferred size behaves as auto or depends on the size of its containing block in the relevant axis: 447 /// Its minimum contribution is the outer size that would result from assuming the item’s used minimum size as its preferred size; 448 /// - Else the item’s minimum contribution is its min-content contribution. 449 /// 450 /// Because the minimum contribution often depends on the size of the item’s content, it is considered a type of intrinsic size contribution. 451 /// See: https://www.w3.org/TR/css-grid-1/#min-size-auto minimum_contribution( &mut self, tree: &mut impl LayoutPartialTree, axis: AbstractAxis, axis_tracks: &[GridTrack], known_dimensions: Size<Option<f32>>, inner_node_size: Size<Option<f32>>, ) -> f32452 pub fn minimum_contribution( 453 &mut self, 454 tree: &mut impl LayoutPartialTree, 455 axis: AbstractAxis, 456 axis_tracks: &[GridTrack], 457 known_dimensions: Size<Option<f32>>, 458 inner_node_size: Size<Option<f32>>, 459 ) -> f32 { 460 let padding = self.padding.resolve_or_zero(inner_node_size); 461 let border = self.border.resolve_or_zero(inner_node_size); 462 let padding_border_size = (padding + border).sum_axes(); 463 let box_sizing_adjustment = 464 if self.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO }; 465 let size = self 466 .size 467 .maybe_resolve(inner_node_size) 468 .maybe_apply_aspect_ratio(self.aspect_ratio) 469 .maybe_add(box_sizing_adjustment) 470 .get(axis) 471 .or_else(|| { 472 self.min_size 473 .maybe_resolve(inner_node_size) 474 .maybe_apply_aspect_ratio(self.aspect_ratio) 475 .maybe_add(box_sizing_adjustment) 476 .get(axis) 477 }) 478 .or_else(|| self.overflow.get(axis).maybe_into_automatic_min_size()) 479 .unwrap_or_else(|| { 480 // Automatic minimum size. See https://www.w3.org/TR/css-grid-1/#min-size-auto 481 482 // To provide a more reasonable default minimum size for grid items, the used value of its automatic minimum size 483 // in a given axis is the content-based minimum size if all of the following are true: 484 let item_axis_tracks = &axis_tracks[self.track_range_excluding_lines(axis)]; 485 486 // it is not a scroll container 487 // TODO: support overflow property 488 489 // it spans at least one track in that axis whose min track sizing function is auto 490 let spans_auto_min_track = axis_tracks 491 .iter() 492 // TODO: should this be 'behaves as auto' rather than just literal auto? 493 .any(|track| track.min_track_sizing_function == MinTrackSizingFunction::Auto); 494 495 // if it spans more than one track in that axis, none of those tracks are flexible 496 let only_span_one_track = item_axis_tracks.len() == 1; 497 let spans_a_flexible_track = axis_tracks 498 .iter() 499 .any(|track| matches!(track.max_track_sizing_function, MaxTrackSizingFunction::Fraction(_))); 500 501 let use_content_based_minimum = 502 spans_auto_min_track && (only_span_one_track || !spans_a_flexible_track); 503 504 // Otherwise, the automatic minimum size is zero, as usual. 505 if use_content_based_minimum { 506 self.min_content_contribution_cached(axis, tree, known_dimensions, inner_node_size) 507 } else { 508 0.0 509 } 510 }); 511 512 // In all cases, the size suggestion is additionally clamped by the maximum size in the affected axis, if it’s definite. 513 // Note: The argument to fit-content() does not clamp the content-based minimum size in the same way as a fixed max track 514 // sizing function. 515 let limit = self.spanned_fixed_track_limit(axis, axis_tracks, inner_node_size.get(axis)); 516 size.maybe_min(limit) 517 } 518 519 /// Retrieve the item's minimum contribution from the cache or compute it using the provided parameters 520 #[inline(always)] minimum_contribution_cached( &mut self, tree: &mut impl LayoutPartialTree, axis: AbstractAxis, axis_tracks: &[GridTrack], known_dimensions: Size<Option<f32>>, inner_node_size: Size<Option<f32>>, ) -> f32521 pub fn minimum_contribution_cached( 522 &mut self, 523 tree: &mut impl LayoutPartialTree, 524 axis: AbstractAxis, 525 axis_tracks: &[GridTrack], 526 known_dimensions: Size<Option<f32>>, 527 inner_node_size: Size<Option<f32>>, 528 ) -> f32 { 529 self.minimum_contribution_cache.get(axis).unwrap_or_else(|| { 530 let size = self.minimum_contribution(tree, axis, axis_tracks, known_dimensions, inner_node_size); 531 self.minimum_contribution_cache.set(axis, Some(size)); 532 size 533 }) 534 } 535 } 536