Separate contained grid contributions from stretch
This commit is contained in:
@@ -5367,28 +5367,42 @@ fn effective_container_type(style: &crate::LayoutStyle) -> crate::ContainerType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does this auto inline-size use shrink-to-fit/content sizing rather than
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
/// filling a definite containing block?
|
enum ContainerAutoInlineSize {
|
||||||
fn container_auto_inline_size_is_intrinsic(
|
FillAvailable,
|
||||||
|
Intrinsic,
|
||||||
|
StretchedGridItem,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Classify how an auto inline-size is resolved. Stretched grid items need
|
||||||
|
/// their intrinsic contribution contained without replacing the final auto
|
||||||
|
/// size that stretch alignment consumes.
|
||||||
|
fn container_auto_inline_size(
|
||||||
tree: &DomTree,
|
tree: &DomTree,
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
style: &crate::LayoutStyle,
|
style: &crate::LayoutStyle,
|
||||||
styles: &HashMap<NodeId, crate::LayoutStyle>,
|
styles: &HashMap<NodeId, crate::LayoutStyle>,
|
||||||
) -> bool {
|
) -> ContainerAutoInlineSize {
|
||||||
if style.is_inline_block || style.float.is_some() {
|
if style.is_inline_block || style.float.is_some() {
|
||||||
return true;
|
return ContainerAutoInlineSize::Intrinsic;
|
||||||
}
|
}
|
||||||
if matches!(style.position, Some(taffy::Position::Absolute)) {
|
if matches!(style.position, Some(taffy::Position::Absolute)) {
|
||||||
// An absolutely positioned auto width is fill-available only when
|
// An absolutely positioned auto width is fill-available only when
|
||||||
// both inline insets are definite.
|
// both inline insets are definite.
|
||||||
return style.inset[1].is_none() || style.inset[3].is_none();
|
return if style.inset[1].is_none() || style.inset[3].is_none() {
|
||||||
|
ContainerAutoInlineSize::Intrinsic
|
||||||
|
} else {
|
||||||
|
ContainerAutoInlineSize::FillAvailable
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut parent = tree.get_node(id).and_then(|node| node.parent);
|
let mut parent = tree.get_node(id).and_then(|node| node.parent);
|
||||||
let parent_style = loop {
|
let parent_style = loop {
|
||||||
let Some(parent_id) = parent else { return false };
|
let Some(parent_id) = parent else {
|
||||||
|
return ContainerAutoInlineSize::FillAvailable;
|
||||||
|
};
|
||||||
let Some(parent_style) = styles.get(&parent_id) else {
|
let Some(parent_style) = styles.get(&parent_id) else {
|
||||||
return false;
|
return ContainerAutoInlineSize::FillAvailable;
|
||||||
};
|
};
|
||||||
if parent_style.display_contents {
|
if parent_style.display_contents {
|
||||||
parent = tree.get_node(parent_id).and_then(|node| node.parent);
|
parent = tree.get_node(parent_id).and_then(|node| node.parent);
|
||||||
@@ -5406,30 +5420,38 @@ fn container_auto_inline_size_is_intrinsic(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
if row {
|
if row {
|
||||||
true
|
ContainerAutoInlineSize::Intrinsic
|
||||||
|
} else if style
|
||||||
|
.align_self
|
||||||
|
.unwrap_or(
|
||||||
|
parent_style
|
||||||
|
.align_items
|
||||||
|
.unwrap_or(taffy::AlignItems::STRETCH),
|
||||||
|
)
|
||||||
|
== taffy::AlignSelf::STRETCH
|
||||||
|
{
|
||||||
|
ContainerAutoInlineSize::FillAvailable
|
||||||
} else {
|
} else {
|
||||||
style
|
ContainerAutoInlineSize::Intrinsic
|
||||||
.align_self
|
|
||||||
.unwrap_or(
|
|
||||||
parent_style
|
|
||||||
.align_items
|
|
||||||
.unwrap_or(taffy::AlignItems::STRETCH),
|
|
||||||
)
|
|
||||||
!= taffy::AlignSelf::STRETCH
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::Display::Grid => {
|
crate::Display::Grid => {
|
||||||
style
|
if style
|
||||||
.justify_self
|
.justify_self
|
||||||
.unwrap_or(
|
.unwrap_or(
|
||||||
parent_style
|
parent_style
|
||||||
.justify_items
|
.justify_items
|
||||||
.unwrap_or(taffy::JustifyItems::STRETCH),
|
.unwrap_or(taffy::JustifyItems::STRETCH),
|
||||||
)
|
)
|
||||||
!= taffy::AlignSelf::STRETCH
|
== taffy::AlignSelf::STRETCH
|
||||||
|
{
|
||||||
|
ContainerAutoInlineSize::StretchedGridItem
|
||||||
|
} else {
|
||||||
|
ContainerAutoInlineSize::Intrinsic
|
||||||
|
}
|
||||||
}
|
}
|
||||||
crate::Display::Inline => true,
|
crate::Display::Inline => ContainerAutoInlineSize::Intrinsic,
|
||||||
_ => false,
|
_ => ContainerAutoInlineSize::FillAvailable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5458,11 +5480,16 @@ fn apply_container_size_containment(
|
|||||||
}
|
}
|
||||||
let ratio_transfers_inline_size =
|
let ratio_transfers_inline_size =
|
||||||
style.aspect_ratio.is_some() && !matches!(style.height, crate::Dimension::Auto);
|
style.aspect_ratio.is_some() && !matches!(style.height, crate::Dimension::Auto);
|
||||||
if matches!(style.width, crate::Dimension::Auto)
|
if matches!(style.width, crate::Dimension::Auto) && !ratio_transfers_inline_size {
|
||||||
&& !ratio_transfers_inline_size
|
match container_auto_inline_size(tree, id, style, styles) {
|
||||||
&& container_auto_inline_size_is_intrinsic(tree, id, style, styles)
|
ContainerAutoInlineSize::Intrinsic => {
|
||||||
{
|
taffy_style.size.width = taffy::Dimension::length(0.0);
|
||||||
taffy_style.size.width = taffy::Dimension::length(0.0);
|
}
|
||||||
|
ContainerAutoInlineSize::StretchedGridItem => {
|
||||||
|
taffy_style.intrinsic_size_containment.width = true;
|
||||||
|
}
|
||||||
|
ContainerAutoInlineSize::FillAvailable => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if kind == crate::ContainerType::Size {
|
if kind == crate::ContainerType::Size {
|
||||||
|
|||||||
@@ -1090,6 +1090,63 @@ fn item_self_alignment_places_flex_and_grid_items() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stretched_inline_size_container_has_zero_intrinsic_grid_contribution() {
|
||||||
|
// Chromium 140: size containment affects intrinsic track sizing, but not
|
||||||
|
// the item's later stretch alignment. In the first case the grid therefore
|
||||||
|
// shrink-wraps to zero. In the definite-width control the contained item
|
||||||
|
// contributes zero, then stretches to the 50px left after the fixed track.
|
||||||
|
// Its 240px child remains visible overflow in both cases.
|
||||||
|
let tree = parse_html(
|
||||||
|
r#"
|
||||||
|
<style>
|
||||||
|
html, body { margin: 0 }
|
||||||
|
#shrink-host { display: flex; width: 600px }
|
||||||
|
.grid { display: grid }
|
||||||
|
.item { container-type: inline-size }
|
||||||
|
.child { width: 240px; height: 20px }
|
||||||
|
#fixed-grid {
|
||||||
|
display: grid;
|
||||||
|
width: 150px;
|
||||||
|
grid-template-columns: auto 100px;
|
||||||
|
}
|
||||||
|
#fixed { height: 20px }
|
||||||
|
</style>
|
||||||
|
<div id="shrink-host">
|
||||||
|
<div id="shrink-grid" class="grid">
|
||||||
|
<div id="shrink-item" class="item"><div id="shrink-child" class="child"></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="fixed-grid" class="grid">
|
||||||
|
<div id="fixed-item" class="item"><div id="fixed-child" class="child"></div></div>
|
||||||
|
<div id="fixed"></div>
|
||||||
|
</div>
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
let layout = layout_dom(&tree, (800.0, 600.0));
|
||||||
|
let rect = |id| layout.rects[&tree.get_element_by_id(id).unwrap()];
|
||||||
|
let shrink_grid = rect("shrink-grid");
|
||||||
|
let shrink_item = rect("shrink-item");
|
||||||
|
let shrink_child = rect("shrink-child");
|
||||||
|
let fixed_grid = rect("fixed-grid");
|
||||||
|
let fixed_item = rect("fixed-item");
|
||||||
|
let fixed_child = rect("fixed-child");
|
||||||
|
let fixed = rect("fixed");
|
||||||
|
assert!(
|
||||||
|
shrink_grid.width.abs() < 0.01
|
||||||
|
&& shrink_item.width.abs() < 0.01
|
||||||
|
&& (shrink_child.width - 240.0).abs() < 0.01
|
||||||
|
&& (fixed_grid.width - 150.0).abs() < 0.01
|
||||||
|
&& (fixed_item.width - 50.0).abs() < 0.01
|
||||||
|
&& (fixed_child.width - 240.0).abs() < 0.01
|
||||||
|
&& (fixed.x - 50.0).abs() < 0.01
|
||||||
|
&& (fixed.width - 100.0).abs() < 0.01,
|
||||||
|
"contained intrinsic contribution leaked into the grid track: \
|
||||||
|
shrink=({shrink_grid:?}, {shrink_item:?}, {shrink_child:?}) \
|
||||||
|
fixed=({fixed_grid:?}, {fixed_item:?}, {fixed_child:?}, {fixed:?})"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn non_stretched_auto_grid_item_shrink_wraps_inside_grid_area() {
|
fn non_stretched_auto_grid_item_shrink_wraps_inside_grid_area() {
|
||||||
let tree = parse_html(
|
let tree = parse_html(
|
||||||
|
|||||||
17
vendor/taffy/src/compute/grid/types/grid_item.rs
vendored
17
vendor/taffy/src/compute/grid/types/grid_item.rs
vendored
@@ -31,6 +31,8 @@ pub(in super::super) struct GridItem {
|
|||||||
/// Is it a compressible replaced element?
|
/// Is it a compressible replaced element?
|
||||||
/// https://drafts.csswg.org/css-sizing-3/#min-content-zero
|
/// https://drafts.csswg.org/css-sizing-3/#min-content-zero
|
||||||
pub is_compressible_replaced: bool,
|
pub is_compressible_replaced: bool,
|
||||||
|
/// Whether descendants are ignored for intrinsic size contributions in each axis.
|
||||||
|
pub intrinsic_size_containment: Size<bool>,
|
||||||
/// The item's overflow style
|
/// The item's overflow style
|
||||||
pub overflow: Point<Overflow>,
|
pub overflow: Point<Overflow>,
|
||||||
/// The item's box_sizing style
|
/// The item's box_sizing style
|
||||||
@@ -108,6 +110,7 @@ impl GridItem {
|
|||||||
row: row_span,
|
row: row_span,
|
||||||
column: col_span,
|
column: col_span,
|
||||||
is_compressible_replaced: style.is_compressible_replaced(),
|
is_compressible_replaced: style.is_compressible_replaced(),
|
||||||
|
intrinsic_size_containment: style.intrinsic_size_containment(),
|
||||||
overflow: style.overflow(),
|
overflow: style.overflow(),
|
||||||
box_sizing: style.box_sizing(),
|
box_sizing: style.box_sizing(),
|
||||||
size: style.size(),
|
size: style.size(),
|
||||||
@@ -425,6 +428,13 @@ impl GridItem {
|
|||||||
available_space: Size<Option<f32>>,
|
available_space: Size<Option<f32>>,
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
let known_dimensions = self.known_dimensions(tree, grid_area_size);
|
let known_dimensions = self.known_dimensions(tree, grid_area_size);
|
||||||
|
if self.intrinsic_size_containment.get(axis) {
|
||||||
|
let padding =
|
||||||
|
self.padding.resolve_or_zero(grid_area_size.width, |val, basis| tree.calc(val, basis));
|
||||||
|
let border =
|
||||||
|
self.border.resolve_or_zero(grid_area_size.width, |val, basis| tree.calc(val, basis));
|
||||||
|
return known_dimensions.get(axis).unwrap_or((padding + border).sum_axes().get(axis));
|
||||||
|
}
|
||||||
// The child sees the grid area as its containing block during intrinsic measurement, so
|
// The child sees the grid area as its containing block during intrinsic measurement, so
|
||||||
// percentage box properties resolve against the grid area when that size is definite.
|
// percentage box properties resolve against the grid area when that size is definite.
|
||||||
// Spec:
|
// Spec:
|
||||||
@@ -469,6 +479,13 @@ impl GridItem {
|
|||||||
available_space: Size<Option<f32>>,
|
available_space: Size<Option<f32>>,
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
let known_dimensions = self.known_dimensions(tree, grid_area_size);
|
let known_dimensions = self.known_dimensions(tree, grid_area_size);
|
||||||
|
if self.intrinsic_size_containment.get(axis) {
|
||||||
|
let padding =
|
||||||
|
self.padding.resolve_or_zero(grid_area_size.width, |val, basis| tree.calc(val, basis));
|
||||||
|
let border =
|
||||||
|
self.border.resolve_or_zero(grid_area_size.width, |val, basis| tree.calc(val, basis));
|
||||||
|
return known_dimensions.get(axis).unwrap_or((padding + border).sum_axes().get(axis));
|
||||||
|
}
|
||||||
// See the min-content path above. Max-content measurement uses the same containing-block
|
// See the min-content path above. Max-content measurement uses the same containing-block
|
||||||
// basis so percentage-dependent item geometry is measured from the grid area rather than
|
// basis so percentage-dependent item geometry is measured from the grid area rather than
|
||||||
// from the container.
|
// from the container.
|
||||||
|
|||||||
6
vendor/taffy/src/style/grid.rs
vendored
6
vendor/taffy/src/style/grid.rs
vendored
@@ -237,6 +237,12 @@ pub trait GridContainerStyle: CoreStyle {
|
|||||||
|
|
||||||
/// The set of styles required for a CSS Grid item (child of a CSS Grid container)
|
/// The set of styles required for a CSS Grid item (child of a CSS Grid container)
|
||||||
pub trait GridItemStyle: CoreStyle {
|
pub trait GridItemStyle: CoreStyle {
|
||||||
|
/// Whether descendants are ignored for intrinsic size contributions in each axis.
|
||||||
|
#[inline(always)]
|
||||||
|
fn intrinsic_size_containment(&self) -> Size<bool> {
|
||||||
|
Size { width: false, height: false }
|
||||||
|
}
|
||||||
|
|
||||||
/// Defines which row in the grid the item should start and end at
|
/// Defines which row in the grid the item should start and end at
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
|
fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
|
||||||
|
|||||||
15
vendor/taffy/src/style/mod.rs
vendored
15
vendor/taffy/src/style/mod.rs
vendored
@@ -442,6 +442,11 @@ pub struct Style<S: CheapCloneStr = DefaultCheapStr> {
|
|||||||
/// Is it a replaced element like an image or form field?
|
/// Is it a replaced element like an image or form field?
|
||||||
/// <https://drafts.csswg.org/css-sizing-3/#min-content-zero>
|
/// <https://drafts.csswg.org/css-sizing-3/#min-content-zero>
|
||||||
pub item_is_replaced: bool,
|
pub item_is_replaced: bool,
|
||||||
|
/// Whether descendants are ignored for intrinsic size contributions in each axis.
|
||||||
|
///
|
||||||
|
/// Ordinary final layout is unaffected, allowing an auto-sized grid item to
|
||||||
|
/// retain stretch alignment after its contained intrinsic contribution is removed.
|
||||||
|
pub intrinsic_size_containment: Size<bool>,
|
||||||
/// Should size styles apply to the content box or the border box of the node
|
/// Should size styles apply to the content box or the border box of the node
|
||||||
pub box_sizing: BoxSizing,
|
pub box_sizing: BoxSizing,
|
||||||
/// Sets the direction of text, table and grid columns, and horizontal overflow.
|
/// Sets the direction of text, table and grid columns, and horizontal overflow.
|
||||||
@@ -591,6 +596,7 @@ impl<S: CheapCloneStr> Style<S> {
|
|||||||
display: Display::DEFAULT,
|
display: Display::DEFAULT,
|
||||||
item_is_table: false,
|
item_is_table: false,
|
||||||
item_is_replaced: false,
|
item_is_replaced: false,
|
||||||
|
intrinsic_size_containment: Size { width: false, height: false },
|
||||||
box_sizing: BoxSizing::BorderBox,
|
box_sizing: BoxSizing::BorderBox,
|
||||||
direction: Direction::Ltr,
|
direction: Direction::Ltr,
|
||||||
overflow: Point { x: Overflow::Visible, y: Overflow::Visible },
|
overflow: Point { x: Overflow::Visible, y: Overflow::Visible },
|
||||||
@@ -1150,6 +1156,10 @@ impl<T: GridContainerStyle> GridContainerStyle for &'_ T {
|
|||||||
|
|
||||||
#[cfg(feature = "grid")]
|
#[cfg(feature = "grid")]
|
||||||
impl<S: CheapCloneStr> GridItemStyle for Style<S> {
|
impl<S: CheapCloneStr> GridItemStyle for Style<S> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn intrinsic_size_containment(&self) -> Size<bool> {
|
||||||
|
self.intrinsic_size_containment
|
||||||
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn grid_row(&self) -> Line<GridPlacement<S>> {
|
fn grid_row(&self) -> Line<GridPlacement<S>> {
|
||||||
// TODO: Investigate eliminating clone
|
// TODO: Investigate eliminating clone
|
||||||
@@ -1172,6 +1182,10 @@ impl<S: CheapCloneStr> GridItemStyle for Style<S> {
|
|||||||
|
|
||||||
#[cfg(feature = "grid")]
|
#[cfg(feature = "grid")]
|
||||||
impl<T: GridItemStyle> GridItemStyle for &'_ T {
|
impl<T: GridItemStyle> GridItemStyle for &'_ T {
|
||||||
|
#[inline(always)]
|
||||||
|
fn intrinsic_size_containment(&self) -> Size<bool> {
|
||||||
|
(*self).intrinsic_size_containment()
|
||||||
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
|
fn grid_row(&self) -> Line<GridPlacement<Self::CustomIdent>> {
|
||||||
(*self).grid_row()
|
(*self).grid_row()
|
||||||
@@ -1208,6 +1222,7 @@ mod tests {
|
|||||||
display: Default::default(),
|
display: Default::default(),
|
||||||
item_is_table: false,
|
item_is_table: false,
|
||||||
item_is_replaced: false,
|
item_is_replaced: false,
|
||||||
|
intrinsic_size_containment: Size { width: false, height: false },
|
||||||
box_sizing: Default::default(),
|
box_sizing: Default::default(),
|
||||||
#[cfg(feature = "float_layout")]
|
#[cfg(feature = "float_layout")]
|
||||||
float: Default::default(),
|
float: Default::default(),
|
||||||
|
|||||||
Reference in New Issue
Block a user