Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
- An out-of-flow box's insets and percentage sizes resolve against its containing block's padding box, and with `auto` insets it falls back to its *static position*: where it would have been placed in its DOM parent's normal flow (including flex/grid alignment), expressed relative to the containing block
- Out-of-flow boxes contribute to the *scrollable overflow* of their containing block rather than their DOM parent
- `LayoutOutput` is no longer `Copy` (it now carries the list of out-of-flow candidates bubbling towards their containing block, which is also preserved in the layout cache so cache hits at intermediate nodes still re-propagate hoisted descendants)
- `LayoutPartialTree` gains `set_hoisted_children`/`add_hoisted_children` methods and `RoundTree` gains `is_hoisted`/`hoisted_child_count`/`get_hoisted_child_id` methods, which custom tree implementations must implement: each containing block records the out-of-flow boxes it laid out, and `round_layout` skips hoisted boxes when recursing through DOM children and instead visits them via their containing block (so rounding is applied against the containing block's cumulative unrounded position)
- `LayoutContainingBlock` gains `set_hoisted_children`/`add_hoisted_children` methods and `RoundTree` gains `is_hoisted`/`hoisted_child_count`/`get_hoisted_child_id` methods, which custom tree implementations must implement: each containing block records the out-of-flow boxes it laid out, and `round_layout` skips hoisted boxes when recursing through DOM children and instead visits them via their containing block (so rounding is applied against the containing block's cumulative unrounded position)

- An out-of-flow box whose containing block is a grid container is now positioned relative to the grid area determined by its grid-placement properties (falling back to the containing block's padding box for `auto` placement), matching CSS. This applies to any such box — including boxes that are not direct children of the grid container — and is resolved at final positioning time from the containing block's stored detailed grid info. Supporting changes:
- New `OofItemStyle: CoreStyle` style trait with (defaulted, `grid`-feature-gated) `grid_row`/`grid_column` methods, so grid placement can be read for any out-of-flow box at positioning time
- New `LayoutContainingBlock: LayoutPartialTree` tree trait with required `get_oof_item_style` (returning the associated `OofItemStyle` type for an out-of-flow box being positioned), `set_hoisted_children` and `add_hoisted_children` methods (moved from `LayoutPartialTree`) and a defaulted `get_detailed_layout_info` method which the out-of-flow positioning pass uses to read back the containing block's detailed layout info. The default implementation returns `DetailedLayoutInfo::None`, in which case such boxes are positioned relative to the containing block's padding box instead of their grid area. This trait is required by all container layout algorithms (`compute_block_layout`, `compute_flexbox_layout`, `compute_grid_layout` and `compute_root_layout`), so custom tree implementations using these must implement it
- `DetailedLayoutInfo`, `DetailedGridInfo` and the other detailed grid info types are no longer gated behind the `detailed_layout_info` cargo feature (which is retained but no longer gates anything), and `DetailedLayoutInfo` is now generic over the custom identifier string type. Grid layout now always records detailed grid info via `LayoutGridContainer::set_detailed_grid_info` (also no longer feature-gated)

### Fixed

Expand Down
31 changes: 21 additions & 10 deletions examples/custom_tree_owned_partial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,6 @@ impl taffy::LayoutPartialTree for Node {
self.node_from_id_mut(node_id).layout = *layout
}

fn set_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
let vec = &mut self.node_from_id_mut(node_id).hoisted_children;
vec.clear();
vec.extend_from_slice(hoisted);
}

fn add_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
self.node_from_id_mut(node_id).hoisted_children.extend_from_slice(hoisted);
}

fn resolve_calc_value(&self, _val: *const (), _basis: f32) -> f32 {
0.0
}
Expand Down Expand Up @@ -198,6 +188,27 @@ impl taffy::LayoutPartialTree for Node {
}
}

impl taffy::LayoutContainingBlock for Node {
type OofItemStyle<'a>
= &'a Style
where
Self: 'a;

fn get_oof_item_style(&self, node_id: NodeId) -> Self::OofItemStyle<'_> {
&self.node_from_id(node_id).style
}

fn set_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
let vec = &mut self.node_from_id_mut(node_id).hoisted_children;
vec.clear();
vec.extend_from_slice(hoisted);
}

fn add_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
self.node_from_id_mut(node_id).hoisted_children.extend_from_slice(hoisted);
}
}

impl CacheTree for Node {
fn cache_get(&mut self, node_id: NodeId, inputs: &taffy::LayoutInput) -> Option<taffy::LayoutOutput> {
self.node_from_id_mut(node_id).cache.get(inputs)
Expand Down
33 changes: 22 additions & 11 deletions examples/custom_tree_owned_unsafe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use taffy::tree::Cache;
use taffy::util::print_tree;
use taffy::{
compute_cached_layout, compute_flexbox_layout, compute_grid_layout, compute_leaf_layout, compute_root_layout,
prelude::*, round_layout, CacheTree,
prelude::*, round_layout, CacheTree, LayoutContainingBlock,
};

#[derive(Debug, Copy, Clone)]
Expand Down Expand Up @@ -148,16 +148,6 @@ impl LayoutPartialTree for StatelessLayoutTree {
unsafe { node_from_id_mut(node_id).unrounded_layout = *layout };
}

fn set_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
let vec = unsafe { &mut node_from_id_mut(node_id).hoisted_children };
vec.clear();
vec.extend_from_slice(hoisted);
}

fn add_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
unsafe { node_from_id_mut(node_id).hoisted_children.extend_from_slice(hoisted) };
}

fn resolve_calc_value(&self, _val: *const (), _basis: f32) -> f32 {
0.0
}
Expand Down Expand Up @@ -196,6 +186,27 @@ impl LayoutPartialTree for StatelessLayoutTree {
}
}

impl LayoutContainingBlock for StatelessLayoutTree {
type OofItemStyle<'a>
= &'a Style
where
Self: 'a;

fn get_oof_item_style(&self, node_id: NodeId) -> Self::OofItemStyle<'_> {
unsafe { &node_from_id(node_id).style }
}

fn set_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
let vec = unsafe { &mut node_from_id_mut(node_id).hoisted_children };
vec.clear();
vec.extend_from_slice(hoisted);
}

fn add_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
unsafe { node_from_id_mut(node_id).hoisted_children.extend_from_slice(hoisted) };
}
}

impl CacheTree for StatelessLayoutTree {
fn cache_get(&mut self, node_id: NodeId, inputs: &taffy::LayoutInput) -> Option<taffy::LayoutOutput> {
unsafe { node_from_id_mut(node_id) }.cache.get(inputs)
Expand Down
31 changes: 21 additions & 10 deletions examples/custom_tree_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,6 @@ impl taffy::LayoutPartialTree for Tree {
self.node_from_id_mut(node_id).unrounded_layout = *layout;
}

fn set_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
let vec = &mut self.node_from_id_mut(node_id).hoisted_children;
vec.clear();
vec.extend_from_slice(hoisted);
}

fn add_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
self.node_from_id_mut(node_id).hoisted_children.extend_from_slice(hoisted);
}

fn resolve_calc_value(&self, _val: *const (), _basis: f32) -> f32 {
0.0
}
Expand Down Expand Up @@ -204,6 +194,27 @@ impl taffy::LayoutPartialTree for Tree {
}
}

impl taffy::LayoutContainingBlock for Tree {
type OofItemStyle<'a>
= &'a Style
where
Self: 'a;

fn get_oof_item_style(&self, node_id: NodeId) -> Self::OofItemStyle<'_> {
&self.node_from_id(node_id).style
}

fn set_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
let vec = &mut self.node_from_id_mut(node_id).hoisted_children;
vec.clear();
vec.extend_from_slice(hoisted);
}

fn add_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
self.node_from_id_mut(node_id).hoisted_children.extend_from_slice(hoisted);
}
}

impl CacheTree for Tree {
fn cache_get(&mut self, node_id: NodeId, inputs: &taffy::LayoutInput) -> Option<taffy::LayoutOutput> {
self.node_from_id_mut(node_id).cache.get(inputs)
Expand Down
5 changes: 3 additions & 2 deletions src/compute/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ struct BlockItem {

/// Computes the layout of [`LayoutPartialTree`] according to the block layout algorithm
pub fn compute_block_layout(
tree: &mut impl LayoutBlockContainer,
tree: &mut (impl LayoutBlockContainer + crate::tree::LayoutContainingBlock),
node_id: NodeId,
inputs: LayoutInput,
block_ctx: Option<&mut BlockContext<'_>>,
Expand Down Expand Up @@ -454,7 +454,7 @@ pub fn compute_block_layout(

/// Computes the layout of [`LayoutBlockContainer`] according to the block layout algorithm
fn compute_inner(
tree: &mut impl LayoutBlockContainer,
tree: &mut (impl LayoutBlockContainer + crate::tree::LayoutContainingBlock),
node_id: NodeId,
inputs: LayoutInput,
#[allow(unused_mut)] mut block_ctx: &mut BlockContext<'_>,
Expand Down Expand Up @@ -782,6 +782,7 @@ fn compute_inner(
let mut unclaimed = OofCandidates::new();
let absolute_overflow_rect = perform_oof_layout(
tree,
node_id,
candidates,
absolute_position_area,
absolute_position_offset,
Expand Down
9 changes: 7 additions & 2 deletions src/compute/flexbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ impl AlgoConstants {

/// Computes the layout of a box according to the flexbox algorithm
pub fn compute_flexbox_layout(
tree: &mut impl LayoutFlexboxContainer,
tree: &mut (impl LayoutFlexboxContainer + crate::tree::LayoutContainingBlock),
node: NodeId,
inputs: LayoutInput,
) -> LayoutOutput {
Expand Down Expand Up @@ -321,7 +321,11 @@ pub fn compute_flexbox_layout(
}

/// Compute a preliminary size for an item
fn compute_preliminary(tree: &mut impl LayoutFlexboxContainer, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
fn compute_preliminary(
tree: &mut (impl LayoutFlexboxContainer + crate::tree::LayoutContainingBlock),
node: NodeId,
inputs: LayoutInput,
) -> LayoutOutput {
let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs;

// Define some general constants we will need for the remainder of the algorithm.
Expand Down Expand Up @@ -504,6 +508,7 @@ fn compute_preliminary(tree: &mut impl LayoutFlexboxContainer, node: NodeId, inp
let mut unclaimed = OofCandidates::new();
let absolute_overflow_rect = perform_oof_layout(
tree,
node,
candidates,
absolute_position_area,
absolute_position_offset,
Expand Down
58 changes: 22 additions & 36 deletions src/compute/grid/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,12 @@ use track_sizing::{
};
use types::{CellOccupancyMatrix, GridTrack, NamedLineResolver};

#[cfg(feature = "detailed_layout_info")]
use crate::sys::{DefaultCheapStr, String};
#[cfg(feature = "detailed_layout_info")]
use crate::{CheapCloneStr, GridPlacement};
#[cfg(feature = "detailed_layout_info")]
use types::{GridItem, GridTrackKind, TrackCounts};

pub(crate) use types::{GridCoordinate, GridLine, OriginZeroLine, MAX_GRID_TRACKS, MAX_OZ_LINE, MIN_OZ_LINE};

#[cfg(feature = "detailed_layout_info")]
pub use types::{GridLineNames, GridLineNamesIter};

mod alignment;
Expand All @@ -51,7 +47,7 @@ mod util;
/// - Placing items (which also resolves the implicit grid)
/// - Track (row/column) sizing
/// - Alignment & Final item placement
pub fn compute_grid_layout<Tree: LayoutGridContainer>(
pub fn compute_grid_layout<Tree: LayoutGridContainer + crate::tree::LayoutContainingBlock>(
tree: &mut Tree,
node: NodeId,
inputs: LayoutInput,
Expand Down Expand Up @@ -232,9 +228,7 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
name_resolver.set_explicit_row_count(explicit_row_count);

// Build the per-line names of the explicit grid from the name resolver's collected pairs
#[cfg(feature = "detailed_layout_info")]
let mut detailed_column_line_names = name_resolver.detailed_line_names(AbsoluteAxis::Horizontal);
#[cfg(feature = "detailed_layout_info")]
let mut detailed_row_line_names = name_resolver.detailed_line_names(AbsoluteAxis::Vertical);

// 3. Implicit Grid: Estimate Track Counts
Expand Down Expand Up @@ -903,9 +897,30 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
};
let absolute_position_area = container_border_box - absolute_position_inset.sum_axes();
let absolute_position_offset = Point { x: absolute_position_inset.left, y: absolute_position_inset.top };
// Store the detailed grid info before the out-of-flow positioning pass so that the pass can
// resolve the grid areas of out-of-flow boxes whose containing block is this grid
name_resolver.populate_detailed_line_resolvers(&mut detailed_row_line_names, &mut detailed_column_line_names);
tree.set_detailed_grid_info(
node,
DetailedGridInfo {
rows: DetailedGridTracksInfo::from_grid_tracks_and_track_count(
final_row_counts,
rows,
detailed_row_line_names,
),
columns: DetailedGridTracksInfo::from_grid_tracks_and_track_count(
final_col_counts,
columns,
detailed_column_line_names,
),
items: items.iter().map(DetailedGridItemsInfo::from_grid_item).collect(),
},
);

let mut unclaimed = OofCandidates::new();
let oof_overflow_rect = perform_oof_layout(
tree,
node,
direct_oof_candidates,
absolute_position_area,
absolute_position_offset,
Expand All @@ -925,28 +940,6 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
#[cfg(not(feature = "content_size"))]
let _ = oof_overflow_rect;

#[cfg(feature = "detailed_layout_info")]
name_resolver.populate_detailed_line_resolvers(&mut detailed_row_line_names, &mut detailed_column_line_names);

// Set detailed grid information
#[cfg(feature = "detailed_layout_info")]
tree.set_detailed_grid_info(
node,
DetailedGridInfo {
rows: DetailedGridTracksInfo::from_grid_tracks_and_track_count(
final_row_counts,
rows,
detailed_row_line_names,
),
columns: DetailedGridTracksInfo::from_grid_tracks_and_track_count(
final_col_counts,
columns,
detailed_column_line_names,
),
items: items.iter().map(DetailedGridItemsInfo::from_grid_item).collect(),
},
);

// If there are no in-flow items then return the container size and the overflow
// contributed by absolutely positioned children (no baseline)
if items.is_empty() {
Expand Down Expand Up @@ -1015,7 +1008,6 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(

/// Information from the computation of grid
#[derive(Debug, Clone, PartialEq)]
#[cfg(feature = "detailed_layout_info")]
pub struct DetailedGridInfo<S: CheapCloneStr = DefaultCheapStr> {
/// <https://drafts.csswg.org/css-grid-1/#grid-row>
pub rows: DetailedGridTracksInfo<S>,
Expand All @@ -1025,7 +1017,6 @@ pub struct DetailedGridInfo<S: CheapCloneStr = DefaultCheapStr> {
pub items: Vec<DetailedGridItemsInfo>,
}

#[cfg(feature = "detailed_layout_info")]
impl<S: CheapCloneStr> DetailedGridTracksInfo<S> {
/// Resolve an absolute placement in this axis to physical start and end coordinates
fn resolve_absolute_grid_axis(
Expand Down Expand Up @@ -1079,7 +1070,6 @@ impl<S: CheapCloneStr> DetailedGridTracksInfo<S> {
}
}

#[cfg(feature = "detailed_layout_info")]
impl<S: CheapCloneStr> DetailedGridInfo<S> {
/// Write the used row track sizes and line names to the passed writer in the resolved value
/// format of the `grid-template-rows` property
Expand Down Expand Up @@ -1148,7 +1138,6 @@ impl<S: CheapCloneStr> DetailedGridInfo<S> {

/// Information from the computation of grids tracks
#[derive(Debug, Clone, PartialEq)]
#[cfg(feature = "detailed_layout_info")]
pub struct DetailedGridTracksInfo<S: CheapCloneStr = DefaultCheapStr> {
/// Number of leading implicit grid tracks
pub negative_implicit_tracks: u16,
Expand All @@ -1174,7 +1163,6 @@ pub struct DetailedGridTracksInfo<S: CheapCloneStr = DefaultCheapStr> {
pub line_names: GridLineNames<S>,
}

#[cfg(feature = "detailed_layout_info")]
impl<S: CheapCloneStr> DetailedGridTracksInfo<S> {
/// Get the start and end position of each track relative to the grid container's border box
fn positions_from_grid_track_layout(grid_tracks: &[GridTrack]) -> Vec<Line<f32>> {
Expand Down Expand Up @@ -1287,7 +1275,6 @@ impl<S: CheapCloneStr> DetailedGridTracksInfo<S> {
/// The values is 1-indexed grid line numbers bounding the area.
/// This matches the Chrome and Firefox's format as of 2nd Jan 2024.
#[derive(Debug, Clone, PartialEq)]
#[cfg(feature = "detailed_layout_info")]
pub struct DetailedGridItemsInfo {
/// row-start with 1-indexed grid line numbers
pub row_start: u16,
Expand All @@ -1300,7 +1287,6 @@ pub struct DetailedGridItemsInfo {
}

/// Grid area information from the placement algorithm
#[cfg(feature = "detailed_layout_info")]
impl DetailedGridItemsInfo {
/// Construct from GridItems
#[inline(always)]
Expand Down
1 change: 0 additions & 1 deletion src/compute/grid/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ pub(super) use grid_item::GridItem;
pub(super) use grid_track::GridTrack;
pub(super) use grid_track_counts::TrackCounts;
pub(super) use named::NamedLineResolver;
#[cfg(feature = "detailed_layout_info")]
pub use named::{GridLineNames, GridLineNamesIter};

#[allow(unused_imports)]
Expand Down
Loading