Grid: selectively rerun track sizing (#341)
* Add crosses_intrinsic_{row,column} properties to GridItem
* Only process items that cross an intrinsic track when resolving intrinsic track sizes
* Only re-run track sizing if min content contributions have changed
* Integrate rerunning track sizing optimisation with caching
* Fix known_dimensions caching
* Also re-run sizing in the case of an indefinitely sized container containing percentage sized tracks
* Add TODOs
* Fix clippy lints
08c16a7c36ce61a1f6b10a792710e39701a2dffc · by Nico Burns · 2023-01-29T17:49:45Z (3 years ago) · parent 6ce28e153 · browse the tree at this commit
Semantic delta · 25
src/compute/grid/track_sizing.rs
src/compute/grid/types/grid_item.rs
src/compute/grid/types/grid_track.rs
diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs
index 1fc504081..3b114e6cc 100644
--- a/src/compute/grid/mod.rs
+++ b/src/compute/grid/mod.rs
@@ -14,7 +14,9 @@ use alignment::{align_and_position_item, align_tracks};
use explicit_grid::{compute_explicit_grid_size_in_axis, initialize_grid_tracks};
use implicit_grid::compute_grid_size_estimate;
use placement::place_grid_items;
-use track_sizing::{determine_if_item_crosses_flexible_tracks, resolve_item_track_indexes, track_sizing_algorithm};
+use track_sizing::{
+ determine_if_item_crosses_flexible_or_intrinsic_tracks, resolve_item_track_indexes, track_sizing_algorithm,
+};
use types::{CellOccupancyMatrix, GridTrack};
pub(crate) use types::{GridCoordinate, GridLine, OriginZeroLine};
@@ -175,7 +177,7 @@ pub fn compute(
// For each item, and in each axis, determine whether the item crosses any flexible (fr) tracks
// Record this as a boolean (per-axis) on each item for later use in the track-sizing algorithm
- determine_if_item_crosses_flexible_tracks(&mut items, &columns, &rows);
+ determine_if_item_crosses_flexible_or_intrinsic_tracks(&mut items, &columns, &rows);
// Run track sizing algorithm for Inline axis
track_sizing_algorithm(
@@ -194,6 +196,8 @@ pub fn compute(
let initial_column_sum = columns.iter().map(|track| track.base_size).sum::<f32>();
inner_node_size.width = inner_node_size.width.or_else(|| initial_column_sum.into());
+ items.iter_mut().for_each(|item| item.known_dimensions_cache = None);
+
// Run track sizing algorithm for Block axis
track_sizing_algorithm(
tree,
@@ -231,35 +235,113 @@ pub fn compute(
return container_border_box.into();
}
- // Re-run track sizing algorithm for Inline axis
- track_sizing_algorithm(
- tree,
- AbstractAxis::Inline,
- min_size.get(AbstractAxis::Inline),
- max_size.get(AbstractAxis::Inline),
- style.grid_align_content(AbstractAxis::Block),
- available_grid_space,
- inner_node_size,
- &mut columns,
- &mut rows,
- &mut items,
- |track: &GridTrack, _| Some(track.base_size),
- );
+ // Column sizing must be re-run (once) if:
+ // - The grid container's width was initially indefinite and there are any columns with percentage track sizing functions
+ // - Any grid item crossing an intrinsically sized track's min content contribution width has changed
+ // TODO: Only rerun sizing for tracks that actually require it rather than for all tracks if any need it.
+ let mut rerun_column_sizing;
+
+ let has_percentage_column = columns.iter().any(|track| track.uses_percentage());
+ let parent_width_indefinite = !available_space.width.is_definite();
+ rerun_column_sizing = parent_width_indefinite && has_percentage_column;
+
+ if !rerun_column_sizing {
+ let min_content_contribution_changed = items
+ .iter_mut()
+ .filter(|item| item.crosses_intrinsic_column)
+ .map(|item| {
+ let known_dimensions = item.known_dimensions(
+ AbstractAxis::Inline,
+ &rows,
+ inner_node_size.height,
+ |track: &GridTrack, _| Some(track.base_size),
+ );
+ let new_min_content_contribution =
+ item.min_content_contribution(AbstractAxis::Inline, tree, known_dimensions, inner_node_size);
+
+ let has_changed = Some(new_min_content_contribution) != item.min_content_contribution_cache.width;
+
+ item.known_dimensions_cache = Some(known_dimensions);
+ item.min_content_contribution_cache.width = Some(new_min_content_contribution);
+ item.max_content_contribution_cache.width = None;
+ item.minimum_contribution_cache.width = None;
+
+ has_changed
+ })
+ .any(|has_changed| has_changed);
+ rerun_column_sizing = min_content_contribution_changed;
+ }
- // Re-run track sizing algorithm for Block axis
- track_sizing_algorithm(
- tree,
- AbstractAxis::Block,
- min_size.get(AbstractAxis::Block),
- max_size.get(AbstractAxis::Block),
- style.grid_align_content(AbstractAxis::Inline),
- available_grid_space,
- inner_node_size,
- &mut rows,
- &mut columns,
- &mut items,
- |track: &GridTrack, _| Some(track.base_size),
- );
+ if rerun_column_sizing {
+ // Re-run track sizing algorithm for Inline axis
+ track_sizing_algorithm(
+ tree,
+ AbstractAxis::Inline,
+ min_size.get(AbstractAxis::Inline),
+ max_size.get(AbstractAxis::Inline),
+ style.grid_align_content(AbstractAxis::Block),
+ available_grid_space,
+ inner_node_size,
+ &mut columns,
+ &mut rows,
+ &mut items,
+ |track: &GridTrack, _| Some(track.base_size),
+ );
+
+ // Row sizing must be re-run (once) if:
+ // - The grid container's height was initially indefinite and there are any rows with percentage track sizing functions
+ // - Any grid item crossing an intrinsically sized track's min content contribution height has changed
+ // TODO: Only rerun sizing for tracks that actually require it rather than for all tracks if any need it.
+ let mut rerun_row_sizing;
+
+ let has_percentage_row = rows.iter().any(|track| track.uses_percentage());
+ let parent_height_indefinite = !available_space.height.is_definite();
+ rerun_row_sizing = parent_height_indefinite && has_percentage_row;
+
+ if !rerun_row_sizing {
+ let min_content_contribution_changed = items
+ .iter_mut()
+ .filter(|item| item.crosses_intrinsic_column)
+ .map(|item| {
+ let known_dimensions = item.known_dimensions(
+ AbstractAxis::Block,
+ &rows,
+ inner_node_size.width,
+ |track: &GridTrack, _| Some(track.base_size),
+ );
+ let new_min_content_contribution =
+ item.min_content_contribution(AbstractAxis::Block, tree, known_dimensions, inner_node_size);
+
+ let has_changed = Some(new_min_content_contribution) != item.min_content_contribution_cache.height;
+
+ item.known_dimensions_cache = Some(known_dimensions);
+ item.min_content_contribution_cache.height = Some(new_min_content_contribution);
+ item.max_content_contribution_cache.height = None;
+ item.minimum_contribution_cache.height = None;
+
+ has_changed
+ })
+ .any(|has_changed| has_changed);
+ rerun_row_sizing = min_content_contribution_changed;
+ }
+
+ if rerun_row_sizing {
+ // Re-run track sizing algorithm for Block axis
+ track_sizing_algorithm(
+ tree,
+ AbstractAxis::Block,
+ min_size.get(AbstractAxis::Block),
+ max_size.get(AbstractAxis::Block),
+ style.grid_align_content(AbstractAxis::Inline),
+ available_grid_space,
+ inner_node_size,
+ &mut rows,
+ &mut columns,
+ &mut items,
+ |track: &GridTrack, _| Some(track.base_size),
+ );
+ }
+ }
// 7. Resolve percentage track base sizes
// In the case of an indefinitely sized container these resolve to zero during the "Initialise Tracks" step
diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs
index fc6943ffc..134d50f0b 100644
--- a/src/compute/grid/track_sizing.rs
+++ b/src/compute/grid/track_sizing.rs
@@ -159,7 +159,7 @@ pub(super) fn resolve_item_track_indexes(items: &mut [GridItem], column_counts:
/// Determine (in each axis) whether the item crosses any flexible tracks
#[inline(always)]
-pub(super) fn determine_if_item_crosses_flexible_tracks(
+pub(super) fn determine_if_item_crosses_flexible_or_intrinsic_tracks(
items: &mut Vec<GridItem>,
columns: &[GridTrack],
rows: &[GridTrack],
@@ -167,8 +167,12 @@ pub(super) fn determine_if_item_crosses_flexible_tracks(
for item in items {
item.crosses_flexible_column =
item.track_range_excluding_lines(AbstractAxis::Inline).any(|i| columns[i].is_flexible());
+ item.crosses_intrinsic_column =
+ item.track_range_excluding_lines(AbstractAxis::Inline).any(|i| columns[i].has_intrinsic_sizing_function());
item.crosses_flexible_row =
item.track_range_excluding_lines(AbstractAxis::Block).any(|i| rows[i].is_flexible());
+ item.crosses_intrinsic_row =
+ item.track_range_excluding_lines(AbstractAxis::Block).any(|i| rows[i].has_intrinsic_sizing_function());
}
}
@@ -199,10 +203,6 @@ pub(super) fn track_sizing_algorithm<Tree: LayoutTree>(
return;
}
- // Clear caches
- // These caches are only valid for a single run of the track sizing algorithm, so ensure that they are clear.
- items.iter_mut().for_each(|item| item.clear_contribution_caches());
-
// Pre-computations for 11.5 Resolve Intrinsic Track Sizes
// The track sizing algorithm requires us to iterate through the items in ascendeding order of the number of
@@ -430,8 +430,9 @@ fn resolve_intrinsic_track_sizes(
fn min_content_contribution(&mut self, item: &mut GridItem) -> f32 {
let known_dimensions = self.known_dimensions(item);
let margin_axis_sums = self.margins_axis_sums(item);
- let contribution = item.min_content_contribution_cached(self.tree, known_dimensions, self.inner_node_size);
- contribution.get(self.axis) + margin_axis_sums.get(self.axis)
+ let contribution =
+ item.min_content_contribution_cached(self.axis, self.tree, known_dimensions, self.inner_node_size);
+ contribution + margin_axis_sums.get(self.axis)
}
/// Retrieve the item's max content contribution from the cache or compute it using the provided parameters
@@ -439,8 +440,9 @@ fn resolve_intrinsic_track_sizes(
fn max_content_contribution(&mut self, item: &mut GridItem) -> f32 {
let known_dimensions = self.known_dimensions(item);
let margin_axis_sums = self.margins_axis_sums(item);
- let contribution = item.max_content_contribution_cached(self.tree, known_dimensions, self.inner_node_size);
- contribution.get(self.axis) + margin_axis_sums.get(self.axis)
+ let contribution =
+ item.max_content_contribution_cached(self.axis, self.tree, known_dimensions, self.inner_node_size);
+ contribution + margin_axis_sums.get(self.axis)
}
/// The minimum contribution of an item is the smallest outer size it can have.
@@ -474,7 +476,7 @@ fn resolve_intrinsic_track_sizes(
// First increase the base size of tracks with an intrinsic min track sizing function
let has_intrinsic_min_track_sizing_function =
move |track: &GridTrack| track.min_track_sizing_function.definite_value(axis_inner_node_size).is_none();
- for item in batch.iter_mut() {
+ for item in batch.iter_mut().filter(|item| item.crosses_intrinsic_track(axis)) {
// ...by distributing extra space as needed to accommodate these items’ minimum contributions.
// If the grid container is being sized under a min- or max-content constraint, use the items’ limited min-content contributions
// in place of their minimum contributions here.
@@ -911,8 +913,8 @@ fn expand_flexible_tracks(
let tracks = &axis_tracks[item.track_range_excluding_lines(axis)];
// TODO: plumb estimate of other axis size (known_dimensions) in here rather than just passing Size::NONE?
let max_content_contribution =
- item.max_content_contribution_cached(tree, Size::NONE, inner_node_size);
- find_size_of_fr(tracks, max_content_contribution.get(axis))
+ item.max_content_contribution_cached(axis, tree, Size::NONE, inner_node_size);
+ find_size_of_fr(tracks, max_content_contribution)
})
.max_by(|a, b| a.total_cmp(b))
.unwrap_or(0.0),
diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs
index f42bb5ada..82d7a9ab9 100644
--- a/src/compute/grid/types/grid_item.rs
+++ b/src/compute/grid/types/grid_item.rs
@@ -45,16 +45,20 @@ pub(in super::super) struct GridItem {
pub crosses_flexible_row: bool,
/// Whether the item crosses a flexible column
pub crosses_flexible_column: bool,
+ /// Whether the item crosses a intrinsic row
+ pub crosses_intrinsic_row: bool,
+ /// Whether the item crosses a intrinsic column
+ pub crosses_intrinsic_column: bool,
// Caches for intrinsic size computation. These caches are only valid for a single run of the track-sizing algorithm.
/// Cache for the known_dimensions input to intrinsic sizing computation
pub known_dimensions_cache: Option<Size<Option<f32>>>,
/// Cache for the min-content size
- pub min_content_contribution_cache: Option<Size<f32>>,
+ pub min_content_contribution_cache: Size<Option<f32>>,
/// Cache for the minimum contribution
- pub minimum_contribution_cache: Option<f32>,
+ pub minimum_contribution_cache: Size<Option<f32>>,
/// Cache for the max-content size
- pub max_content_contribution_cache: Option<Size<f32>>,
+ pub max_content_contribution_cache: Size<Option<f32>>,
}
impl GridItem {
@@ -76,10 +80,12 @@ impl GridItem {
column_indexes: Line { start: 0, end: 0 }, // Properly initialised later
crosses_flexible_row: false, // Properly initialised later
crosses_flexible_column: false, // Properly initialised later
+ crosses_intrinsic_row: false, // Properly initialised later
+ crosses_intrinsic_column: false, // Properly initialised later
known_dimensions_cache: None,
- min_content_contribution_cache: None,
- max_content_contribution_cache: None,
- minimum_contribution_cache: None,
+ min_content_contribution_cache: Size::NONE,
+ max_content_contribution_cache: Size::NONE,
+ minimum_contribution_cache: Size::NONE,
}
}
@@ -124,37 +130,13 @@ impl GridItem {
}
}
- /// Compute the known_dimensions to be passed to the child sizing functions
- /// These are estimates based on either the max track sizing function or the provisional base size in the opposite
- /// axis to the one currently being sized.
- /// https://www.w3.org/TR/css-grid-1/#algo-overview
- pub fn known_dimensions_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>> {
- self.known_dimensions_cache.unwrap_or_else(|| {
- let item_other_axis_size: Option<f32> = {
- other_axis_tracks[self.track_range_excluding_lines(axis.other())]
- .iter()
- .map(|track| {
- get_track_size_estimate(track, other_axis_available_space)
- .map(|size| size + track.content_alignment_adjustment)
- })
- .sum::<Option<f32>>()
- };
- let known_dimensions = {
- let mut size = Size::NONE;
- size.set(axis.other(), item_other_axis_size);
- size
- };
-
- self.known_dimensions_cache = Some(known_dimensions);
-
- known_dimensions
- })
+ /// Returns the pre-computed value indicating whether the grid item crosses an intrinsic track in
+ /// the specified axis
+ pub fn crosses_intrinsic_track(&self, axis: AbstractAxis) -> bool {
+ match axis {
+ AbstractAxis::Inline => self.crosses_intrinsic_column,
+ AbstractAxis::Block => self.crosses_intrinsic_row,
+ }
}
/// For an item spanning multiple tracks, the upper limit used to calculate its limited min-/max-content contribution is the
@@ -180,44 +162,114 @@ impl GridItem {
}
}
+ /// Compute the known_dimensions to be passed to the child sizing functions
+ /// These are estimates based on either the max track sizing function or the provisional base size in the opposite
+ /// axis to the one currently being sized.
+ /// https://www.w3.org/TR/css-grid-1/#algo-overview
+ pub fn known_dimensions(
+ &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>> {
+ let item_other_axis_size: Option<f32> = {
+ other_axis_tracks[self.track_range_excluding_lines(axis.other())]
+ .iter()
+ .map(|track| {
+ get_track_size_estimate(track, other_axis_available_space)
+ .map(|size| size + track.content_alignment_adjustment)
+ })
+ .sum::<Option<f32>>()
+ };
+
+ let mut size = Size::NONE;
+ size.set(axis.other(), item_other_axis_size);
+ size
+ }
+
+ /// Retrieve the known_dimensions from the cache or compute them using the passed parameters
+ pub fn known_dimensions_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>> {
+ self.known_dimensions_cache.unwrap_or_else(|| {
+ let known_dimensions =
+ self.known_dimensions(axis, other_axis_tracks, other_axis_available_space, get_track_size_estimate);
+ self.known_dimensions_cache = Some(known_dimensions);
+ known_dimensions
+ })
+ }
+
+ /// Compute the item's min content contribution from the provided parameters
+ pub fn min_content_contribution(
+ &self,
+ axis: AbstractAxis,
+ tree: &mut impl LayoutTree,
+ known_dimensions: Size<Option<f32>>,
+ inner_node_size: Size<Option<f32>>,
+ ) -> f32 {
+ GenericAlgorithm::measure_size(
+ tree,
+ self.node,
+ known_dimensions,
+ inner_node_size,
+ Size::MIN_CONTENT,
+ SizingMode::InherentSize,
+ )
+ .get(axis)
+ }
+
/// Retrieve the item's min content contribution from the cache or compute it using the provided parameters
+ #[inline(always)]
pub fn min_content_contribution_cached(
&mut self,
+ axis: AbstractAxis,
tree: &mut impl LayoutTree,
known_dimensions: Size<Option<f32>>,
inner_node_size: Size<Option<f32>>,
- ) -> Size<f32> {
- self.min_content_contribution_cache.unwrap_or_else(|| {
- let size = GenericAlgorithm::measure_size(
- tree,
- self.node,
- known_dimensions,
- inner_node_size,
- Size::MIN_CONTENT,
- SizingMode::InherentSize,
- );
- self.min_content_contribution_cache = Some(size);
+ ) -> f32 {
+ self.min_content_contribution_cache.get(axis).unwrap_or_else(|| {
+ let size = self.min_content_contribution(axis, tree, known_dimensions, inner_node_size);
+ self.min_content_contribution_cache.set(axis, Some(size));
size
})
}
+ /// Compute the item's max content contribution from the provided parameters
+ pub fn max_content_contribution(
+ &self,
+ axis: AbstractAxis,
+ tree: &mut impl LayoutTree,
+ known_dimensions: Size<Option<f32>>,
+ inner_node_size: Size<Option<f32>>,
+ ) -> f32 {
+ GenericAlgorithm::measure_size(
+ tree,
+ self.node,
+ known_dimensions,
+ inner_node_size,
+ Size::MAX_CONTENT,
+ SizingMode::InherentSize,
+ )
+ .get(axis)
+ }
+
/// Retrieve the item's max content contribution from the cache or compute it using the provided parameters
+ #[inline(always)]
pub fn max_content_contribution_cached(
&mut self,
+ axis: AbstractAxis,
tree: &mut impl LayoutTree,
known_dimensions: Size<Option<f32>>,
inner_node_size: Size<Option<f32>>,
- ) -> Size<f32> {
- self.max_content_contribution_cache.unwrap_or_else(|| {
- let size = GenericAlgorithm::measure_size(
- tree,
- self.node,
- known_dimensions,
- inner_node_size,
- Size::MAX_CONTENT,
- SizingMode::InherentSize,
- );
- self.max_content_contribution_cache = Some(size);
+ ) -> f32 {
+ self.max_content_contribution_cache.get(axis).unwrap_or_else(|| {
+ let size = self.max_content_contribution(axis, tree, known_dimensions, inner_node_size);
+ self.max_content_contribution_cache.set(axis, Some(size));
size
})
}
@@ -228,7 +280,7 @@ impl GridItem {
/// Its minimum contribution is the outer size that would result from assuming the item’s used minimum size as its preferred size;
/// - Else the item’s minimum contribution is its min-content contribution.
/// Because the minimum contribution often depends on the size of the item’s content, it is considered a type of intrinsic size contribution.
- pub fn minimum_contribution_cached(
+ pub fn minimum_contribution(
&mut self,
tree: &mut impl LayoutTree,
axis: AbstractAxis,
@@ -236,58 +288,63 @@ impl GridItem {
known_dimensions: Size<Option<f32>>,
inner_node_size: Size<Option<f32>>,
) -> f32 {
- self.minimum_contribution_cache.unwrap_or_else(|| {
- let style = tree.style(self.node);
- let contribution = style
- .size
- .maybe_resolve(inner_node_size)
- .maybe_apply_aspect_ratio(style.aspect_ratio)
- .get(axis)
- .or_else(|| {
- style.min_size.maybe_resolve(inner_node_size).maybe_apply_aspect_ratio(style.aspect_ratio).get(axis)
- })
- .unwrap_or_else(|| {
- // Automatic minimum size. See https://www.w3.org/TR/css-grid-1/#min-size-auto
+ let style = tree.style(self.node);
+ style
+ .size
+ .maybe_resolve(inner_node_size)
+ .maybe_apply_aspect_ratio(style.aspect_ratio)
+ .get(axis)
+ .or_else(|| {
+ style.min_size.maybe_resolve(inner_node_size).maybe_apply_aspect_ratio(style.aspect_ratio).get(axis)
+ })
+ .unwrap_or_else(|| {
+ // Automatic minimum size. See https://www.w3.org/TR/css-grid-1/#min-size-auto
- // To provide a more reasonable default minimum size for grid items, the used value of its automatic minimum size
- // in a given axis is the content-based minimum size if all of the following are true:
- let item_axis_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
+ // To provide a more reasonable default minimum size for grid items, the used value of its automatic minimum size
+ // in a given axis is the content-based minimum size if all of the following are true:
+ let item_axis_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
- // it is not a scroll container
- // TODO: support overflow propety
+ // it is not a scroll container
+ // TODO: support overflow propety
- // it spans at least one track in that axis whose min track sizing function is auto
- let spans_auto_min_track = axis_tracks
- .iter()
- // TODO: should this be 'behaves as auto' rather than just literal auto?
- .any(|track| track.min_track_sizing_function == MinTrackSizingFunction::Auto);
+ // it spans at least one track in that axis whose min track sizing function is auto
+ let spans_auto_min_track = axis_tracks
+ .iter()
+ // TODO: should this be 'behaves as auto' rather than just literal auto?
+ .any(|track| track.min_track_sizing_function == MinTrackSizingFunction::Auto);
- // if it spans more than one track in that axis, none of those tracks are flexible
- let only_span_one_track = item_axis_tracks.len() == 1;
- let spans_a_flexible_track = axis_tracks
- .iter()
- .any(|track| matches!(track.max_track_sizing_function, MaxTrackSizingFunction::Flex(_)));
+ // if it spans more than one track in that axis, none of those tracks are flexible
+ let only_span_one_track = item_axis_tracks.len() == 1;
+ let spans_a_flexible_track = axis_tracks
+ .iter()
+ .any(|track| matches!(track.max_track_sizing_function, MaxTrackSizingFunction::Flex(_)));
- let use_content_based_minimum =
- spans_auto_min_track && (only_span_one_track || !spans_a_flexible_track);
+ let use_content_based_minimum =
+ spans_auto_min_track && (only_span_one_track || !spans_a_flexible_track);
- // Otherwise, the automatic minimum size is zero, as usual.
- if use_content_based_minimum {
- self.min_content_contribution_cached(tree, known_dimensions, inner_node_size).get(axis)
- } else {
- 0.0
- }
- });
- self.minimum_contribution_cache = Some(contribution);
- contribution
- })
+ // Otherwise, the automatic minimum size is zero, as usual.
+ if use_content_based_minimum {
+ self.min_content_contribution_cached(axis, tree, known_dimensions, inner_node_size)
+ } else {
+ 0.0
+ }
+ })
}
- /// Clears the per-track-sizing-alogrithm-run caches
- pub fn clear_contribution_caches(&mut self) {
- self.known_dimensions_cache = None;
- self.min_content_contribution_cache = None;
- self.max_content_contribution_cache = None;
- self.minimum_contribution_cache = None;
+ /// Retrieve the item's minimum contribution from the cache or compute it using the provided parameters
+ #[inline(always)]
+ pub fn minimum_contribution_cached(
+ &mut self,
+ tree: &mut impl LayoutTree,
+ axis: AbstractAxis,
+ axis_tracks: &[GridTrack],
+ known_dimensions: Size<Option<f32>>,
+ inner_node_size: Size<Option<f32>>,
+ ) -> f32 {
+ self.minimum_contribution_cache.get(axis).unwrap_or_else(|| {
+ let size = self.minimum_contribution(tree, axis, axis_tracks, known_dimensions, inner_node_size);
+ self.minimum_contribution_cache.set(axis, Some(size));
+ size
+ })
}
}
diff --git a/src/compute/grid/types/grid_track.rs b/src/compute/grid/types/grid_track.rs
index 2c57b8e65..99e4e4da5 100644
--- a/src/compute/grid/types/grid_track.rs
+++ b/src/compute/grid/types/grid_track.rs
@@ -105,12 +105,24 @@ impl GridTrack {
self.max_track_sizing_function = MaxTrackSizingFunction::Fixed(LengthPercentage::Points(0.0));
}
- #[inline]
+ #[inline(always)]
/// Returns true if the track is flexible (has a Flex MaxTrackSizingFunction), else false.
pub fn is_flexible(&self) -> bool {
matches!(self.max_track_sizing_function, MaxTrackSizingFunction::Flex(_))
}
+ #[inline(always)]
+ /// Returns true if the track is flexible (has a Flex MaxTrackSizingFunction), else false.
+ pub fn uses_percentage(&self) -> bool {
+ self.min_track_sizing_function.uses_percentage() || self.max_track_sizing_function.uses_percentage()
+ }
+
+ #[inline(always)]
+ /// Returns true if the track has an intrinsic min and or max sizing function
+ pub fn has_intrinsic_sizing_function(&self) -> bool {
+ self.min_track_sizing_function.is_intrinsic() || self.max_track_sizing_function.is_intrinsic()
+ }
+
#[inline]
/// Returns true if the track is flexible (has a Flex MaxTrackSizingFunction), else false.
pub fn fit_content_limit(&self, axis_available_grid_space: Option<f32>) -> f32 {
diff --git a/src/style/grid.rs b/src/style/grid.rs
index fa6364698..388a9290d 100644
--- a/src/style/grid.rs
+++ b/src/style/grid.rs
@@ -287,7 +287,7 @@ impl FromFlex for MaxTrackSizingFunction {
}
impl MaxTrackSizingFunction {
- /// Returns true if the max track sizing function is `MinContent`, `MaxContent` or `Auto`, else false.
+ /// Returns true if the max track sizing function is `MinContent`, `MaxContent`, `FitContent` or `Auto`, else false.
#[inline(always)]
pub fn is_intrinsic(&self) -> bool {
matches!(self, Self::MinContent | Self::MaxContent | Self::FitContent(_) | Self::Auto)
@@ -346,6 +346,13 @@ impl MaxTrackSizingFunction {
Fixed(LengthPercentage::Points(_)) | MinContent | MaxContent | FitContent(_) | Auto | Flex(_) => None,
}
}
+
+ /// Whether the track sizing functions depends on the size of the parent node
+ #[inline(always)]
+ pub fn uses_percentage(self) -> bool {
+ use MaxTrackSizingFunction::*;
+ matches!(self, Fixed(LengthPercentage::Percent(_)) | FitContent(LengthPercentage::Percent(_)))
+ }
}
/// Minimum track sizing function
@@ -388,6 +395,12 @@ impl FromPercent for MinTrackSizingFunction {
}
impl MinTrackSizingFunction {
+ /// Returns true if the min track sizing function is `MinContent`, `MaxContent` or `Auto`, else false.
+ #[inline(always)]
+ pub fn is_intrinsic(&self) -> bool {
+ matches!(self, Self::MinContent | Self::MaxContent | Self::Auto)
+ }
+
/// Returns fixed point values directly. Attempts to resolve percentage values against
/// the passed available_space and returns if this results in a concrete value (which it
/// will if the available_space is `Some`). Otherwise returns `None`.
@@ -411,6 +424,13 @@ impl MinTrackSizingFunction {
Fixed(LengthPercentage::Points(_)) | MinContent | MaxContent | Auto => None,
}
}
+
+ /// Whether the track sizing functions depends on the size of the parent node
+ #[inline(always)]
+ pub fn uses_percentage(self) -> bool {
+ use MinTrackSizingFunction::*;
+ matches!(self, Fixed(LengthPercentage::Percent(_)))
+ }
}
/// The sizing function for a grid track (row/column) (either auto-track or template track)