diff --git a/CHANGELOG.md b/CHANGELOG.md index c9c4a63f3..95bc498a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ In contexts where a keyword cannot be resolved it behaves as `auto` -- All built-in layout algorithms (flexbox, grid and block) now compute and output the *last baseline* of a container (`LayoutOutput::baselines.last`) in addition to its first baseline. A flex container's last baseline is generated from the last item of its cross-end-most line, a grid container's from the last row containing items, and a block container's from its last in-flow child with a baseline. Last baselines reported by children (e.g. by measure functions) are propagated up the tree, with scroll containers' baselines clamped to their border box. Note that last-baseline *alignment* (`align-items: last baseline`) is not yet supported +- All built-in layout algorithms (flexbox, grid and block) now compute and output the *last baseline* of a container (`LayoutOutput::baselines.last`) in addition to its first baseline. A flex container's last baseline is generated from the last item of its cross-end-most line (preferring items participating in last-baseline alignment), a grid container's from the last row containing items, and a block container's from its last in-flow child with a baseline. Last baselines reported by children (e.g. by measure functions) are propagated up the tree, with scroll containers' baselines clamped to their border box + +- Support for *last-baseline alignment* (CSS `align-items: last baseline` / `align-self: last baseline`) in flexbox and grid layout. `AlignItemsKeyword` gains a `LastBaseline` variant, with a corresponding `AlignItems::LAST_BASELINE` constant, `"LastBaseline"` serde representation and CSS parsing support for `last baseline` (and `first baseline` as an alias of `baseline`). Items with `align-self: last baseline` form a separate baseline group from first-baseline items: within a flex line or grid row their last baselines are aligned, with the group anchored towards the cross-end/block-end of the line or row. As with first-baseline alignment, flex items with `auto` cross-axis margins do not participate, missing baselines are synthesized from the item's border box, and scroll containers' baselines are clamped to their border box. In flex columns (where horizontal baselines cannot be aligned along the cross axis) last-baseline items fall back to being anchored to the cross-end edge of the line - `Dimension` also supports the `content` keyword (`Dimension::content()`, CSS `content`), which indicates an automatic size based on the box's content. This keyword is only valid for `flex-basis`, where it sizes the item based on its content (ignoring its main size property) when computing its flex base size. In any other context (e.g. `width`/`height`) it behaves as `auto` diff --git a/benches/src/yoga_helpers.rs b/benches/src/yoga_helpers.rs index dc520c5f3..bca08654b 100644 --- a/benches/src/yoga_helpers.rs +++ b/benches/src/yoga_helpers.rs @@ -155,7 +155,8 @@ fn items_into_align(align: Option) -> yg::Align { tf::AlignItemsKeyword::Start | tf::AlignItemsKeyword::End | tf::AlignItemsKeyword::SelfStart - | tf::AlignItemsKeyword::SelfEnd => unimplemented!(), + | tf::AlignItemsKeyword::SelfEnd + | tf::AlignItemsKeyword::LastBaseline => unimplemented!(), } } diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs index e2c42f30d..4c70ff2ed 100644 --- a/src/compute/flexbox.rs +++ b/src/compute/flexbox.rs @@ -117,11 +117,20 @@ impl FlexItem { self.overflow.x.is_scroll_container() | self.overflow.y.is_scroll_container() } - /// Returns true if the item participates in baseline alignment: it has `align-self: baseline` - /// and neither of its cross-axis margins are `auto`. + /// Returns true if the item participates in first-baseline alignment: it has + /// `align-self: baseline` and neither of its cross-axis margins are `auto`. /// See fn participates_in_baseline_alignment(&self, dir: FlexDirection) -> bool { - self.align_self == AlignSelf::BASELINE + self.align_self.keyword == AlignItemsKeyword::Baseline + && !self.margin_is_auto.cross_start(dir) + && !self.margin_is_auto.cross_end(dir) + } + + /// Returns true if the item participates in last-baseline alignment: it has + /// `align-self: last baseline` and neither of its cross-axis margins are `auto`. + /// See + fn participates_in_last_baseline_alignment(&self, dir: FlexDirection) -> bool { + self.align_self.keyword == AlignItemsKeyword::LastBaseline && !self.margin_is_auto.cross_start(dir) && !self.margin_is_auto.cross_end(dir) } @@ -526,10 +535,20 @@ fn compute_preliminary(tree: &mut impl LayoutFlexboxContainer, node: NodeId, inp }); // The container's last baseline is generated from the cross-end-most line (the first line for - // wrap-reverse containers). As no items ever participate in last-baseline alignment (which is not - // yet supported), it is always generated from the line's last flex item. + // wrap-reverse containers), preferring items which participate in last-baseline alignment and + // falling back to the line's last flex item. let last_line = if constants.is_wrap_reverse { flex_lines.first() } else { flex_lines.last() }; - let last_vertical_baseline = last_line.and_then(|line| line.items.last().map(|child| child.last_baseline)); + let last_vertical_baseline = last_line.and_then(|line| { + if constants.is_column { + line.items.last().map(|child| child.last_baseline) + } else { + line.items + .iter() + .find(|item| item.participates_in_last_baseline_alignment(constants.dir)) + .or_else(|| line.items.last()) + .map(|child| child.last_baseline) + } + }); LayoutOutput::from_sizes_and_baselines( constants.container_size, @@ -1838,16 +1857,23 @@ fn calculate_children_base_lines( } for line in flex_lines { - // If a flex line has one or zero items participating in baseline alignment then baseline alignment is a no-op so we skip - let line_baseline_child_count = + // If a baseline alignment group has one or zero items then baseline alignment is a no-op + // for those items so we skip measuring them + let line_first_baseline_child_count = line.items.iter().filter(|child| child.participates_in_baseline_alignment(constants.dir)).count(); - if line_baseline_child_count <= 1 { + let line_last_baseline_child_count = + line.items.iter().filter(|child| child.participates_in_last_baseline_alignment(constants.dir)).count(); + if line_first_baseline_child_count <= 1 && line_last_baseline_child_count <= 1 { continue; } for child in line.items.iter_mut() { // Only calculate baselines for children participating in baseline alignment - if !child.participates_in_baseline_alignment(constants.dir) { + let is_first_baseline = child.participates_in_baseline_alignment(constants.dir); + let is_last_baseline = child.participates_in_last_baseline_alignment(constants.dir); + let should_measure = (is_first_baseline && line_first_baseline_child_count > 1) + || (is_last_baseline && line_last_baseline_child_count > 1); + if !should_measure { continue; } @@ -1887,19 +1913,26 @@ fn calculate_children_base_lines( }, ); - let baseline = measured_size_and_baselines.baselines.first; let height = measured_size_and_baselines.size.height; // Scroll containers' baselines are determined from their content as if scrolled to the // initial position, but are additionally clamped to their border box. // See https://github.com/w3c/csswg-drafts/issues/7660 - let baseline = if child.overflow.y.is_scroll_container() { - baseline.unwrap_or(height).min(height).max(0.0) - } else { - baseline.unwrap_or(height) + let clamp_to_border_box = |baseline: f32| { + if child.overflow.y.is_scroll_container() { + baseline.min(height).max(0.0) + } else { + baseline + } }; - child.baseline = baseline + child.margin.top; + if is_first_baseline { + let baseline = clamp_to_border_box(measured_size_and_baselines.baselines.first.unwrap_or(height)); + child.baseline = baseline + child.margin.top; + } else { + let baseline = clamp_to_border_box(measured_size_and_baselines.baselines.last.unwrap_or(height)); + child.last_baseline = baseline + child.margin.top; + } } } } @@ -1939,12 +1972,20 @@ fn calculate_cross_size(flex_lines: &mut [FlexLine], node_size: Size // previous two steps and zero. for line in flex_lines.iter_mut() { let max_baseline: f32 = line.items.iter().map(|child| child.baseline).fold(0.0, |acc, x| acc.max(x)); + let max_last_baseline_descent: f32 = line + .items + .iter() + .filter(|child| child.participates_in_last_baseline_alignment(constants.dir)) + .map(|child| child.hypothetical_outer_size.cross(constants.dir) - child.last_baseline) + .fold(0.0, |acc, x| acc.max(x)); line.cross_size = line .items .iter() .map(|child| { if child.participates_in_baseline_alignment(constants.dir) { max_baseline - child.baseline + child.hypothetical_outer_size.cross(constants.dir) + } else if child.participates_in_last_baseline_alignment(constants.dir) { + child.last_baseline + max_last_baseline_descent } else { child.hypothetical_outer_size.cross(constants.dir) } @@ -2160,6 +2201,14 @@ fn resolve_cross_axis_auto_margins(flex_lines: &mut [FlexLine], constants: &Algo .filter(|child| child.participates_in_baseline_alignment(constants.dir)) .map(|child| child.outer_target_size.cross(constants.dir) - child.baseline) .fold(0.0, |acc, x| acc.max(x)); + let max_last_baseline: f32 = + line.items.iter_mut().map(|child| child.last_baseline).fold(0.0, |acc, x| acc.max(x)); + let max_last_baseline_descent: f32 = line + .items + .iter_mut() + .filter(|child| child.participates_in_last_baseline_alignment(constants.dir)) + .map(|child| child.outer_target_size.cross(constants.dir) - child.last_baseline) + .fold(0.0, |acc, x| acc.max(x)); for child in line.items.iter_mut() { let free_space = line_cross_size - child.outer_target_size.cross(constants.dir); @@ -2191,6 +2240,8 @@ fn resolve_cross_axis_auto_margins(flex_lines: &mut [FlexLine], constants: &Algo free_space, max_baseline, max_baseline_to_bottom_distance, + max_last_baseline, + max_last_baseline_descent, constants, ); } @@ -2205,11 +2256,14 @@ fn resolve_cross_axis_auto_margins(flex_lines: &mut [FlexLine], constants: &Algo /// - [**Align all flex items along the cross-axis**](https://www.w3.org/TR/css-flexbox-1/#algo-cross-align) per `align-self`, /// if neither of the item's cross-axis margins are `auto`. #[inline] +#[allow(clippy::too_many_arguments)] fn align_flex_items_along_cross_axis( child: &FlexItem, free_space: f32, max_baseline: f32, max_baseline_to_bottom_distance: f32, + max_last_baseline: f32, + max_last_baseline_descent: f32, constants: &AlgoConstants, ) -> f32 { let cross_axis_should_reverse = constants.is_column && matches!(constants.layout_direction, Direction::Rtl); @@ -2275,6 +2329,30 @@ fn align_flex_items_along_cross_axis( } } } + AlignItemsKeyword::LastBaseline => { + if constants.is_row { + if constants.is_wrap_reverse { + // In a wrap-reverse container the cross axis is flipped, so the last-baseline-aligned + // group of items is aligned to the cross-end edge, which is the top of the line. + max_last_baseline - child.last_baseline + } else { + let line_cross_size = free_space + child.outer_target_size.cross(constants.dir); + line_cross_size - max_last_baseline_descent - child.last_baseline + } + } else { + // Until we support vertical writing modes, baselines cannot be determined in + // columns, so items synthesize their baselines from their border boxes: their + // left edges are aligned, with the group anchored to the cross-end edge of the + // line (the fallback alignment for last-baseline groups). + let baseline_column_should_reverse = cross_axis_should_reverse && !constants.is_wrap; + let child_cross_size = child.outer_target_size.cross(constants.dir); + if constants.is_wrap_reverse ^ baseline_column_should_reverse { + 0.0 + } else { + free_space + child_cross_size - max_last_baseline_descent + } + } + } AlignItemsKeyword::Stretch => { if constants.is_wrap_reverse ^ cross_axis_should_reverse { free_space @@ -2929,14 +3007,20 @@ fn perform_absolute_layout_on_absolute_children( // `flex-start`/`flex-end` and the `stretch` fallback are flex-relative. let start_position = match cross_keyword { AlignItemsKeyword::Start | AlignItemsKeyword::Baseline => !cross_is_rtl, - AlignItemsKeyword::End => cross_is_rtl, + AlignItemsKeyword::End | AlignItemsKeyword::LastBaseline => cross_is_rtl, _ => true, }; match (cross_keyword, cross_axis_flex_start_reversed) { // Stretch alignment does not apply to absolutely positioned items // See "Example 3" at https://www.w3.org/TR/css-flexbox-1/#abspos-items // Note: Stretch should be FlexStart not Start when we support both - (AlignItemsKeyword::Start | AlignItemsKeyword::End | AlignItemsKeyword::Baseline, _) => { + ( + AlignItemsKeyword::Start + | AlignItemsKeyword::End + | AlignItemsKeyword::Baseline + | AlignItemsKeyword::LastBaseline, + _, + ) => { if start_position { constants.content_box_inset.cross_start(constants.dir) + resolved_margin.cross_start(constants.dir) diff --git a/src/compute/grid/alignment.rs b/src/compute/grid/alignment.rs index 890e3a8e0..1965f8b3d 100644 --- a/src/compute/grid/alignment.rs +++ b/src/compute/grid/alignment.rs @@ -90,7 +90,7 @@ pub(super) fn align_and_position_item( order: u32, grid_area: Rect, container_alignment_styles: InBothAbsAxis>, - baseline_shim: f32, + baseline_shims: Line, direction: Direction, container_border_box_width: f32, container_border: Rect, @@ -186,7 +186,7 @@ pub(super) fn align_and_position_item( let grid_area_minus_item_margins_size = Size { width: grid_area_size.width.maybe_sub(margin.left).maybe_sub(margin.right), - height: grid_area_size.height.maybe_sub(margin.top).maybe_sub(margin.bottom) - baseline_shim, + height: grid_area_size.height.maybe_sub(margin.top).maybe_sub(margin.bottom) - baseline_shims.sum(), }; // A size that is a sizing keyword (min-content, max-content, fit-content, @@ -359,7 +359,7 @@ pub(super) fn align_and_position_item( position, inset_horizontal, margin.horizontal_components(), - 0.0, + Line { start: 0.0, end: 0.0 }, direction, ); let (y, y_margin) = align_item_within_area( @@ -369,7 +369,7 @@ pub(super) fn align_and_position_item( position, inset_vertical, margin.vertical_components(), - baseline_shim, + baseline_shims, Direction::Ltr, ); @@ -438,11 +438,14 @@ pub(super) fn align_item_within_area( position: Position, inset: Line>, margin: Line>, - baseline_shim: f32, + baseline_shims: Line, direction: Direction, ) -> (f32, Line) { // Calculate grid area dimension in the axis - let non_auto_margin = Line { start: margin.start.unwrap_or(0.0) + baseline_shim, end: margin.end.unwrap_or(0.0) }; + let non_auto_margin = Line { + start: margin.start.unwrap_or(0.0) + baseline_shims.start, + end: margin.end.unwrap_or(0.0) + baseline_shims.end, + }; let grid_area_size = f32_max(grid_area.end - grid_area.start, 0.0); let free_space = f32_max(grid_area_size - resolved_size - non_auto_margin.sum(), 0.0); @@ -450,8 +453,8 @@ pub(super) fn align_item_within_area( let auto_margin_count = margin.start.is_none() as u8 + margin.end.is_none() as u8; let auto_margin_size = if auto_margin_count > 0 { free_space / auto_margin_count as f32 } else { 0.0 }; let resolved_margin = Line { - start: margin.start.unwrap_or(auto_margin_size) + baseline_shim, - end: margin.end.unwrap_or(auto_margin_size), + start: margin.start.unwrap_or(auto_margin_size) + baseline_shims.start, + end: margin.end.unwrap_or(auto_margin_size) + baseline_shims.end, }; let overflows = resolved_size + non_auto_margin.sum() > grid_area_size; @@ -459,7 +462,8 @@ pub(super) fn align_item_within_area( // Compute offset in the axis let alignment_based_offset = match alignment_keyword { - // TODO: Add support for baseline alignment. For now we treat it as "start". + // First-baseline aligned items are aligned to "start" with a baseline shim applied as + // an extra start-side margin (which aligns the baselines of the items in the group) AlignItemsKeyword::Start | AlignItemsKeyword::FlexStart | AlignItemsKeyword::Baseline @@ -470,7 +474,9 @@ pub(super) fn align_item_within_area( resolved_margin.start } } - AlignItemsKeyword::End | AlignItemsKeyword::FlexEnd => { + // Last-baseline aligned items are aligned to "end" with a baseline shim applied as + // an extra end-side margin (which aligns the baselines of the items in the group) + AlignItemsKeyword::End | AlignItemsKeyword::FlexEnd | AlignItemsKeyword::LastBaseline => { if direction.is_rtl() { resolved_margin.start } else { diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs index 737cfc413..041b7e5f7 100644 --- a/src/compute/grid/mod.rs +++ b/src/compute/grid/mod.rs @@ -2,7 +2,7 @@ //! use crate::geometry::{AbsoluteAxis, AbstractAxis, InBothAbsAxis}; use crate::geometry::{Line, Point, Rect, Size}; -use crate::style::{AlignItems, AvailableSpace, Overflow, Position}; +use crate::style::{AlignItems, AlignItemsKeyword, AvailableSpace, Overflow, Position}; use crate::tree::{Baselines, Layout, LayoutInput, LayoutOutput, LayoutPartialTreeExt, NodeId, RunMode, SizingMode}; use crate::util::debug::debug_log; use crate::util::sys::{f32_max, f32_min, GridTrackVec, Vec}; @@ -629,7 +629,7 @@ pub fn compute_grid_layout( index as u32, grid_area, container_alignment_styles, - item.baseline_shim, + item.baseline_shims, direction, container_border_box.width, border, @@ -776,7 +776,7 @@ pub fn compute_grid_layout( order, grid_area, container_alignment_styles, - 0.0, + Line { start: 0.0, end: 0.0 }, direction, container_border_box.width, border, @@ -844,19 +844,18 @@ pub fn compute_grid_layout( // Create a slice of all of the items start in this row (taking advantage of the fact that we have just sorted the array) let first_row_items = &items[0..].split(|item| item.row_indexes.start != first_row).next().unwrap(); - // Check if any items in *this row* participate in baseline alignment - // (items with an auto block-axis margin do not participate: https://www.w3.org/TR/css-align-3/#baseline-align-self) + // Prefer the first item in *this row* which participates in first-baseline alignment + // (items with an auto block-axis margin do not participate: https://www.w3.org/TR/css-align-3/#baseline-align-self), + // falling back to the row's first item let item = first_row_items .iter() - .find(|item| item.participates_in_baseline_alignment()) + .find(|item| item.participates_in_baseline_group(AlignItemsKeyword::Baseline)) .unwrap_or(&first_row_items[0]); Some(item.y_position + item.baseline.unwrap_or(item.height)) }; - // Determine the grid container's last baseline, generated from the last row containing items. - // As no items ever participate in last-baseline alignment (which is not yet supported), it is - // always generated from the row's first item. + // Determine the grid container's last baseline, generated from the last row containing items let grid_container_last_baseline: Option = if contain.suppresses_baseline() { None } else { @@ -869,7 +868,12 @@ pub fn compute_grid_layout( // Create a slice of all of the items that start in this row (taking advantage of the fact that the array is sorted) let last_row_items = &items[0..].rsplit(|item| item.row_indexes.start != last_row).next().unwrap(); - let item = &last_row_items[0]; + // Prefer the first item in *this row* which participates in last-baseline alignment, + // falling back to the row's first item + let item = last_row_items + .iter() + .find(|item| item.align_self.keyword == AlignItemsKeyword::LastBaseline) + .unwrap_or(&last_row_items[0]); Some(item.y_position + item.last_baseline.unwrap_or(item.height)) }; diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs index 0184dcb44..8823bc15c 100644 --- a/src/compute/grid/track_sizing.rs +++ b/src/compute/grid/track_sizing.rs @@ -2,7 +2,7 @@ //! use super::types::{GridItem, GridTrack, TrackCounts}; use crate::geometry::{AbstractAxis, Line, Size}; -use crate::style::{AlignContent, AlignContentKeyword, AvailableSpace}; +use crate::style::{AlignContent, AlignContentKeyword, AlignItemsKeyword, AvailableSpace}; use crate::style_helpers::TaffyMinContent; use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, SizingMode}; use crate::util::sys::{f32_max, f32_min, Vec}; @@ -490,17 +490,27 @@ fn resolve_item_baselines( row_items }; - // Count how many items in *this row* are baseline aligned - // If a row has one or zero items participating in baseline alignment then baseline alignment is a no-op - // for those items and we skip further computations for that row - let row_baseline_item_count = row_items.iter().filter(|item| item.participates_in_baseline_alignment()).count(); - if row_baseline_item_count <= 1 { + // Count how many items in *this row* participate in each baseline alignment group + // (items with an auto block-axis margin do not participate). + // If a group has one or zero items then baseline alignment is a no-op for those items + // and we skip further computations for that group + let row_first_baseline_item_count = + row_items.iter().filter(|item| item.participates_in_baseline_group(AlignItemsKeyword::Baseline)).count(); + let row_last_baseline_item_count = row_items + .iter() + .filter(|item| item.participates_in_baseline_group(AlignItemsKeyword::LastBaseline)) + .count(); + if row_first_baseline_item_count <= 1 && row_last_baseline_item_count <= 1 { continue; } // Compute the baselines of all items in the row participating in baseline alignment for item in row_items.iter_mut() { - if !item.participates_in_baseline_alignment() { + let is_first_baseline = item.participates_in_baseline_group(AlignItemsKeyword::Baseline); + let is_last_baseline = item.participates_in_baseline_group(AlignItemsKeyword::LastBaseline); + let should_measure = (is_first_baseline && row_first_baseline_item_count > 1) + || (is_last_baseline && row_last_baseline_item_count > 1); + if !should_measure { continue; } @@ -513,35 +523,63 @@ fn resolve_item_baselines( Line::FALSE, ); - let baseline = measured_size_and_baselines.baselines.first; let height = measured_size_and_baselines.size.height; // Scroll containers' baselines are determined from their content as if scrolled to the // initial position, but are additionally clamped to their border box. // See https://github.com/w3c/csswg-drafts/issues/7660 - let baseline = if item.overflow.y.is_scroll_container() { - baseline.unwrap_or(height).min(height).max(0.0) - } else { - baseline.unwrap_or(height) + let clamp_to_border_box = |baseline: f32| { + if item.overflow.y.is_scroll_container() { + baseline.min(height).max(0.0) + } else { + baseline + } }; - item.baseline = Some( - baseline + item.margin.top.resolve_or_zero(inner_node_size.width, |val, basis| tree.calc(val, basis)), - ); + if is_first_baseline { + let baseline = clamp_to_border_box(measured_size_and_baselines.baselines.first.unwrap_or(height)); + item.baseline = Some( + baseline + + item.margin.top.resolve_or_zero(inner_node_size.width, |val, basis| tree.calc(val, basis)), + ); + } else { + let baseline = clamp_to_border_box(measured_size_and_baselines.baselines.last.unwrap_or(height)); + // Store the item's last-baseline *descent*: the distance from its last baseline + // to the bottom of its margin box + item.last_baseline = Some( + (height - baseline) + + item.margin.bottom.resolve_or_zero(inner_node_size.width, |val, basis| tree.calc(val, basis)), + ); + } } - // Compute the max baseline of all items in the row participating in baseline alignment - let row_max_baseline = row_items - .iter() - .filter(|item| item.participates_in_baseline_alignment()) - .map(|item| item.baseline.unwrap_or(0.0)) - .max_by(|a, b| a.total_cmp(b)) - .unwrap(); + // Compute the max first-baseline ascent and shim each item in the first-baseline group + if row_first_baseline_item_count > 1 { + let row_max_baseline = row_items + .iter() + .filter(|item| item.participates_in_baseline_group(AlignItemsKeyword::Baseline)) + .map(|item| item.baseline.unwrap_or(0.0)) + .max_by(|a, b| a.total_cmp(b)) + .unwrap(); + for item in + row_items.iter_mut().filter(|item| item.participates_in_baseline_group(AlignItemsKeyword::Baseline)) + { + item.baseline_shims.start = row_max_baseline - item.baseline.unwrap_or(0.0); + } + } - // Compute the baseline shim for each item in the row participating in baseline alignment - for item in row_items.iter_mut() { - if item.participates_in_baseline_alignment() { - item.baseline_shim = row_max_baseline - item.baseline.unwrap_or(0.0); + // Compute the max last-baseline descent and shim each item in the last-baseline group + if row_last_baseline_item_count > 1 { + let row_max_descent = row_items + .iter() + .filter(|item| item.participates_in_baseline_group(AlignItemsKeyword::LastBaseline)) + .map(|item| item.last_baseline.unwrap_or(0.0)) + .max_by(|a, b| a.total_cmp(b)) + .unwrap(); + for item in + row_items.iter_mut().filter(|item| item.participates_in_baseline_group(AlignItemsKeyword::LastBaseline)) + { + item.baseline_shims.end = row_max_descent - item.last_baseline.unwrap_or(0.0); } } } diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs index 5ef1263f5..57a49d2df 100644 --- a/src/compute/grid/types/grid_item.rs +++ b/src/compute/grid/types/grid_item.rs @@ -4,7 +4,9 @@ use crate::compute::common::sizing_keyword::{resolve_sizing_keyword, SizingKeywo use crate::compute::grid::OriginZeroLine; use crate::geometry::AbstractAxis; use crate::geometry::{Line, Point, Rect, Size}; -use crate::style::{AlignItems, AlignSelf, AvailableSpace, Dimension, LengthPercentageAuto, Overflow}; +use crate::style::{ + AlignItems, AlignItemsKeyword, AlignSelf, AvailableSpace, Dimension, LengthPercentageAuto, Overflow, +}; use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, NodeId, SizingMode}; use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero}; use crate::{BoxSizing, GridItemStyle, LengthPercentage}; @@ -58,10 +60,14 @@ pub(in super::super) struct GridItem { pub baseline: Option, /// The item's last baseline (horizontal), measured from the top of its border box. /// Set from the item's final layout. Used to compute the container's last baseline. + /// During track sizing this field transiently stores the item's last-baseline *descent* + /// (the distance from its last baseline to the bottom of its margin box), which is used + /// to compute last-baseline shims. pub last_baseline: Option, - /// Shim for baseline alignment that acts like an extra top margin - /// TODO: Support last baseline and vertical text baselines - pub baseline_shim: f32, + /// Shims for baseline alignment: `start` acts like an extra top margin (first-baseline + /// alignment) and `end` like an extra bottom margin (last-baseline alignment) + /// TODO: Support vertical text baselines + pub baseline_shims: Line, /// The item's definite row-start and row-end (same as `row` field, except in a different coordinate system) /// (as indexes into the Vec stored in a grid's AbstractAxisTracks) @@ -125,7 +131,7 @@ impl GridItem { justify_self: style.justify_self().unwrap_or(parent_justify_items), baseline: None, last_baseline: None, - baseline_shim: 0.0, + baseline_shims: Line { start: 0.0, end: 0.0 }, row_indexes: Line { start: 0, end: 0 }, // Properly initialised later column_indexes: Line { start: 0, end: 0 }, // Properly initialised later crosses_flexible_row: false, // Properly initialised later @@ -146,7 +152,21 @@ impl GridItem { /// See #[inline(always)] pub fn participates_in_baseline_alignment(&self) -> bool { - self.align_self == AlignSelf::BASELINE && !self.margin.top.is_auto() && !self.margin.bottom.is_auto() + matches!(self.align_self.keyword, AlignItemsKeyword::Baseline | AlignItemsKeyword::LastBaseline) + && !self.has_auto_block_margin() + } + + /// Whether the item has an auto margin in the block axis + #[inline(always)] + pub fn has_auto_block_margin(&self) -> bool { + self.margin.top.is_auto() || self.margin.bottom.is_auto() + } + + /// Whether this item participates in the specified baseline alignment group + /// (items with an auto block-axis margin do not participate: ) + #[inline(always)] + pub fn participates_in_baseline_group(&self, group_keyword: AlignItemsKeyword) -> bool { + self.align_self.keyword == group_keyword && !self.has_auto_block_margin() } /// This item's placement in the specified axis in OriginZero coordinates @@ -449,8 +469,9 @@ impl GridItem { left: self.margin.left.resolve_or_zero(Some(0.0), |val, basis| tree.calc(val, basis)), right: self.margin.right.resolve_or_zero(Some(0.0), |val, basis| tree.calc(val, basis)), top: self.margin.top.resolve_or_zero(inner_node_width, |val, basis| tree.calc(val, basis)) - + self.baseline_shim, - bottom: self.margin.bottom.resolve_or_zero(inner_node_width, |val, basis| tree.calc(val, basis)), + + self.baseline_shims.start, + bottom: self.margin.bottom.resolve_or_zero(inner_node_width, |val, basis| tree.calc(val, basis)) + + self.baseline_shims.end, } .sum_axes() } diff --git a/src/style/alignment.rs b/src/style/alignment.rs index 6c80a0d12..1a506b42b 100644 --- a/src/style/alignment.rs +++ b/src/style/alignment.rs @@ -50,8 +50,10 @@ pub enum AlignItemsKeyword { SelfEnd, /// Items are packed along the center of the cross axis. Center, - /// Items are aligned such as their baselines align. + /// Items are aligned such as their first baselines align. Baseline, + /// Items are aligned such as their last baselines align. + LastBaseline, /// Stretch to fill the container. Stretch, } @@ -159,8 +161,10 @@ impl AlignItems { pub const SELF_END: Self = Self { keyword: AlignItemsKeyword::SelfEnd, safety: AlignmentSafety::Unsafe }; /// Items are packed along the center of the cross axis. pub const CENTER: Self = Self { keyword: AlignItemsKeyword::Center, safety: AlignmentSafety::Unsafe }; - /// Items are aligned such as their baselines align. + /// Items are aligned such as their first baselines align. pub const BASELINE: Self = Self { keyword: AlignItemsKeyword::Baseline, safety: AlignmentSafety::Unsafe }; + /// Items are aligned such as their last baselines align. + pub const LAST_BASELINE: Self = Self { keyword: AlignItemsKeyword::LastBaseline, safety: AlignmentSafety::Unsafe }; /// Stretch to fill the container. pub const STRETCH: Self = Self { keyword: AlignItemsKeyword::Stretch, safety: AlignmentSafety::Unsafe }; /// Like [`AlignItems::START`], but falls back to [`AlignItems::START`] when the @@ -275,6 +279,20 @@ impl FromCss for AlignItems { "self-end" => Ok(Self::SELF_END), "center" => Ok(Self::CENTER), "baseline" => Ok(Self::BASELINE), + "first" => { + let pos = input.expect_ident()?.clone(); + cssparser::match_ignore_ascii_case! { &*pos, + "baseline" => Ok(Self::BASELINE), + _ => Err(input.new_unexpected_token_error(Token::Ident(pos))), + } + }, + "last" => { + let pos = input.expect_ident()?.clone(); + cssparser::match_ignore_ascii_case! { &*pos, + "baseline" => Ok(Self::LAST_BASELINE), + _ => Err(input.new_unexpected_token_error(Token::Ident(pos))), + } + }, "stretch" => Ok(Self::STRETCH), _ => Err(input.new_unexpected_token_error(Token::Ident(first))), } @@ -438,6 +456,7 @@ const ALIGN_ITEMS_NAMES: &[&str] = &[ "SelfEnd", "Center", "Baseline", + "LastBaseline", "Stretch", "SafeStart", "SafeEnd", @@ -460,6 +479,7 @@ impl serde::Serialize for AlignItems { (AlignItemsKeyword::SelfEnd, AlignmentSafety::Unsafe) => "SelfEnd", (AlignItemsKeyword::Center, AlignmentSafety::Unsafe) => "Center", (AlignItemsKeyword::Baseline, _) => "Baseline", + (AlignItemsKeyword::LastBaseline, _) => "LastBaseline", (AlignItemsKeyword::Stretch, _) => "Stretch", (AlignItemsKeyword::Start, AlignmentSafety::Safe) => "SafeStart", (AlignItemsKeyword::End, AlignmentSafety::Safe) => "SafeEnd", @@ -492,6 +512,7 @@ impl<'de> serde::Deserialize<'de> for AlignItems { "SelfEnd" => AlignItems::SELF_END, "Center" => AlignItems::CENTER, "Baseline" => AlignItems::BASELINE, + "LastBaseline" => AlignItems::LAST_BASELINE, "Stretch" => AlignItems::STRETCH, "SafeStart" => AlignItems::SAFE_START, "SafeEnd" => AlignItems::SAFE_END, @@ -707,6 +728,9 @@ mod tests { assert_eq!("self-start".parse::().unwrap(), AlignItems::SELF_START); assert_eq!("self-end".parse::().unwrap(), AlignItems::SELF_END); assert_eq!("baseline".parse::().unwrap(), AlignItems::BASELINE); + assert_eq!("first baseline".parse::().unwrap(), AlignItems::BASELINE); + assert_eq!("last baseline".parse::().unwrap(), AlignItems::LAST_BASELINE); + assert_eq!("LAST Baseline".parse::().unwrap(), AlignItems::LAST_BASELINE); assert_eq!("stretch".parse::().unwrap(), AlignItems::STRETCH); } @@ -744,6 +768,10 @@ mod tests { fn parse_align_items_rejects_invalid_safe_combos() { assert!("safe stretch".parse::().is_err()); assert!("safe baseline".parse::().is_err()); + assert!("safe last baseline".parse::().is_err()); + assert!("last".parse::().is_err()); + assert!("last stretch".parse::().is_err()); + assert!("first".parse::().is_err()); assert!("safe space-between".parse::().is_err()); assert!("safe".parse::().is_err()); assert!("safe garbage".parse::().is_err()); @@ -800,6 +828,7 @@ mod tests { (AlignItems::FLEX_END, "\"FlexEnd\""), (AlignItems::CENTER, "\"Center\""), (AlignItems::BASELINE, "\"Baseline\""), + (AlignItems::LAST_BASELINE, "\"LastBaseline\""), (AlignItems::STRETCH, "\"Stretch\""), (AlignItems::SAFE_START, "\"SafeStart\""), (AlignItems::SAFE_END, "\"SafeEnd\""), diff --git a/test_fixtures/flex/align_last_baseline.html b/test_fixtures/flex/align_last_baseline.html new file mode 100644 index 000000000..b08ad2080 --- /dev/null +++ b/test_fixtures/flex/align_last_baseline.html @@ -0,0 +1,18 @@ + + + + + + + Test description + + + + +
+
+
+
+ + + diff --git a/test_fixtures/flex/align_last_baseline_child_auto_margin.html b/test_fixtures/flex/align_last_baseline_child_auto_margin.html new file mode 100644 index 000000000..d39c6f72d --- /dev/null +++ b/test_fixtures/flex/align_last_baseline_child_auto_margin.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+ + + diff --git a/test_fixtures/flex/align_last_baseline_column.html b/test_fixtures/flex/align_last_baseline_column.html new file mode 100644 index 000000000..5ffcbf948 --- /dev/null +++ b/test_fixtures/flex/align_last_baseline_column.html @@ -0,0 +1,18 @@ + + + + + + + Test description + + + + +
+
+
+
+ + + diff --git a/test_fixtures/flex/align_last_baseline_mixed.html b/test_fixtures/flex/align_last_baseline_mixed.html new file mode 100644 index 000000000..838137344 --- /dev/null +++ b/test_fixtures/flex/align_last_baseline_mixed.html @@ -0,0 +1,21 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+ + + diff --git a/test_fixtures/flex/align_last_baseline_nested_child.html b/test_fixtures/flex/align_last_baseline_nested_child.html new file mode 100644 index 000000000..440a9004d --- /dev/null +++ b/test_fixtures/flex/align_last_baseline_nested_child.html @@ -0,0 +1,21 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+ + + diff --git a/test_fixtures/flex/align_last_baseline_wrap_reverse.html b/test_fixtures/flex/align_last_baseline_wrap_reverse.html new file mode 100644 index 000000000..7cdcd12ba --- /dev/null +++ b/test_fixtures/flex/align_last_baseline_wrap_reverse.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+ + + diff --git a/test_fixtures/grid/grid_align_items_last_baseline.html b/test_fixtures/grid/grid_align_items_last_baseline.html new file mode 100644 index 000000000..76c05b334 --- /dev/null +++ b/test_fixtures/grid/grid_align_items_last_baseline.html @@ -0,0 +1,18 @@ + + + + + + + Test description + + + + +
+
+
+
+ + + diff --git a/test_fixtures/grid/grid_align_items_last_baseline_mixed.html b/test_fixtures/grid/grid_align_items_last_baseline_mixed.html new file mode 100644 index 000000000..58cbb77fd --- /dev/null +++ b/test_fixtures/grid/grid_align_items_last_baseline_mixed.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+ + + diff --git a/test_fixtures/grid/grid_align_items_last_baseline_nested_child.html b/test_fixtures/grid/grid_align_items_last_baseline_nested_child.html new file mode 100644 index 000000000..c6c3f0e38 --- /dev/null +++ b/test_fixtures/grid/grid_align_items_last_baseline_nested_child.html @@ -0,0 +1,21 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+ + + diff --git a/tests/xml/flex/align_last_baseline__border_box_ltr.xml b/tests/xml/flex/align_last_baseline__border_box_ltr.xml new file mode 100644 index 000000000..626426cf5 --- /dev/null +++ b/tests/xml/flex/align_last_baseline__border_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/align_last_baseline__border_box_rtl.xml b/tests/xml/flex/align_last_baseline__border_box_rtl.xml new file mode 100644 index 000000000..ec9af374f --- /dev/null +++ b/tests/xml/flex/align_last_baseline__border_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/align_last_baseline__content_box_ltr.xml b/tests/xml/flex/align_last_baseline__content_box_ltr.xml new file mode 100644 index 000000000..48b843e03 --- /dev/null +++ b/tests/xml/flex/align_last_baseline__content_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/align_last_baseline__content_box_rtl.xml b/tests/xml/flex/align_last_baseline__content_box_rtl.xml new file mode 100644 index 000000000..b70603d08 --- /dev/null +++ b/tests/xml/flex/align_last_baseline__content_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_child_auto_margin__border_box_ltr.xml b/tests/xml/flex/align_last_baseline_child_auto_margin__border_box_ltr.xml new file mode 100644 index 000000000..bfd124529 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_child_auto_margin__border_box_ltr.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_child_auto_margin__border_box_rtl.xml b/tests/xml/flex/align_last_baseline_child_auto_margin__border_box_rtl.xml new file mode 100644 index 000000000..69965a41a --- /dev/null +++ b/tests/xml/flex/align_last_baseline_child_auto_margin__border_box_rtl.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_child_auto_margin__content_box_ltr.xml b/tests/xml/flex/align_last_baseline_child_auto_margin__content_box_ltr.xml new file mode 100644 index 000000000..2dd4cb776 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_child_auto_margin__content_box_ltr.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_child_auto_margin__content_box_rtl.xml b/tests/xml/flex/align_last_baseline_child_auto_margin__content_box_rtl.xml new file mode 100644 index 000000000..ad1d5a9d4 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_child_auto_margin__content_box_rtl.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_column__border_box_ltr.xml b/tests/xml/flex/align_last_baseline_column__border_box_ltr.xml new file mode 100644 index 000000000..147ed6001 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_column__border_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_column__border_box_rtl.xml b/tests/xml/flex/align_last_baseline_column__border_box_rtl.xml new file mode 100644 index 000000000..c45e4d2ab --- /dev/null +++ b/tests/xml/flex/align_last_baseline_column__border_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_column__content_box_ltr.xml b/tests/xml/flex/align_last_baseline_column__content_box_ltr.xml new file mode 100644 index 000000000..096d08808 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_column__content_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_column__content_box_rtl.xml b/tests/xml/flex/align_last_baseline_column__content_box_rtl.xml new file mode 100644 index 000000000..d30b15477 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_column__content_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_mixed__border_box_ltr.xml b/tests/xml/flex/align_last_baseline_mixed__border_box_ltr.xml new file mode 100644 index 000000000..7b72354e1 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_mixed__border_box_ltr.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_mixed__border_box_rtl.xml b/tests/xml/flex/align_last_baseline_mixed__border_box_rtl.xml new file mode 100644 index 000000000..ef0be8fd2 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_mixed__border_box_rtl.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_mixed__content_box_ltr.xml b/tests/xml/flex/align_last_baseline_mixed__content_box_ltr.xml new file mode 100644 index 000000000..1c075d27f --- /dev/null +++ b/tests/xml/flex/align_last_baseline_mixed__content_box_ltr.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_mixed__content_box_rtl.xml b/tests/xml/flex/align_last_baseline_mixed__content_box_rtl.xml new file mode 100644 index 000000000..eb80a8959 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_mixed__content_box_rtl.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_nested_child__border_box_ltr.xml b/tests/xml/flex/align_last_baseline_nested_child__border_box_ltr.xml new file mode 100644 index 000000000..778889565 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_nested_child__border_box_ltr.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_nested_child__border_box_rtl.xml b/tests/xml/flex/align_last_baseline_nested_child__border_box_rtl.xml new file mode 100644 index 000000000..224679feb --- /dev/null +++ b/tests/xml/flex/align_last_baseline_nested_child__border_box_rtl.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_nested_child__content_box_ltr.xml b/tests/xml/flex/align_last_baseline_nested_child__content_box_ltr.xml new file mode 100644 index 000000000..c27a0dca5 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_nested_child__content_box_ltr.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_nested_child__content_box_rtl.xml b/tests/xml/flex/align_last_baseline_nested_child__content_box_rtl.xml new file mode 100644 index 000000000..fa3235280 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_nested_child__content_box_rtl.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_wrap_reverse__border_box_ltr.xml b/tests/xml/flex/align_last_baseline_wrap_reverse__border_box_ltr.xml new file mode 100644 index 000000000..4e407b377 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_wrap_reverse__border_box_ltr.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_wrap_reverse__border_box_rtl.xml b/tests/xml/flex/align_last_baseline_wrap_reverse__border_box_rtl.xml new file mode 100644 index 000000000..51e0e51e0 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_wrap_reverse__border_box_rtl.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_wrap_reverse__content_box_ltr.xml b/tests/xml/flex/align_last_baseline_wrap_reverse__content_box_ltr.xml new file mode 100644 index 000000000..cde252168 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_wrap_reverse__content_box_ltr.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/flex/align_last_baseline_wrap_reverse__content_box_rtl.xml b/tests/xml/flex/align_last_baseline_wrap_reverse__content_box_rtl.xml new file mode 100644 index 000000000..b2a4c2095 --- /dev/null +++ b/tests/xml/flex/align_last_baseline_wrap_reverse__content_box_rtl.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline__border_box_ltr.xml b/tests/xml/grid/grid_align_items_last_baseline__border_box_ltr.xml new file mode 100644 index 000000000..cd81afc24 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline__border_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline__border_box_rtl.xml b/tests/xml/grid/grid_align_items_last_baseline__border_box_rtl.xml new file mode 100644 index 000000000..fac2545fb --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline__border_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline__content_box_ltr.xml b/tests/xml/grid/grid_align_items_last_baseline__content_box_ltr.xml new file mode 100644 index 000000000..a55e8e3e2 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline__content_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline__content_box_rtl.xml b/tests/xml/grid/grid_align_items_last_baseline__content_box_rtl.xml new file mode 100644 index 000000000..39163208c --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline__content_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline_mixed__border_box_ltr.xml b/tests/xml/grid/grid_align_items_last_baseline_mixed__border_box_ltr.xml new file mode 100644 index 000000000..38084cc34 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline_mixed__border_box_ltr.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline_mixed__border_box_rtl.xml b/tests/xml/grid/grid_align_items_last_baseline_mixed__border_box_rtl.xml new file mode 100644 index 000000000..f26b651a0 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline_mixed__border_box_rtl.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline_mixed__content_box_ltr.xml b/tests/xml/grid/grid_align_items_last_baseline_mixed__content_box_ltr.xml new file mode 100644 index 000000000..e2a7079c7 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline_mixed__content_box_ltr.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline_mixed__content_box_rtl.xml b/tests/xml/grid/grid_align_items_last_baseline_mixed__content_box_rtl.xml new file mode 100644 index 000000000..3e37ea106 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline_mixed__content_box_rtl.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline_nested_child__border_box_ltr.xml b/tests/xml/grid/grid_align_items_last_baseline_nested_child__border_box_ltr.xml new file mode 100644 index 000000000..705c2ff17 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline_nested_child__border_box_ltr.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline_nested_child__border_box_rtl.xml b/tests/xml/grid/grid_align_items_last_baseline_nested_child__border_box_rtl.xml new file mode 100644 index 000000000..a4d4bdf99 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline_nested_child__border_box_rtl.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline_nested_child__content_box_ltr.xml b/tests/xml/grid/grid_align_items_last_baseline_nested_child__content_box_ltr.xml new file mode 100644 index 000000000..d10f1c593 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline_nested_child__content_box_ltr.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/grid/grid_align_items_last_baseline_nested_child__content_box_rtl.xml b/tests/xml/grid/grid_align_items_last_baseline_nested_child__content_box_rtl.xml new file mode 100644 index 000000000..96735db86 --- /dev/null +++ b/tests/xml/grid/grid_align_items_last_baseline_nested_child__content_box_rtl.xml @@ -0,0 +1,21 @@ + + + +
+
+
+
+
+
+
+ + + + + + + + + + + diff --git a/tests/xml/mod.rs b/tests/xml/mod.rs index cf9a35bbb..2f6cc4365 100644 --- a/tests/xml/mod.rs +++ b/tests/xml/mod.rs @@ -9137,6 +9137,126 @@ mod flex { crate::run_xml_test("flex", "align_items_stretch_min_cross__content_box_rtl"); } + #[test] + fn align_last_baseline__border_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline__border_box_ltr"); + } + + #[test] + fn align_last_baseline__content_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline__content_box_ltr"); + } + + #[test] + fn align_last_baseline__border_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline__border_box_rtl"); + } + + #[test] + fn align_last_baseline__content_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline__content_box_rtl"); + } + + #[test] + fn align_last_baseline_child_auto_margin__border_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_child_auto_margin__border_box_ltr"); + } + + #[test] + fn align_last_baseline_child_auto_margin__content_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_child_auto_margin__content_box_ltr"); + } + + #[test] + fn align_last_baseline_child_auto_margin__border_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_child_auto_margin__border_box_rtl"); + } + + #[test] + fn align_last_baseline_child_auto_margin__content_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_child_auto_margin__content_box_rtl"); + } + + #[test] + fn align_last_baseline_column__border_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_column__border_box_ltr"); + } + + #[test] + fn align_last_baseline_column__content_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_column__content_box_ltr"); + } + + #[test] + fn align_last_baseline_column__border_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_column__border_box_rtl"); + } + + #[test] + fn align_last_baseline_column__content_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_column__content_box_rtl"); + } + + #[test] + fn align_last_baseline_mixed__border_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_mixed__border_box_ltr"); + } + + #[test] + fn align_last_baseline_mixed__content_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_mixed__content_box_ltr"); + } + + #[test] + fn align_last_baseline_mixed__border_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_mixed__border_box_rtl"); + } + + #[test] + fn align_last_baseline_mixed__content_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_mixed__content_box_rtl"); + } + + #[test] + fn align_last_baseline_nested_child__border_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_nested_child__border_box_ltr"); + } + + #[test] + fn align_last_baseline_nested_child__content_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_nested_child__content_box_ltr"); + } + + #[test] + fn align_last_baseline_nested_child__border_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_nested_child__border_box_rtl"); + } + + #[test] + fn align_last_baseline_nested_child__content_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_nested_child__content_box_rtl"); + } + + #[test] + fn align_last_baseline_wrap_reverse__border_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_wrap_reverse__border_box_ltr"); + } + + #[test] + fn align_last_baseline_wrap_reverse__content_box_ltr() { + crate::run_xml_test("flex", "align_last_baseline_wrap_reverse__content_box_ltr"); + } + + #[test] + fn align_last_baseline_wrap_reverse__border_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_wrap_reverse__border_box_rtl"); + } + + #[test] + fn align_last_baseline_wrap_reverse__content_box_rtl() { + crate::run_xml_test("flex", "align_last_baseline_wrap_reverse__content_box_rtl"); + } + #[test] fn align_self_baseline__border_box_ltr() { crate::run_xml_test("flex", "align_self_baseline__border_box_ltr"); @@ -21327,6 +21447,78 @@ mod grid { crate::run_xml_test("grid", "grid_align_items_baseline_overflow_scroll__content_box_rtl"); } + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline__border_box_ltr() { + crate::run_xml_test("grid", "grid_align_items_last_baseline__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline__content_box_ltr() { + crate::run_xml_test("grid", "grid_align_items_last_baseline__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline__border_box_rtl() { + crate::run_xml_test("grid", "grid_align_items_last_baseline__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline__content_box_rtl() { + crate::run_xml_test("grid", "grid_align_items_last_baseline__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline_mixed__border_box_ltr() { + crate::run_xml_test("grid", "grid_align_items_last_baseline_mixed__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline_mixed__content_box_ltr() { + crate::run_xml_test("grid", "grid_align_items_last_baseline_mixed__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline_mixed__border_box_rtl() { + crate::run_xml_test("grid", "grid_align_items_last_baseline_mixed__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline_mixed__content_box_rtl() { + crate::run_xml_test("grid", "grid_align_items_last_baseline_mixed__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline_nested_child__border_box_ltr() { + crate::run_xml_test("grid", "grid_align_items_last_baseline_nested_child__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline_nested_child__content_box_ltr() { + crate::run_xml_test("grid", "grid_align_items_last_baseline_nested_child__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline_nested_child__border_box_rtl() { + crate::run_xml_test("grid", "grid_align_items_last_baseline_nested_child__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_align_items_last_baseline_nested_child__content_box_rtl() { + crate::run_xml_test("grid", "grid_align_items_last_baseline_nested_child__content_box_rtl"); + } + #[cfg(feature = "grid")] #[test] fn grid_align_items_sized_center__border_box_ltr() {