diff --git a/src/compute/grid/alignment.rs b/src/compute/grid/alignment.rs
index b9a701ffa..3cec376d6 100644
--- a/src/compute/grid/alignment.rs
+++ b/src/compute/grid/alignment.rs
@@ -9,6 +9,7 @@ use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero};
#[cfg(feature = "content_size")]
use crate::compute::common::content_size::compute_content_size_contribution;
+use crate::BoxSizing;
/// Align the grid tracks within the grid according to the align-content (rows) or
/// justify-content (columns) property. This only does anything if the size of the
@@ -79,14 +80,25 @@ pub(super) fn align_and_position_item(
let padding = style.padding.map(|p| p.resolve_or_zero(Some(grid_area_size.width)));
let border = style.border.map(|p| p.resolve_or_zero(Some(grid_area_size.width)));
let padding_border_size = (padding + border).sum_axes();
- let inherent_size = style.size.maybe_resolve(grid_area_size).maybe_apply_aspect_ratio(aspect_ratio);
+ let box_sizing_adjustment =
+ if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
+ let inherent_size = style
+ .size
+ .maybe_resolve(grid_area_size)
+ .maybe_apply_aspect_ratio(aspect_ratio)
+ .maybe_add(box_sizing_adjustment);
let min_size = style
.min_size
.maybe_resolve(grid_area_size)
+ .maybe_add(box_sizing_adjustment)
.or(padding_border_size.map(Some))
.maybe_max(padding_border_size)
.maybe_apply_aspect_ratio(aspect_ratio);
- let max_size = style.max_size.maybe_resolve(grid_area_size).maybe_apply_aspect_ratio(aspect_ratio);
+ let max_size = style
+ .max_size
+ .maybe_resolve(grid_area_size)
+ .maybe_apply_aspect_ratio(aspect_ratio)
+ .maybe_add(box_sizing_adjustment);
// Resolve default alignment styles if they are set on neither the parent or the node itself
// Note: if the child has a preferred aspect ratio but neither width or height are set, then the width is stretched
diff --git a/src/compute/grid/explicit_grid.rs b/src/compute/grid/explicit_grid.rs
index 76ff737bf..dbc450627 100644
--- a/src/compute/grid/explicit_grid.rs
+++ b/src/compute/grid/explicit_grid.rs
@@ -14,7 +14,7 @@ use num_traits::float::FloatCore;
/// Compute the number of rows and columns in the explicit grid
pub(crate) fn compute_explicit_grid_size_in_axis(
style: &Style,
- preferred_size: Size>,
+ inner_container_size: Size >,
axis: AbsoluteAxis,
) -> u16 {
// Load the grid-template-rows or grid-template-columns definition (depending on the axis)
@@ -92,20 +92,11 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
// Otherwise, if the grid container has a definite min size in the relevant axis:
// - then the number of repetitions is the smallest possible positive integer that fulfills that minimum requirement
// Otherwise, the specified track list repeats only once.
- let style_size = preferred_size.get_abs(axis);
- let style_min_size = style.min_size.get_abs(axis).into_option();
- let style_max_size = style.max_size.get_abs(axis).into_option();
-
- let outer_container_size = style_size.maybe_min(style_max_size).or(style_max_size).or(style_min_size);
- let inner_container_size = outer_container_size.map(|size| {
- let padding_sum = style.padding.resolve_or_zero(outer_container_size).grid_axis_sum(axis);
- let border_sum = style.border.resolve_or_zero(outer_container_size).grid_axis_sum(axis);
- size - padding_sum - border_sum
- });
- let size_is_maximum = style_size.is_some() || style_max_size.is_some();
+ let size_is_maximum =
+ style.size.get_abs(axis).into_option().is_some() || style.max_size.get_abs(axis).into_option().is_some();
// Determine the number of repetitions
- let num_repetitions: u16 = match inner_container_size {
+ let num_repetitions: u16 = match inner_container_size.get_abs(axis) {
None => 1,
Some(inner_container_size) => {
let parent_size = Some(inner_container_size);
@@ -352,9 +343,9 @@ mod test {
grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
..Default::default()
};
- let preferred_size = grid_style.size.map(|s| s.into_option());
- let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
- let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
+ let inner_container_size = Size { width: Some(120.0), height: Some(80.0) };
+ let width = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Horizontal);
+ let height = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Vertical);
assert_eq!(width, 3);
assert_eq!(height, 4);
}
@@ -369,9 +360,9 @@ mod test {
grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
..Default::default()
};
- let preferred_size = grid_style.size.map(|s| s.into_option());
- let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
- let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
+ let inner_container_size = Size { width: Some(140.0), height: Some(90.0) };
+ let width = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Horizontal);
+ let height = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Vertical);
assert_eq!(width, 4);
assert_eq!(height, 5);
}
@@ -457,9 +448,9 @@ mod test {
grid_template_rows: vec![repeat(AutoFill, vec![length(20.0)])],
..Default::default()
};
- let preferred_size = grid_style.size.map(|s| s.into_option());
- let width = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Horizontal);
- let height = compute_explicit_grid_size_in_axis(&grid_style, preferred_size, AbsoluteAxis::Vertical);
+ let inner_container_size = Size { width: Some(100.0), height: Some(80.0) };
+ let width = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Horizontal);
+ let height = compute_explicit_grid_size_in_axis(&grid_style, inner_container_size, AbsoluteAxis::Vertical);
assert_eq!(width, 5); // 40px horizontal padding
assert_eq!(height, 4); // 20px vertical padding
}
diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs
index 656613d34..2fc02e5bf 100644
--- a/src/compute/grid/mod.rs
+++ b/src/compute/grid/mod.rs
@@ -3,13 +3,13 @@
use crate::geometry::{AbsoluteAxis, AbstractAxis, InBothAbsAxis};
use crate::geometry::{Line, Point, Rect, Size};
use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, Display, Overflow, Position};
-use crate::style_helpers::*;
use crate::tree::{Layout, LayoutInput, LayoutOutput, RunMode, SizingMode};
use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId};
use crate::util::debug::debug_log;
use crate::util::sys::{f32_max, GridTrackVec, Vec};
use crate::util::MaybeMath;
use crate::util::{MaybeResolve, ResolveOrZero};
+use crate::{style_helpers::*, BoxSizing};
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;
@@ -38,28 +38,109 @@ mod util;
pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs;
- let get_child_styles_iter = |node| tree.child_ids(node).map(|child_node: NodeId| tree.get_style(child_node));
let style = tree.get_style(node).clone();
- let child_styles_iter = get_child_styles_iter(node);
+ // 1. Compute "available grid space"
+ // https://www.w3.org/TR/css-grid-1/#available-grid-space
+ let aspect_ratio = style.aspect_ratio;
+ let padding = style.padding.resolve_or_zero(parent_size.width);
+ let border = style.border.resolve_or_zero(parent_size.width);
+ let padding_border = padding + border;
+ let padding_border_size = padding_border.sum_axes();
+ let box_sizing_adjustment =
+ if style.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
+
+ let min_size = style
+ .min_size
+ .maybe_resolve(parent_size)
+ .maybe_apply_aspect_ratio(aspect_ratio)
+ .maybe_add(box_sizing_adjustment);
+ let max_size = style
+ .max_size
+ .maybe_resolve(parent_size)
+ .maybe_apply_aspect_ratio(aspect_ratio)
+ .maybe_add(box_sizing_adjustment);
let preferred_size = if inputs.sizing_mode == SizingMode::InherentSize {
- style.size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(style.aspect_ratio)
+ style
+ .size
+ .maybe_resolve(parent_size)
+ .maybe_apply_aspect_ratio(style.aspect_ratio)
+ .maybe_add(box_sizing_adjustment)
} else {
Size::NONE
};
- // 1. Resolve the explicit grid
+ // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`.
+ // However, the axis are switched (transposed) because a node that scrolls vertically needs
+ // *horizontal* space to be reserved for a scrollbar
+ let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow {
+ Overflow::Scroll => style.scrollbar_width,
+ _ => 0.0,
+ });
+ // TODO: make side configurable based on the `direction` property
+ let mut content_box_inset = padding_border;
+ content_box_inset.right += scrollbar_gutter.x;
+ content_box_inset.bottom += scrollbar_gutter.y;
+
+ let constrained_available_space = known_dimensions
+ .or(preferred_size)
+ .map(|size| size.map(AvailableSpace::Definite))
+ .unwrap_or(available_space)
+ .maybe_clamp(min_size, max_size)
+ .maybe_max(padding_border_size);
+
+ let available_grid_space = Size {
+ width: constrained_available_space
+ .width
+ .map_definite_value(|space| space - content_box_inset.horizontal_axis_sum()),
+ height: constrained_available_space
+ .height
+ .map_definite_value(|space| space - content_box_inset.vertical_axis_sum()),
+ };
+
+ let outer_node_size =
+ known_dimensions.or(preferred_size).maybe_clamp(min_size, max_size).maybe_max(padding_border_size);
+ let mut inner_node_size = Size {
+ width: outer_node_size.width.map(|space| space - content_box_inset.horizontal_axis_sum()),
+ height: outer_node_size.height.map(|space| space - content_box_inset.vertical_axis_sum()),
+ };
+
+ debug_log!("parent_size", dbg:parent_size);
+ debug_log!("outer_node_size", dbg:outer_node_size);
+ debug_log!("inner_node_size", dbg:inner_node_size);
+
+ if let (RunMode::ComputeSize, Some(width), Some(height)) = (run_mode, outer_node_size.width, outer_node_size.height)
+ {
+ return LayoutOutput::from_outer_size(Size { width, height });
+ }
+
+ let get_child_styles_iter = |node| tree.child_ids(node).map(|child_node: NodeId| tree.get_style(child_node));
+ let child_styles_iter = get_child_styles_iter(node);
+
+ // 2. Resolve the explicit grid
+
+ // This is very similar to the inner_node_size except if the inner_node_size is not definite but the node
+ // has a min- or max- size style then that will be used in it's place.
+ let auto_fit_container_size = outer_node_size
+ .or(max_size)
+ .or(min_size)
+ .maybe_clamp(min_size, max_size)
+ .maybe_max(padding_border_size)
+ .maybe_sub(content_box_inset.sum_axes());
+
// Exactly compute the number of rows and columns in the explicit grid.
- let explicit_col_count = compute_explicit_grid_size_in_axis(&style, preferred_size, AbsoluteAxis::Horizontal);
- let explicit_row_count = compute_explicit_grid_size_in_axis(&style, preferred_size, AbsoluteAxis::Vertical);
+ let explicit_col_count =
+ compute_explicit_grid_size_in_axis(&style, auto_fit_container_size, AbsoluteAxis::Horizontal);
+ let explicit_row_count =
+ compute_explicit_grid_size_in_axis(&style, auto_fit_container_size, AbsoluteAxis::Vertical);
- // 2. Implicit Grid: Estimate Track Counts
+ // 3. Implicit Grid: Estimate Track Counts
// Estimate the number of rows and columns in the implicit grid (= the entire grid)
// This is necessary as part of placement. Doing it early here is a perf optimisation to reduce allocations.
let (est_col_counts, est_row_counts) =
compute_grid_size_estimate(explicit_col_count, explicit_row_count, child_styles_iter);
- // 2. Grid Item Placement
+ // 4. Grid Item Placement
// Match items (children) to a definite grid position (row start/end and column start/end position)
let mut items = Vec::with_capacity(tree.child_count(node));
let mut cell_occupancy_matrix = CellOccupancyMatrix::with_track_counts(est_col_counts, est_row_counts);
@@ -82,7 +163,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
let final_col_counts = *cell_occupancy_matrix.track_counts(AbsoluteAxis::Horizontal);
let final_row_counts = *cell_occupancy_matrix.track_counts(AbsoluteAxis::Vertical);
- // 3. Initialize Tracks
+ // 5. Initialize Tracks
// Initialize (explicit and implicit) grid tracks (and gutters)
// This resolves the min and max track sizing functions for all tracks and gutters
let mut columns = GridTrackVec::new();
@@ -104,56 +185,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutPartialTree, node: NodeId, inpu
|row_index| cell_occupancy_matrix.row_is_occupied(row_index),
);
- // 4. Compute "available grid space"
- // https://www.w3.org/TR/css-grid-1/#available-grid-space
- let padding = style.padding.resolve_or_zero(parent_size.width);
- let border = style.border.resolve_or_zero(parent_size.width);
- let padding_border = padding + border;
- let padding_border_size = padding_border.sum_axes();
- let aspect_ratio = style.aspect_ratio;
- let min_size = style.min_size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio);
- let max_size = style.max_size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio);
- let size = preferred_size;
-
- // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`.
- // However, the axis are switched (transposed) because a node that scrolls vertically needs
- // *horizontal* space to be reserved for a scrollbar
- let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow {
- Overflow::Scroll => style.scrollbar_width,
- _ => 0.0,
- });
- // TODO: make side configurable based on the `direction` property
- let mut content_box_inset = padding_border;
- content_box_inset.right += scrollbar_gutter.x;
- content_box_inset.bottom += scrollbar_gutter.y;
-
- let constrained_available_space = known_dimensions
- .or(size)
- .map(|size| size.map(AvailableSpace::Definite))
- .unwrap_or(available_space)
- .maybe_clamp(min_size, max_size)
- .maybe_max(padding_border_size);
-
- let available_grid_space = Size {
- width: constrained_available_space
- .width
- .map_definite_value(|space| space - content_box_inset.horizontal_axis_sum()),
- height: constrained_available_space
- .height
- .map_definite_value(|space| space - content_box_inset.vertical_axis_sum()),
- };
-
- let outer_node_size = known_dimensions.or(size).maybe_clamp(min_size, max_size).maybe_max(padding_border_size);
- let mut inner_node_size = Size {
- width: outer_node_size.width.map(|space| space - content_box_inset.horizontal_axis_sum()),
- height: outer_node_size.height.map(|space| space - content_box_inset.vertical_axis_sum()),
- };
-
- debug_log!("parent_size", dbg:parent_size);
- debug_log!("outer_node_size", dbg:outer_node_size);
- debug_log!("inner_node_size", dbg:inner_node_size);
-
- // 5. Track Sizing
+ // 6. Track Sizing
// Convert grid placements in origin-zero coordinates to indexes into the GridTrack (rows and columns) vectors
// This computation is relatively trivial, but it requires the final number of negative (implicit) tracks in
diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs
index 8c33798a6..b77f7de02 100644
--- a/src/compute/grid/types/grid_item.rs
+++ b/src/compute/grid/types/grid_item.rs
@@ -9,6 +9,7 @@ use crate::style::{
};
use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId, SizingMode};
use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero};
+use crate::{BoxSizing, LengthPercentage};
use core::ops::Range;
/// Represents a single grid item
@@ -32,6 +33,8 @@ pub(in super::super) struct GridItem {
/// The item's overflow style
pub overflow: Point,
+ /// The item's box_sizing style
+ pub box_sizing: BoxSizing,
/// The item's size style
pub size: Size,
/// The item's min_size style
@@ -40,6 +43,10 @@ pub(in super::super) struct GridItem {
pub max_size: Size,
/// The item's aspect_ratio style
pub aspect_ratio: Option,
+ /// The item's padding style
+ pub padding: Rect,
+ /// The item's border style
+ pub border: Rect,
/// The item's margin style
pub margin: Rect,
/// The item's align_self property, or the parent's align_items property is not set
@@ -101,10 +108,13 @@ impl GridItem {
row: row_span,
column: col_span,
overflow: style.overflow,
+ box_sizing: style.box_sizing,
size: style.size,
min_size: style.min_size,
max_size: style.max_size,
aspect_ratio: style.aspect_ratio,
+ padding: style.padding,
+ border: style.border,
margin: style.margin,
align_self: style.align_self.unwrap_or(parent_align_items),
justify_self: style.justify_self.unwrap_or(parent_justify_items),
@@ -232,9 +242,26 @@ impl GridItem {
let margins = self.margins_axis_sums_with_baseline_shims(inner_node_size.width);
let aspect_ratio = self.aspect_ratio;
- let inherent_size = self.size.maybe_resolve(grid_area_size).maybe_apply_aspect_ratio(aspect_ratio);
- let min_size = self.min_size.maybe_resolve(grid_area_size).maybe_apply_aspect_ratio(aspect_ratio);
- let max_size = self.max_size.maybe_resolve(grid_area_size).maybe_apply_aspect_ratio(aspect_ratio);
+ let padding = self.padding.resolve_or_zero(grid_area_size);
+ let border = self.border.resolve_or_zero(grid_area_size);
+ let padding_border_size = (padding + border).sum_axes();
+ let box_sizing_adjustment =
+ if self.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
+ let inherent_size = self
+ .size
+ .maybe_resolve(grid_area_size)
+ .maybe_apply_aspect_ratio(aspect_ratio)
+ .maybe_add(box_sizing_adjustment);
+ let min_size = self
+ .min_size
+ .maybe_resolve(grid_area_size)
+ .maybe_apply_aspect_ratio(aspect_ratio)
+ .maybe_add(box_sizing_adjustment);
+ let max_size = self
+ .max_size
+ .maybe_resolve(grid_area_size)
+ .maybe_apply_aspect_ratio(aspect_ratio)
+ .maybe_add(box_sizing_adjustment);
let grid_area_minus_item_margins_size = grid_area_size.maybe_sub(margins);
@@ -430,13 +457,23 @@ impl GridItem {
known_dimensions: Size>,
inner_node_size: Size >,
) -> f32 {
+ let padding = self.padding.resolve_or_zero(inner_node_size);
+ let border = self.border.resolve_or_zero(inner_node_size);
+ let padding_border_size = (padding + border).sum_axes();
+ let box_sizing_adjustment =
+ if self.box_sizing == BoxSizing::ContentBox { padding_border_size } else { Size::ZERO };
let size = self
.size
.maybe_resolve(inner_node_size)
.maybe_apply_aspect_ratio(self.aspect_ratio)
+ .maybe_add(box_sizing_adjustment)
.get(axis)
.or_else(|| {
- self.min_size.maybe_resolve(inner_node_size).maybe_apply_aspect_ratio(self.aspect_ratio).get(axis)
+ self.min_size
+ .maybe_resolve(inner_node_size)
+ .maybe_apply_aspect_ratio(self.aspect_ratio)
+ .maybe_add(box_sizing_adjustment)
+ .get(axis)
})
.or_else(|| self.overflow.get(axis).maybe_into_automatic_min_size())
.unwrap_or_else(|| {