HTML 5544Rust 88Markdown 11TOML 91926 Definiciones
Optimise CSS Grid (#338)
* Only compute the intrinsic sizes of children that are actually required
* Use inner_node_size rather than available_grid_space for margin and known_dimension computation
* Convert distribute_item_space_to_base_size to use inner_node_size rather than available_grid_space
* Convert compute_alignment_gutter_adjustment to use inner_node_size
* Remove track_sizing_algorithm dependency on available_space
* Convert definite_value methods on track sizing functions to take Option<f32> instead of AvailableSpace
* Scatter inline(always) on grid sizing functions
* Add early return to grid algorithm for RunMode::ComputeSize case
* Use separate cache slots for width and height
* Remove absolutely positioned child from deep grid benchmarks (fixes superdeep benchmarks)
* Add caching tests for css grid
* Cache minimum_contribution correctly
diff --git a/benches/big_tree_grid.rs b/benches/big_tree_grid.rs
index 23c4f3e0e..1077dd934 100644
--- a/benches/big_tree_grid.rs
+++ b/benches/big_tree_grid.rs
@@ -67,7 +67,7 @@ pub fn build_deep_grid_tree(
create_container_node: &mut impl FnMut(&mut Taffy, Vec<Node>) -> Node,
) -> Vec<Node> {
// The extra one is for a position:absolute child
- let child_count = track_count.pow(2) + 1;
+ let child_count = track_count * track_count;
if levels == 1 {
// Build leaf nodes
@@ -80,13 +80,6 @@ pub fn build_deep_grid_tree(
.map(|_| {
let sub_children =
build_deep_grid_tree(tree, levels - 1, track_count, create_leaf_node, create_container_node);
- tree.set_style(sub_children[child_count - 1], {
- let mut style = tree.style(sub_children[child_count - 1]).unwrap().clone();
- style.position = Position::Absolute;
- style.size = percent(0.5);
- style
- })
- .unwrap();
create_container_node(tree, sub_children)
})
.collect()
@@ -127,8 +120,8 @@ fn taffy_benchmarks(c: &mut Criterion) {
let mut group = c.benchmark_group("grid/deep");
group.sample_size(10);
- for (tracks, levels) in [(3, 3), (3, 4) /*, (3, 5)*/, (2, 3), (2, 4), (2, 5), (2, 6)].iter() {
- let children_per_level: usize = (tracks * tracks) + 1;
+ for (tracks, levels) in [(2, 5), (3, 4), (2, 7) /*, (3, 5)*/].iter() {
+ let children_per_level: usize = tracks * tracks;
group.bench_with_input(
BenchmarkId::new(format!("{c}x{c}", c = tracks), children_per_level.pow(*levels as u32)),
&(*levels, *tracks),
@@ -145,7 +138,7 @@ fn taffy_benchmarks(c: &mut Criterion) {
let mut group = c.benchmark_group("grid/superdeep");
group.sample_size(10);
- for levels in [10, 20].iter() {
+ for levels in [100, 1000].iter() {
group.bench_with_input(BenchmarkId::new("1x1", levels), levels, |b, &levels| {
b.iter_batched(
|| build_taffy_deep_grid_hierarchy(levels, 1),
diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs
index a0279872d..f4cee05f2 100644
--- a/src/compute/flexbox.rs
+++ b/src/compute/flexbox.rs
@@ -364,7 +364,7 @@ fn compute_preliminary(
// We have the container size.
// If our caller does not care about performing layout we are done now.
if run_mode == RunMode::ComputeSize {
- return SizeAndBaselines { size: constants.container_size, first_baselines: Point::NONE };
+ return constants.container_size.into();
}
// 16. Align all flex lines per align-content.
diff --git a/src/compute/grid/explicit_grid.rs b/src/compute/grid/explicit_grid.rs
index 6cdadbf54..ef85bb389 100644
--- a/src/compute/grid/explicit_grid.rs
+++ b/src/compute/grid/explicit_grid.rs
@@ -4,9 +4,7 @@ use super::types::{GridTrack, TrackCounts};
use crate::axis::AbsoluteAxis;
use crate::math::MaybeMath;
use crate::resolve::ResolveOrZero;
-use crate::style::{
- AvailableSpace, GridTrackRepetition, LengthPercentage, NonRepeatedTrackSizingFunction, Style, TrackSizingFunction,
-};
+use crate::style::{GridTrackRepetition, LengthPercentage, NonRepeatedTrackSizingFunction, Style, TrackSizingFunction};
use crate::style_helpers::TaffyAuto;
use crate::sys::{GridTrackVec, Vec};
use core::cmp::{max, min};
@@ -83,25 +81,20 @@ pub(crate) fn compute_explicit_grid_size_in_axis(style: &Style, axis: AbsoluteAx
let num_repetitions: u16 = match inner_container_size {
None => 1,
Some(inner_container_size) => {
- let available_space = AvailableSpace::Definite(inner_container_size);
+ let parent_size = Some(inner_container_size);
/// ...treating each track as its max track sizing function if that is definite or as its minimum track sizing function
/// otherwise, flooring the max track sizing function by the min track sizing function if both are definite
- fn track_definite_value(
- sizing_function: &NonRepeatedTrackSizingFunction,
- available_space: AvailableSpace,
- ) -> f32 {
- let max_size = sizing_function.max.definite_value(available_space);
- let min_size = sizing_function.max.definite_value(available_space);
+ fn track_definite_value(sizing_function: &NonRepeatedTrackSizingFunction, parent_size: Option<f32>) -> f32 {
+ let max_size = sizing_function.max.definite_value(parent_size);
+ let min_size = sizing_function.max.definite_value(parent_size);
max_size.map(|max| max.maybe_min(min_size)).or(min_size).unwrap()
}
let non_repeating_track_used_space: f32 = template
.iter()
.map(|track_def| match track_def {
- TrackSizingFunction::Single(sizing_function) => {
- track_definite_value(sizing_function, available_space)
- }
+ TrackSizingFunction::Single(sizing_function) => track_definite_value(sizing_function, parent_size),
TrackSizingFunction::AutoRepeat(_, _) => 0.0,
})
.sum();
@@ -110,7 +103,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis(style: &Style, axis: AbsoluteAx
// Compute the amount of space that a single repetition of the repeated track list takes
let per_repetition_track_used_space: f32 = repetition_definition
.iter()
- .map(|sizing_function| track_definite_value(sizing_function, available_space))
+ .map(|sizing_function| track_definite_value(sizing_function, parent_size))
.sum::<f32>();
// We special case the first repetition here because the number of gaps in the first repetition
diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs
index c8f6c2939..1fc504081 100644
--- a/src/compute/grid/mod.rs
+++ b/src/compute/grid/mod.rs
@@ -72,7 +72,7 @@ pub fn compute(
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
- _run_mode: RunMode,
+ run_mode: RunMode,
) -> SizeAndBaselines {
let get_child_styles_iter = |node| tree.children(node).map(|child_node: &Node| tree.style(*child_node));
let style = tree.style(node).clone();
@@ -181,16 +181,15 @@ pub fn compute(
track_sizing_algorithm(
tree,
AbstractAxis::Inline,
- available_space,
+ min_size.get(AbstractAxis::Inline),
+ max_size.get(AbstractAxis::Inline),
+ style.grid_align_content(AbstractAxis::Block),
available_grid_space,
inner_node_size,
- &style,
&mut columns,
&mut rows,
&mut items,
- |track: &GridTrack, available_space: AvailableSpace| {
- track.max_track_sizing_function.definite_value(available_space)
- },
+ |track: &GridTrack, parent_size: Option<f32>| track.max_track_sizing_function.definite_value(parent_size),
);
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());
@@ -199,10 +198,11 @@ pub fn compute(
track_sizing_algorithm(
tree,
AbstractAxis::Block,
- available_space,
+ min_size.get(AbstractAxis::Block),
+ max_size.get(AbstractAxis::Block),
+ style.grid_align_content(AbstractAxis::Inline),
available_grid_space,
inner_node_size,
- &style,
&mut rows,
&mut columns,
&mut items,
@@ -211,14 +211,35 @@ pub fn compute(
let initial_row_sum = rows.iter().map(|track| track.base_size).sum::<f32>();
inner_node_size.height = inner_node_size.height.or_else(|| initial_row_sum.into());
+ // 6. Compute container size
+ let resolved_style_size = known_dimensions.or(style.size.maybe_resolve(parent_size));
+ let container_border_box = Size {
+ width: resolved_style_size
+ .get(AbstractAxis::Inline)
+ .unwrap_or_else(|| initial_column_sum + padding.horizontal_axis_sum() + border.horizontal_axis_sum()),
+ height: resolved_style_size
+ .get(AbstractAxis::Block)
+ .unwrap_or_else(|| initial_row_sum + padding.vertical_axis_sum() + border.vertical_axis_sum()),
+ };
+ let container_content_box = Size {
+ width: container_border_box.width - padding.horizontal_axis_sum() - border.horizontal_axis_sum(),
+ height: container_border_box.height - padding.vertical_axis_sum() - border.vertical_axis_sum(),
+ };
+
+ // If only the container's size has been requested
+ if run_mode == RunMode::ComputeSize {
+ return container_border_box.into();
+ }
+
// Re-run track sizing algorithm for Inline axis
track_sizing_algorithm(
tree,
AbstractAxis::Inline,
- available_space,
+ min_size.get(AbstractAxis::Inline),
+ max_size.get(AbstractAxis::Inline),
+ style.grid_align_content(AbstractAxis::Block),
available_grid_space,
inner_node_size,
- &style,
&mut columns,
&mut rows,
&mut items,
@@ -229,31 +250,17 @@ pub fn compute(
track_sizing_algorithm(
tree,
AbstractAxis::Block,
- available_space,
+ min_size.get(AbstractAxis::Block),
+ max_size.get(AbstractAxis::Block),
+ style.grid_align_content(AbstractAxis::Inline),
available_grid_space,
inner_node_size,
- &style,
&mut rows,
&mut columns,
&mut items,
|track: &GridTrack, _| Some(track.base_size),
);
- // 6. Compute container size
- let resolved_style_size = known_dimensions.or(style.size.maybe_resolve(parent_size));
- let container_border_box = Size {
- width: resolved_style_size
- .get(AbstractAxis::Inline)
- .unwrap_or_else(|| initial_column_sum + padding.horizontal_axis_sum() + border.horizontal_axis_sum()),
- height: resolved_style_size
- .get(AbstractAxis::Block)
- .unwrap_or_else(|| initial_row_sum + padding.vertical_axis_sum() + border.vertical_axis_sum()),
- };
- let container_content_box = Size {
- width: container_border_box.width - padding.horizontal_axis_sum() - border.horizontal_axis_sum(),
- height: container_border_box.height - padding.vertical_axis_sum() - border.vertical_axis_sum(),
- };
-
// 7. Resolve percentage track base sizes
// In the case of an indefinitely sized container these resolve to zero during the "Initialise Tracks" step
// and therefore need to be re-resolved here based on the content-sized content box of the container
diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs
index 65d64dd92..fc6943ffc 100644
--- a/src/compute/grid/track_sizing.rs
+++ b/src/compute/grid/track_sizing.rs
@@ -5,8 +5,8 @@ use crate::axis::AbstractAxis;
use crate::geometry::{Rect, Size};
use crate::math::MaybeMath;
use crate::prelude::LayoutTree;
-use crate::resolve::{MaybeResolve, ResolveOrZero};
-use crate::style::{AlignContent, AvailableSpace, MaxTrackSizingFunction, MinTrackSizingFunction, Style};
+use crate::resolve::ResolveOrZero;
+use crate::style::{AlignContent, AvailableSpace, MaxTrackSizingFunction, MinTrackSizingFunction};
use crate::sys::{f32_max, f32_min};
use core::cmp::Ordering;
@@ -26,12 +26,14 @@ struct ItemBatcher {
impl ItemBatcher {
/// Create a new ItemBatcher for the specified axis
+ #[inline(always)]
fn new(axis: AbstractAxis) -> Self {
ItemBatcher { index_offset: 0, axis, current_span: 1, current_is_flex: false }
}
/// This is basically a manual version of Iterator::next which passes `items`
/// in as a parameter on each iteration to work around borrow checker rules
+ #[inline]
fn next<'items>(&mut self, items: &'items mut [GridItem]) -> Option<(&'items mut [GridItem], bool)> {
if self.current_is_flex || self.index_offset >= items.len() {
return None;
@@ -88,12 +90,17 @@ pub(super) fn cmp_by_cross_flex_then_span_then_start(
/// When applying the track sizing algorithm and estimating the size in the other axis for content sizing items
/// we should take into account align-content/justify-content if both the grid container and all items in the
/// other axis have definite sizes. This function computes such a per-gutter additional size adjustment.
+#[inline(always)]
pub(super) fn compute_alignment_gutter_adjustment(
alignment: AlignContent,
- available_space: AvailableSpace,
- get_track_size_estimate: impl Fn(&GridTrack, AvailableSpace) -> Option<f32>,
+ axis_inner_node_size: Option<f32>,
+ get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>,
tracks: &[GridTrack],
) -> f32 {
+ if tracks.len() <= 1 {
+ return 0.0;
+ }
+
// As items never cross the outermost gutters in a grid, we can simplify our calculations by treating
// AlignContent::Start and AlignContent::End the same
let outer_gutter_weight = match alignment {
@@ -120,24 +127,29 @@ pub(super) fn compute_alignment_gutter_adjustment(
AlignContent::SpaceEvenly => 1,
};
- if inner_gutter_weight > 0 && available_space.is_definite() && tracks.len() > 1 {
- let inner_available_space = tracks
+ if inner_gutter_weight == 0 {
+ return 0.0;
+ }
+
+ if let Some(axis_inner_node_size) = axis_inner_node_size {
+ let free_space = tracks
.iter()
- .map(|track| get_track_size_estimate(track, available_space))
+ .map(|track| get_track_size_estimate(track, Some(axis_inner_node_size)))
.sum::<Option<f32>>()
- .map(|track_size_sum| f32_max(0.0, available_space.unwrap() - track_size_sum))
+ .map(|track_size_sum| f32_max(0.0, axis_inner_node_size - track_size_sum))
.unwrap_or(0.0);
let weighted_track_count =
(((tracks.len() - 3) / 2) * inner_gutter_weight as usize) + (2 * outer_gutter_weight as usize);
- (inner_available_space / weighted_track_count as f32) * inner_gutter_weight as f32
- } else {
- 0.0
+ return (free_space / weighted_track_count as f32) * inner_gutter_weight as f32;
}
+
+ 0.0
}
/// Convert origin-zero coordinates track placement in grid track vector indexes
+#[inline(always)]
pub(super) fn resolve_item_track_indexes(items: &mut [GridItem], column_counts: TrackCounts, row_counts: TrackCounts) {
for item in items {
item.column_indexes = item.column.map(|line| line.into_track_vec_index(column_counts) as u16);
@@ -146,6 +158,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(
items: &mut Vec<GridItem>,
columns: &[GridTrack],
@@ -162,17 +175,19 @@ pub(super) fn determine_if_item_crosses_flexible_tracks(
/// Track sizing algorithm
/// Note: Gutters are treated as empty fixed-size tracks for the purpose of the track sizing algorithm.
#[allow(clippy::too_many_arguments)]
+#[inline(always)]
pub(super) fn track_sizing_algorithm<Tree: LayoutTree>(
tree: &mut Tree,
axis: AbstractAxis,
- available_space: Size<AvailableSpace>,
+ axis_min_size: Option<f32>,
+ axis_max_size: Option<f32>,
+ other_axis_alignment: AlignContent,
available_grid_space: Size<AvailableSpace>,
inner_node_size: Size<Option<f32>>,
- container_style: &Style,
axis_tracks: &mut [GridTrack],
other_axis_tracks: &mut [GridTrack],
items: &mut [GridItem],
- get_track_size_estimate: impl Fn(&GridTrack, AvailableSpace) -> Option<f32>,
+ get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>,
) {
// 11.4 Initialise Track sizes
// Initialize each track’s base size and growth limit.
@@ -198,8 +213,8 @@ pub(super) fn track_sizing_algorithm<Tree: LayoutTree>(
// Compute an additional amount to add to each spanned gutter when computing item's estimated size in the
// in the opposite axis based on the alignment, container size, and estimated track sizes in that axis
let gutter_alignment_adjustment = compute_alignment_gutter_adjustment(
- container_style.grid_align_content(axis.other()),
- available_space.get(axis.other()),
+ other_axis_alignment,
+ inner_node_size.get(axis.other()),
&get_track_size_estimate,
other_axis_tracks,
);
@@ -218,19 +233,17 @@ pub(super) fn track_sizing_algorithm<Tree: LayoutTree>(
axis_tracks,
other_axis_tracks,
items,
- available_grid_space,
+ available_grid_space.get(axis),
inner_node_size,
get_track_size_estimate,
);
// 11.6. Maximise Tracks
// Distributes free space (if any) to tracks with FINITE growth limits, up to their limits.
- maximise_tracks(axis, axis_tracks, available_grid_space);
+ maximise_tracks(axis_tracks, inner_node_size.get(axis), available_grid_space.get(axis));
// 11.7. Expand Flexible Tracks
// This step sizes flexible tracks using the largest value it can assign to an fr without exceeding the available space.
- let axis_min_size = container_style.min_size.get(axis).into_option();
- let axis_max_size = container_style.max_size.get(axis).into_option();
expand_flexible_tracks(
tree,
axis,
@@ -244,7 +257,7 @@ pub(super) fn track_sizing_algorithm<Tree: LayoutTree>(
// 11.8. Stretch auto Tracks
// This step expands tracks that have an auto max track sizing function by dividing any remaining positive, definite free space equally amongst them.
- stretch_auto_tracks(axis, axis_tracks, container_style, available_space, available_grid_space);
+ stretch_auto_tracks(axis, axis_tracks, axis_min_size, available_grid_space);
}
/// Whether it is a minimum or maximum size's space being distributed
@@ -260,6 +273,7 @@ enum IntrinsicContributionType {
/// Add any planned base size increases to the base size after a round of distributing space to base sizes
/// Reset the planed base size increase to zero ready for the next round.
+#[inline(always)]
fn flush_planned_base_size_increases(tracks: &mut [GridTrack]) {
for track in tracks {
track.base_size += track.base_size_planned_increase;
@@ -269,6 +283,7 @@ fn flush_planned_base_size_increases(tracks: &mut [GridTrack]) {
/// Add any planned growth limit increases to the growth limit after a round of distributing space to growth limits
/// Reset the planed growth limit increase to zero ready for the next round.
+#[inline(always)]
fn flush_planned_growth_limit_increases(tracks: &mut [GridTrack], set_infinitely_growable: bool) {
for track in tracks {
if track.growth_limit_planned_increase > 0.0 {
@@ -287,6 +302,7 @@ fn flush_planned_growth_limit_increases(tracks: &mut [GridTrack], set_infinitely
/// 11.4 Initialise Track sizes
/// Initialize each track’s base size and growth limit.
+#[inline(always)]
fn initialize_track_sizes(axis_tracks: &mut [GridTrack], axis_inner_node_size: Option<f32>) {
let last_track_idx = axis_tracks.len() - 1;
@@ -304,7 +320,7 @@ fn initialize_track_sizes(axis_tracks: &mut [GridTrack], axis_inner_node_size: O
// Note: Indefinite lengths cannot occur, as they’re treated as auto.
// - An intrinsic sizing function
// Use an initial base size of zero.
- track.base_size = track.min_track_sizing_function.definite_value(axis_inner_node_size.into()).unwrap_or(0.0);
+ track.base_size = track.min_track_sizing_function.definite_value(axis_inner_node_size).unwrap_or(0.0);
// For each track, if the track’s max track sizing function is:
// - A fixed sizing function
@@ -314,7 +330,7 @@ fn initialize_track_sizes(axis_tracks: &mut [GridTrack], axis_inner_node_size: O
// - A flexible sizing function
// Use an initial growth limit of infinity.
track.growth_limit =
- track.max_track_sizing_function.definite_value(axis_inner_node_size.into()).unwrap_or(f32::INFINITY);
+ track.max_track_sizing_function.definite_value(axis_inner_node_size).unwrap_or(f32::INFINITY);
// In all cases, if the growth limit is less than the base size, increase the growth limit to match the base size.
if track.growth_limit < track.base_size {
@@ -331,9 +347,9 @@ fn resolve_intrinsic_track_sizes(
axis_tracks: &mut [GridTrack],
other_axis_tracks: &mut [GridTrack],
items: &mut [GridItem],
- available_grid_space: Size<AvailableSpace>,
+ axis_available_grid_space: AvailableSpace,
inner_node_size: Size<Option<f32>>,
- get_track_size_estimate: impl Fn(&GridTrack, AvailableSpace) -> Option<f32>,
+ get_track_size_estimate: impl Fn(&GridTrack, Option<f32>) -> Option<f32>,
) {
// Step 1. Shim baseline-aligned items so their intrinsic size contributions reflect their baseline alignment.
// TODO: we do not yet support baseline alignment for CSS Grid
@@ -355,58 +371,121 @@ fn resolve_intrinsic_track_sizes(
// Note: For items with a specified minimum size of auto (the initial value), the minimum contribution is usually equivalent
// to the min-content contribution—but can differ in some cases, see §6.6 Automatic Minimum Size of Grid Items.
// Also, minimum contribution <= min-content contribution <= max-content contribution.
- // TODO: be smarter about only computing these when they are required
- let mut compute_item_sizes = |item: &mut GridItem, axis_tracks: &[GridTrack]| {
- let known_dimensions = item.known_dimensions_cached(
- axis,
- other_axis_tracks,
- available_grid_space.get(axis.other()),
- &get_track_size_estimate,
- );
- let margin = Rect {
- left: item.margin.left.resolve_or_zero(Some(0.0)),
- right: item.margin.right.resolve_or_zero(Some(0.0)),
- top: item.margin.top.resolve_or_zero(available_grid_space.width.into_option()),
- bottom: item.margin.bottom.resolve_or_zero(available_grid_space.width.into_option()),
+ /// This struct captures a bunch of variables which are used to compute the intrinsic sizes of children so that those variables
+ /// don't have to be passed around all over the place below. It then has methods that implement the intrinsic sizing computations
+ struct IntrisicSizeMeasurer<'tree, 'oat, Tree, EstimateFunction>
+ where
+ Tree: LayoutTree,
+ EstimateFunction: Fn(&GridTrack, Option<f32>) -> Option<f32>,
+ {
+ /// The layout tree
+ tree: &'tree mut Tree,
+ /// The tracks in the opposite axis to the one we are currently sizing
+ other_axis_tracks: &'oat [GridTrack],
+ /// A function that computes an estimate of an other-axis track's size which is passed to
+ /// the child size measurement functions
+ get_track_size_estimate: EstimateFunction,
+ /// The axis we are currently sizing
+ axis: AbstractAxis,
+ /// The available grid space
+ inner_node_size: Size<Option<f32>>,
+ }
+
+ impl<'tree, 'oat, Tree, EstimateFunction> IntrisicSizeMeasurer<'tree, 'oat, Tree, EstimateFunction>
+ where
+ Tree: LayoutTree,
+ EstimateFunction: Fn(&GridTrack, Option<f32>) -> Option<f32>,
+ {
+ /// 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
+ #[inline(always)]
+ fn known_dimensions(&self, item: &mut GridItem) -> Size<Option<f32>> {
+ item.known_dimensions_cached(
+ self.axis,
+ self.other_axis_tracks,
+ self.inner_node_size.get(self.axis.other()),
+ &self.get_track_size_estimate,
+ )
}
- .sum_axes();
- let min_content_size = item.min_content_contribution_cached(tree, known_dimensions, inner_node_size) + margin;
- let max_content_size = item.max_content_contribution_cached(tree, known_dimensions, inner_node_size) + margin;
- let axis_minimum_size = item.minimum_contribution_cached(
- tree,
- axis,
- axis_tracks,
- available_grid_space,
- known_dimensions,
- inner_node_size,
- ) + margin.get(axis);
+ /// Compute the item's resolved margins for size contributions. Horizontal percentage margins always resolve
+ /// to zero if the container size is indefinite as otherwise this would introduce a cyclic dependency.
+ #[inline(always)]
+ fn margins_axis_sums(&self, item: &mut GridItem) -> Size<f32> {
+ let parent_width = self.inner_node_size.width;
+ Rect {
+ left: item.margin.left.resolve_or_zero(Some(0.0)),
+ right: item.margin.right.resolve_or_zero(Some(0.0)),
+ top: item.margin.top.resolve_or_zero(parent_width),
+ bottom: item.margin.bottom.resolve_or_zero(parent_width),
+ }
+ .sum_axes()
+ }
- (axis_minimum_size, min_content_size.get(axis), max_content_size.get(axis))
- };
+ /// Retrieve the item's min content contribution from the cache or compute it using the provided parameters
+ #[inline(always)]
+ 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 axis_available_grid_space = available_grid_space.get(axis);
+ /// Retrieve the item's max content contribution from the cache or compute it using the provided parameters
+ #[inline(always)]
+ 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)
+ }
+
+ /// The minimum contribution of an item is the smallest outer size it can have.
+ /// Specifically:
+ /// - If the item’s computed preferred size behaves as auto or depends on the size of its containing block in the relevant axis:
+ /// 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.
+ #[inline(always)]
+ fn minimum_contribution(&mut self, item: &mut GridItem, axis_tracks: &[GridTrack]) -> f32 {
+ let known_dimensions = self.known_dimensions(item);
+ let margin_axis_sums = self.margins_axis_sums(item);
+ let contribution = item.minimum_contribution_cached(
+ self.tree,
+ self.axis,
+ axis_tracks,
+ known_dimensions,
+ self.inner_node_size,
+ );
+ contribution + margin_axis_sums.get(self.axis)
+ }
+ }
+
+ let axis_inner_node_size = inner_node_size.get(axis);
+ let mut item_sizer =
+ IntrisicSizeMeasurer { tree, other_axis_tracks, axis, inner_node_size, get_track_size_estimate };
let mut batched_item_iterator = ItemBatcher::new(axis);
while let Some((batch, is_flex)) = batched_item_iterator.next(items) {
// 1. For intrinsic minimums:
// 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(available_grid_space.get(axis)).is_none()
- };
+ 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() {
- let (axis_minimum_size, axis_min_content_size, _) = compute_item_sizes(item, axis_tracks);
-
// ...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.
let space = match axis_available_grid_space {
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
- let limit = item.spanned_fixed_track_limit(axis, axis_tracks, inner_node_size.get(axis).into());
+ let axis_minimum_size = item_sizer.minimum_contribution(item, axis_tracks);
+ let axis_min_content_size = item_sizer.min_content_contribution(item);
+ let limit = item.spanned_fixed_track_limit(axis, axis_tracks, axis_inner_node_size);
axis_min_content_size.maybe_min(limit).max(axis_minimum_size)
}
- AvailableSpace::Definite(_) => axis_minimum_size,
+ AvailableSpace::Definite(_) => item_sizer.minimum_contribution(item, axis_tracks),
};
let tracks = &mut axis_tracks[item.track_range_excluding_lines(axis)];
if space > 0.0 {
@@ -416,7 +495,7 @@ fn resolve_intrinsic_track_sizes(
tracks,
has_intrinsic_min_track_sizing_function,
IntrinsicContributionType::Minimum,
- available_grid_space.get(axis),
+ axis_inner_node_size,
);
}
}
@@ -430,9 +509,8 @@ fn resolve_intrinsic_track_sizes(
matches!(track.min_track_sizing_function, MinContent | MaxContent)
};
for item in batch.iter_mut() {
- let (_, axis_min_content_size, _) = compute_item_sizes(item, axis_tracks);
+ let space = item_sizer.min_content_contribution(item);
let tracks = &mut axis_tracks[item.track_range_excluding_lines(axis)];
- let space = axis_min_content_size;
if space > 0.0 {
distribute_item_space_to_base_size(
is_flex,
@@ -440,7 +518,7 @@ fn resolve_intrinsic_track_sizes(
tracks,
has_min_or_max_content_min_track_sizing_function,
IntrinsicContributionType::Minimum,
- available_grid_space.get(axis),
+ axis_inner_node_size,
);
}
}
@@ -457,8 +535,8 @@ fn resolve_intrinsic_track_sizes(
matches!(track.min_track_sizing_function, Auto | MaxContent)
};
for item in batch.iter_mut() {
- let (_, _, axis_max_content_size) = compute_item_sizes(item, axis_tracks);
- let limit = item.spanned_fixed_track_limit(axis, axis_tracks, inner_node_size.get(axis).into());
+ let axis_max_content_size = item_sizer.max_content_contribution(item);
+ let limit = item.spanned_fixed_track_limit(axis, axis_tracks, axis_inner_node_size);
let space = axis_max_content_size.maybe_min(limit);
let tracks = &mut axis_tracks[item.track_range_excluding_lines(axis)];
if space > 0.0 {
@@ -468,7 +546,7 @@ fn resolve_intrinsic_track_sizes(
tracks,
has_auto_or_max_content_min_track_sizing_function,
IntrinsicContributionType::Maximum,
- available_grid_space.get(axis),
+ axis_inner_node_size,
);
}
}
@@ -480,7 +558,7 @@ fn resolve_intrinsic_track_sizes(
let has_max_content_min_track_sizing_function =
move |track: &GridTrack| matches!(track.min_track_sizing_function, MinTrackSizingFunction::MaxContent);
for item in batch.iter_mut() {
- let (_, _, axis_max_content_size) = compute_item_sizes(item, axis_tracks);
+ let axis_max_content_size = item_sizer.max_content_contribution(item);
let space = axis_max_content_size;
let tracks = &mut axis_tracks[item.track_range_excluding_lines(axis)];
if space > 0.0 {
@@ -490,7 +568,7 @@ fn resolve_intrinsic_track_sizes(
tracks,
has_max_content_min_track_sizing_function,
IntrinsicContributionType::Maximum,
- available_grid_space.get(axis),
+ axis_inner_node_size,
);
}
}
@@ -508,11 +586,10 @@ fn resolve_intrinsic_track_sizes(
if !is_flex {
// 5. For intrinsic maximums: Next increase the growth limit of tracks with an intrinsic max track sizing function by
// distributing extra space as needed to account for these items' min-content contributions.
- let has_intrinsic_max_track_sizing_function = move |track: &GridTrack| {
- track.max_track_sizing_function.definite_value(available_grid_space.get(axis)).is_none()
- };
+ let has_intrinsic_max_track_sizing_function =
+ move |track: &GridTrack| track.max_track_sizing_function.definite_value(axis_inner_node_size).is_none();
for item in batch.iter_mut() {
- let (_, axis_min_content_size, _) = compute_item_sizes(item, axis_tracks);
+ let axis_min_content_size = item_sizer.min_content_contribution(item);
let space = axis_min_content_size;
let tracks = &mut axis_tracks[item.track_range_excluding_lines(axis)];
if space > 0.0 {
@@ -533,7 +610,7 @@ fn resolve_intrinsic_track_sizes(
let has_max_content_max_track_sizing_function =
|track: &GridTrack| track.max_track_sizing_function.is_max_content_alike();
for item in batch.iter_mut() {
- let (_, _, axis_max_content_size) = compute_item_sizes(item, axis_tracks);
+ let axis_max_content_size = item_sizer.max_content_contribution(item);
let space = axis_max_content_size;
let tracks = &mut axis_tracks[item.track_range_excluding_lines(axis)];
if space > 0.0 {
@@ -561,13 +638,14 @@ fn resolve_intrinsic_track_sizes(
/// 11.5.1. Distributing Extra Space Across Spanned Tracks
/// https://www.w3.org/TR/css-grid-1/#extra-space
+#[inline(always)]
fn distribute_item_space_to_base_size(
is_flex: bool,
space: f32,
tracks: &mut [GridTrack],
track_is_affected: impl Fn(&GridTrack) -> bool,
intrinsic_contribution_type: IntrinsicContributionType,
- axis_available_grid_space: AvailableSpace,
+ axis_inner_node_size: Option<f32>,
) {
if is_flex {
let filter = |track: &GridTrack| track.is_flexible() && track_is_affected(track);
@@ -576,7 +654,7 @@ fn distribute_item_space_to_base_size(
tracks,
filter,
intrinsic_contribution_type,
- axis_available_grid_space,
+ axis_inner_node_size,
)
} else {
distribute_item_space_to_base_size_inner(
@@ -584,7 +662,7 @@ fn distribute_item_space_to_base_size(
tracks,
track_is_affected,
intrinsic_contribution_type,
- axis_available_grid_space,
+ axis_inner_node_size,
)
}
@@ -595,7 +673,7 @@ fn distribute_item_space_to_base_size(
tracks: &mut [GridTrack],
track_is_affected: impl Fn(&GridTrack) -> bool,
intrinsic_contribution_type: IntrinsicContributionType,
- axis_available_grid_space: AvailableSpace,
+ axis_inner_node_size: Option<f32>,
) {
// Skip this distribution if there is either
// - no space to distribute
@@ -611,9 +689,8 @@ fn distribute_item_space_to_base_size(
// Define fit_content_limited_growth_limit function. This is passed to the distribute_space_up_to_limits
// helper function, and is used to compute the limit to distribute up to for each track.
// Wrapping the method on GridTrack is necessary in order to resolve percentage fit-content arguments.
- let axis_available_grid_space_opt = axis_available_grid_space.into_option();
let fit_content_limited_growth_limit =
- move |track: &GridTrack| track.fit_content_limited_growth_limit(axis_available_grid_space_opt);
+ move |track: &GridTrack| track.fit_content_limited_growth_limit(axis_inner_node_size);
// 1. Find the space to distribute
let track_sizes: f32 = tracks.iter().map(|track| track.base_size).sum();
@@ -749,19 +826,23 @@ fn distribute_item_space_to_growth_limit(
/// 11.6 Maximise Tracks
/// Distributes free space (if any) to tracks with FINITE growth limits, up to their limits.
-fn maximise_tracks(axis: AbstractAxis, axis_tracks: &mut [GridTrack], available_grid_space: Size<AvailableSpace>) {
+#[inline(always)]
+fn maximise_tracks(
+ axis_tracks: &mut [GridTrack],
+ axis_inner_node_size: Option<f32>,
+ axis_available_grid_space: AvailableSpace,
+) {
let used_space: f32 = axis_tracks.iter().map(|track| track.base_size).sum();
- let free_space = available_grid_space.get(axis).compute_free_space(used_space);
+ let free_space = axis_available_grid_space.compute_free_space(used_space);
if free_space == f32::INFINITY {
axis_tracks.iter_mut().for_each(|track| track.base_size = track.growth_limit);
} else if free_space > 0.0 {
- let axis_available_grid_space = available_grid_space.get(axis).into_option();
distribute_space_up_to_limits(
free_space,
axis_tracks,
|_| true,
|track| track.base_size,
- move |track: &GridTrack| track.fit_content_limited_growth_limit(axis_available_grid_space),
+ move |track: &GridTrack| track.fit_content_limited_growth_limit(axis_inner_node_size),
);
for track in axis_tracks.iter_mut() {
track.base_size += track.item_incurred_increase;
@@ -773,6 +854,7 @@ fn maximise_tracks(axis: AbstractAxis, axis_tracks: &mut [GridTrack], available_
/// 11.7. Expand Flexible Tracks
/// This step sizes flexible tracks using the largest value it can assign to an fr without exceeding the available space.
#[allow(clippy::too_many_arguments)]
+#[inline(always)]
fn expand_flexible_tracks(
tree: &mut impl LayoutTree,
axis: AbstractAxis,
@@ -873,6 +955,7 @@ fn expand_flexible_tracks(
/// 11.7.1. Find the Size of an fr
/// This algorithm finds the largest size that an fr unit can be without exceeding the target size.
/// It must be called with a set of grid tracks and some quantity of space to fill.
+#[inline(always)]
fn find_size_of_fr(tracks: &[GridTrack], space_to_fill: f32) -> f32 {
// Handle the trivial case where there is no space to fill
// Do not remove as otherwise the loop below will loop infinitely
@@ -929,11 +1012,11 @@ fn find_size_of_fr(tracks: &[GridTrack], space_to_fill: f32) -> f32 {
/// 11.8. Stretch auto Tracks
/// This step expands tracks that have an auto max track sizing function by dividing any remaining positive, definite free space equally amongst them.
+#[inline(always)]
fn stretch_auto_tracks(
axis: AbstractAxis,
axis_tracks: &mut [GridTrack],
- container_style: &Style,
- available_space: Size<AvailableSpace>,
+ axis_min_size: Option<f32>,
available_grid_space: Size<AvailableSpace>,
) {
let num_auto_tracks =
@@ -946,7 +1029,7 @@ fn stretch_auto_tracks(
let free_space = if available_grid_space.get(axis).is_definite() {
available_grid_space.get(axis).compute_free_space(used_space)
} else {
- match container_style.min_size.maybe_resolve(available_space.into_options()).get(axis) {
+ match axis_min_size {
Some(size) => size - used_space,
None => 0.0,
}
@@ -963,6 +1046,7 @@ fn stretch_auto_tracks(
/// Helper function for distributing space to tracks evenly
/// Used by both distribute_item_space_to_base_size and maximise_tracks steps
+#[inline(always)]
fn distribute_space_up_to_limits(
space_to_distribute: f32,
tracks: &mut [GridTrack],
diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs
index 36f0d5776..f42bb5ada 100644
--- a/src/compute/grid/types/grid_item.rs
+++ b/src/compute/grid/types/grid_item.rs
@@ -8,7 +8,7 @@ use crate::layout::SizingMode;
use crate::node::Node;
use crate::prelude::LayoutTree;
use crate::resolve::MaybeResolve;
-use crate::style::{AvailableSpace, LengthPercentageAuto, MaxTrackSizingFunction, MinTrackSizingFunction, Style};
+use crate::style::{LengthPercentageAuto, MaxTrackSizingFunction, MinTrackSizingFunction, Style};
use crate::style_helpers::*;
use core::ops::Range;
@@ -125,15 +125,15 @@ 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 on the provisional base size in the opposite
+ /// 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: AvailableSpace,
- get_track_size_estimate: impl Fn(&GridTrack, AvailableSpace) -> Option<f32>,
+ 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> = {
@@ -163,16 +163,16 @@ impl GridItem {
&mut self,
axis: AbstractAxis,
axis_tracks: &[GridTrack],
- axis_available_space: AvailableSpace,
+ axis_parent_size: Option<f32>,
) -> Option<f32> {
let spanned_tracks = &axis_tracks[self.track_range_excluding_lines(axis)];
let tracks_all_fixed = spanned_tracks
.iter()
- .all(|track| track.max_track_sizing_function.definite_limit(axis_available_space).is_some());
+ .all(|track| track.max_track_sizing_function.definite_limit(axis_parent_size).is_some());
if tracks_all_fixed {
let limit: f32 = spanned_tracks
.iter()
- .map(|track| track.max_track_sizing_function.definite_limit(axis_available_space).unwrap())
+ .map(|track| track.max_track_sizing_function.definite_limit(axis_parent_size).unwrap())
.sum();
Some(limit)
} else {
@@ -233,23 +233,18 @@ impl GridItem {
tree: &mut impl LayoutTree,
axis: AbstractAxis,
axis_tracks: &[GridTrack],
- available_space: Size<AvailableSpace>,
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);
- style
+ let contribution = style
.size
- .maybe_resolve(available_space.into_options())
+ .maybe_resolve(inner_node_size)
.maybe_apply_aspect_ratio(style.aspect_ratio)
.get(axis)
.or_else(|| {
- style
- .min_size
- .maybe_resolve(available_space.into_options())
- .maybe_apply_aspect_ratio(style.aspect_ratio)
- .get(axis)
+ 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
@@ -282,7 +277,9 @@ impl GridItem {
} else {
0.0
}
- })
+ });
+ self.minimum_contribution_cache = Some(contribution);
+ contribution
})
}
diff --git a/src/compute/mod.rs b/src/compute/mod.rs
index 6008d8a3a..5bf1dc6dd 100644
--- a/src/compute/mod.rs
+++ b/src/compute/mod.rs
@@ -286,16 +286,21 @@ fn compute_cache_slot(known_dimensions: Size<Option<f32>>, available_space: Size
return 0;
}
- // Slot 1: 1 of 2 known_dimensions were set and the other dimension was either a MaxContent or Definite available space constraint
- // Slot 2: 1 of 2 known_dimensions were set and the other dimension was a MinContent constraint
- if has_known_width || has_known_height {
- let other_dim_available_space = if has_known_width { available_space.height } else { available_space.width };
- return 1 + (other_dim_available_space == AvailableSpace::MinContent) as usize;
+ // Slot 1: width but not height known_dimension was set and the other dimension was either a MaxContent or Definite available space constraint
+ // Slot 2: width but not height known_dimension was set and the other dimension was a MinContent constraint
+ if has_known_width && !has_known_height {
+ return 1 + (available_space.height == AvailableSpace::MinContent) as usize;
}
- // Slot 3: Neither known_dimensions were set and we are sizing under a MaxContent or Definite available space constraint
- // Slot 4: Neither known_dimensions were set and we are sizing under a MinContent constraint
- 3 + (available_space.width == AvailableSpace::MinContent) as usize
+ // Slot 3: height but not width known_dimension was set and the other dimension was either a MaxContent or Definite available space constraint
+ // Slot 4: height but not width known_dimension was set and the other dimension was a MinContent constraint
+ if !has_known_width && has_known_height {
+ return 3 + (available_space.width == AvailableSpace::MinContent) as usize;
+ }
+
+ // Slot 5: Neither known_dimensions were set and we are sizing under a MaxContent or Definite available space constraint
+ // Slot 6: Neither known_dimensions were set and we are sizing under a MinContent constraint
+ 5 + (available_space.width == AvailableSpace::MinContent) as usize
}
/// Try to get the computation result from the cache.
diff --git a/src/data.rs b/src/data.rs
index f09d3cb1b..03a06c673 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -6,7 +6,7 @@ use crate::layout::{Cache, Layout};
use crate::style::Style;
/// The number of cache entries for each node in the tree
-pub(crate) const CACHE_SIZE: usize = 5;
+pub(crate) const CACHE_SIZE: usize = 7;
/// Layout information for a given [`Node`](crate::node::Node)
///
diff --git a/src/layout.rs b/src/layout.rs
index 13fabe78e..522cce1bd 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -36,6 +36,12 @@ pub struct SizeAndBaselines {
pub first_baselines: Point<Option<f32>>,
}
+impl From<Size<f32>> for SizeAndBaselines {
+ fn from(size: Size<f32>) -> Self {
+ Self { size, first_baselines: Point::NONE }
+ }
+}
+
/// The final result of a layout algorithm for a single [`Node`](crate::node::Node).
#[derive(Debug, Copy, Clone)]
pub struct Layout {
diff --git a/src/style/grid.rs b/src/style/grid.rs
index cf89e057e..fa6364698 100644
--- a/src/style/grid.rs
+++ b/src/style/grid.rs
@@ -3,7 +3,6 @@ use super::{AlignContent, LengthPercentage, Style};
use crate::axis::{AbsoluteAxis, AbstractAxis};
use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
use crate::geometry::{Line, MinMax};
-use crate::style::AvailableSpace;
use crate::style_helpers::*;
use crate::sys::GridTrackVec;
use core::cmp::{max, min};
@@ -312,14 +311,11 @@ impl MaxTrackSizingFunction {
/// 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.
#[inline(always)]
- pub fn definite_value(self, available_space: AvailableSpace) -> Option<f32> {
+ pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
use MaxTrackSizingFunction::{Auto, *};
match self {
Fixed(LengthPercentage::Points(size)) => Some(size),
- Fixed(LengthPercentage::Percent(fraction)) => match available_space {
- AvailableSpace::Definite(available_size) => Some(fraction * available_size),
- _ => None,
- },
+ Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
MinContent | MaxContent | FitContent(_) | Auto | Flex(_) => None,
}
}
@@ -330,15 +326,13 @@ impl MaxTrackSizingFunction {
/// - A fit-content sizing function with fixed argument
/// - A fit-content sizing function with percentage argument (with definite available space)
/// All other kinds of track sizing function return None.
- pub fn definite_limit(self, available_space: AvailableSpace) -> Option<f32> {
+ #[inline(always)]
+ pub fn definite_limit(self, parent_size: Option<f32>) -> Option<f32> {
use MaxTrackSizingFunction::FitContent;
match self {
FitContent(LengthPercentage::Points(size)) => Some(size),
- FitContent(LengthPercentage::Percent(fraction)) => match available_space {
- AvailableSpace::Definite(available_size) => Some(fraction * available_size),
- _ => None,
- },
- _ => self.definite_value(available_space),
+ FitContent(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
+ _ => self.definite_value(parent_size),
}
}
@@ -398,14 +392,11 @@ impl MinTrackSizingFunction {
/// 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`.
#[inline(always)]
- pub fn definite_value(self, available_space: AvailableSpace) -> Option<f32> {
+ pub fn definite_value(self, parent_size: Option<f32>) -> Option<f32> {
use MinTrackSizingFunction::{Auto, *};
match self {
Fixed(LengthPercentage::Points(size)) => Some(size),
- Fixed(LengthPercentage::Percent(fraction)) => match available_space {
- AvailableSpace::Definite(available_size) => Some(fraction * available_size),
- _ => None,
- },
+ Fixed(LengthPercentage::Percent(fraction)) => parent_size.map(|size| fraction * size),
MinContent | MaxContent | Auto => None,
}
}
diff --git a/tests/caching.rs b/tests/caching.rs
new file mode 100644
index 000000000..37eff95e2
--- /dev/null
+++ b/tests/caching.rs
@@ -0,0 +1,64 @@
+#[cfg(test)]
+mod caching {
+ use taffy::node::MeasureFunc;
+ use taffy::prelude::*;
+
+ #[test]
+ fn measure_count_flexbox() {
+ use std::sync::atomic::{AtomicU32, Ordering};
+
+ let mut taffy = Taffy::new();
+ static NUM_MEASURES: AtomicU32 = AtomicU32::new(0);
+
+ let grandchild = taffy
+ .new_leaf_with_measure(
+ Style { ..Default::default() },
+ MeasureFunc::Raw(|known_dimensions, _available_space| {
+ NUM_MEASURES.fetch_add(1, Ordering::SeqCst);
+ Size {
+ width: known_dimensions.width.unwrap_or(50.0),
+ height: known_dimensions.height.unwrap_or(50.0),
+ }
+ }),
+ )
+ .unwrap();
+
+ let child = taffy.new_with_children(Style::DEFAULT, &[grandchild]).unwrap();
+
+ let node = taffy.new_with_children(Style::DEFAULT, &[child]).unwrap();
+ taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
+
+ assert_eq!(NUM_MEASURES.load(Ordering::SeqCst), 2);
+ }
+
+ #[test]
+ #[cfg(feature = "grid")]
+ fn measure_count_grid() {
+ use std::sync::atomic::{AtomicU32, Ordering};
+
+ let style = || Style { display: Display::Grid, ..Default::default() };
+
+ let mut taffy = Taffy::new();
+ static NUM_MEASURES: AtomicU32 = AtomicU32::new(0);
+
+ let grandchild = taffy
+ .new_leaf_with_measure(
+ style(),
+ MeasureFunc::Raw(|known_dimensions, _available_space| {
+ NUM_MEASURES.fetch_add(1, Ordering::SeqCst);
+ Size {
+ width: known_dimensions.width.unwrap_or(50.0),
+ height: known_dimensions.height.unwrap_or(50.0),
+ }
+ }),
+ )
+ .unwrap();
+
+ let child = taffy.new_with_children(style(), &[grandchild]).unwrap();
+
+ let node = taffy.new_with_children(style(), &[child]).unwrap();
+
+ taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
+ assert_eq!(NUM_MEASURES.load(Ordering::SeqCst), 2);
+ }
+}
diff --git a/tests/measure.rs b/tests/measure.rs
index 25b9c0f96..3d9cfce44 100644
--- a/tests/measure.rs
+++ b/tests/measure.rs
@@ -446,32 +446,4 @@ mod measure {
assert_eq!(taffy.layout(child).unwrap().size.width, 100.0);
assert_eq!(taffy.layout(child).unwrap().size.height, 100.0);
}
-
- #[test]
- fn only_measure_once() {
- use std::sync::atomic::{AtomicU32, Ordering};
-
- let mut taffy = Taffy::new();
- static NUM_MEASURES: AtomicU32 = AtomicU32::new(0);
-
- let grandchild = taffy
- .new_leaf_with_measure(
- Style { ..Default::default() },
- MeasureFunc::Raw(|known_dimensions, _available_space| {
- NUM_MEASURES.fetch_add(1, Ordering::SeqCst);
- Size {
- width: known_dimensions.width.unwrap_or(50.0),
- height: known_dimensions.height.unwrap_or(50.0),
- }
- }),
- )
- .unwrap();
-
- let child = taffy.new_with_children(Style { ..Default::default() }, &[grandchild]).unwrap();
-
- let node = taffy.new_with_children(Style { ..Default::default() }, &[child]).unwrap();
- taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
-
- assert_eq!(NUM_MEASURES.load(Ordering::SeqCst), 2);
- }
}