Add API to expose computed grid information (#772)
* Add API to expose computed grid information
* Apply rust fmt
* Apply general suggestions
* Expose ComputedGridInfo for low level API
* Rename features and objects to detailed
* Fix clippy and feature flag mistakes
859cd5182556840aff0fbdcdf1635c3df028e321 · ruwaq Steven Novaryo · 2025-01-03T08:47:46Z (1 wata ñawpaq) · tayta b5a5f8001 · kay commitpi sach'ata qhaway
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a63399e39..944bbc480 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -125,6 +125,17 @@ jobs:
- run: cargo build --no-default-features --features block_layout,std,taffy_tree
- run: cargo test --tests --no-default-features --features block_layout,std,taffy_tree
+ # With detailed layout info
+ test-features-grid-detailed-info:
+ name: "Test Suite [std + grid + detailed_layout_info]"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ - run: cargo build --no-default-features --features grid,detailed_layout_info,std
+ - run: cargo build --no-default-features --features grid,detailed_layout_info,std,taffy_tree
+ - run: cargo test --tests --no-default-features --features grid,detailed_layout_info,std,taffy_tree
+
# With alloc feature
test-features-grid-with-alloc:
diff --git a/Cargo.toml b/Cargo.toml
index c4bad026c..b5bc503ce 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -41,10 +41,11 @@ default = [
"grid",
"block_layout",
"content_size",
+ "detailed_layout_info",
]
#! ## Feature Flags
#!
-#! ### Algorithms
+#! ### Algorithms
## Enables the Block layout algorithm. See [`compute_block_layout`](crate::compute_block_layout).
block_layout = []
@@ -54,6 +55,8 @@ flexbox = []
grid = ["alloc", "dep:grid"]
## Causes all algorithms to compute and output a content size for each node
content_size = []
+## Causes algorithms to stores detailed information of the nodes in TaffyTree, with only CSS Grid supporting this.
+detailed_layout_info = []
#! ### Taffy Tree
diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs
index 23dd03acd..d8949dfe4 100644
--- a/src/compute/grid/mod.rs
+++ b/src/compute/grid/mod.rs
@@ -23,6 +23,9 @@ use track_sizing::{
};
use types::{CellOccupancyMatrix, GridTrack};
+#[cfg(feature = "detailed_layout_info")]
+use types::{GridItem, GridTrackKind, TrackCounts};
+
pub(crate) use types::{GridCoordinate, GridLine, OriginZeroLine};
mod alignment;
@@ -592,9 +595,122 @@ pub fn compute_grid_layout(tree: &mut impl LayoutGridContainer, node: NodeId, in
item.y_position + item.baseline.unwrap_or(item.height)
};
+ #[cfg(feature = "detailed_layout_info")]
+ tree.set_detailed_grid_info(
+ node,
+ DetailedGridInfo {
+ rows: DetailedGridTracksInfo::from_grid_tracks_and_track_count(final_row_counts, rows),
+ columns: DetailedGridTracksInfo::from_grid_tracks_and_track_count(final_col_counts, columns),
+ items: items.iter().map(DetailedGridItemsInfo::from_grid_item).collect(),
+ },
+ );
+
LayoutOutput::from_sizes_and_baselines(
container_border_box,
item_content_size_contribution,
Point { x: None, y: Some(grid_container_baseline) },
)
}
+
+/// Information from the computation of grid
+#[derive(Debug, Clone, PartialEq)]
+#[cfg(feature = "detailed_layout_info")]
+pub struct DetailedGridInfo {
+ /// <https://drafts.csswg.org/css-grid-1/#grid-row>
+ pub rows: DetailedGridTracksInfo,
+ /// <https://drafts.csswg.org/css-grid-1/#grid-column>
+ pub columns: DetailedGridTracksInfo,
+ /// <https://drafts.csswg.org/css-grid-1/#grid-items>
+ pub items: Vec<DetailedGridItemsInfo>,
+}
+
+/// Information from the computation of grids tracks
+#[derive(Debug, Clone, PartialEq)]
+#[cfg(feature = "detailed_layout_info")]
+pub struct DetailedGridTracksInfo {
+ /// Number of leading implicit grid tracks
+ pub negative_implicit_tracks: u16,
+ /// Number of explicit grid tracks
+ pub explicit_tracks: u16,
+ /// Number of trailing implicit grid tracks
+ pub positive_implicit_tracks: u16,
+
+ /// Gutters between tracks
+ pub gutters: Vec<f32>,
+ /// The used size of the tracks
+ pub sizes: Vec<f32>,
+}
+
+#[cfg(feature = "detailed_layout_info")]
+impl DetailedGridTracksInfo {
+ /// Get the base_size of [`GridTrack`] with a kind [`types::GridTrackKind`]
+ #[inline(always)]
+ fn grid_track_base_size_of_kind(grid_tracks: &[GridTrack], kind: GridTrackKind) -> Vec<f32> {
+ grid_tracks
+ .iter()
+ .filter_map(|track| match track.kind == kind {
+ true => Some(track.base_size),
+ false => None,
+ })
+ .collect()
+ }
+
+ /// Get the sizes of the gutters
+ fn gutters_from_grid_track_layout(grid_tracks: &[GridTrack]) -> Vec<f32> {
+ DetailedGridTracksInfo::grid_track_base_size_of_kind(grid_tracks, GridTrackKind::Gutter)
+ }
+
+ /// Get the sizes of the tracks
+ fn sizes_from_grid_track_layout(grid_tracks: &[GridTrack]) -> Vec<f32> {
+ DetailedGridTracksInfo::grid_track_base_size_of_kind(grid_tracks, GridTrackKind::Track)
+ }
+
+ /// Construct DetailedGridTracksInfo from TrackCounts and GridTracks
+ fn from_grid_tracks_and_track_count(track_count: TrackCounts, grid_tracks: Vec<GridTrack>) -> Self {
+ DetailedGridTracksInfo {
+ negative_implicit_tracks: track_count.negative_implicit,
+ explicit_tracks: track_count.explicit,
+ positive_implicit_tracks: track_count.positive_implicit,
+ gutters: DetailedGridTracksInfo::gutters_from_grid_track_layout(&grid_tracks),
+ sizes: DetailedGridTracksInfo::sizes_from_grid_track_layout(&grid_tracks),
+ }
+ }
+}
+
+/// Grid area information from the placement algorithm
+///
+/// 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,
+ /// row-end with 1-indexed grid line numbers
+ pub row_end: u16,
+ /// column-start with 1-indexed grid line numbers
+ pub column_start: u16,
+ /// column-end with 1-indexed grid line numbers
+ pub column_end: u16,
+}
+
+/// Grid area information from the placement algorithm
+#[cfg(feature = "detailed_layout_info")]
+impl DetailedGridItemsInfo {
+ /// Construct from GridItems
+ #[inline(always)]
+ fn from_grid_item(grid_item: &GridItem) -> Self {
+ /// Conversion from the indexes of Vec<GridTrack> into 1-indexed grid line numbers. See [`GridItem::row_indexes`] or [`GridItem::column_indexes`]
+ #[inline(always)]
+ fn to_one_indexed_grid_line(grid_track_index: u16) -> u16 {
+ grid_track_index / 2 + 1
+ }
+
+ DetailedGridItemsInfo {
+ row_start: to_one_indexed_grid_line(grid_item.row_indexes.start),
+ row_end: to_one_indexed_grid_line(grid_item.row_indexes.end),
+ column_start: to_one_indexed_grid_line(grid_item.column_indexes.start),
+ column_end: to_one_indexed_grid_line(grid_item.column_indexes.end),
+ }
+ }
+}
diff --git a/src/compute/mod.rs b/src/compute/mod.rs
index 42c3b03f3..4228e21a0 100644
--- a/src/compute/mod.rs
+++ b/src/compute/mod.rs
@@ -272,6 +272,13 @@ pub fn compute_hidden_layout(tree: &mut (impl LayoutPartialTree + CacheTree), no
LayoutOutput::HIDDEN
}
+/// A module for unified re-exports of detailed layout info structs, used by low level API
+#[cfg(feature = "detailed_layout_info")]
+pub mod detailed_info {
+ #[cfg(feature = "grid")]
+ pub use super::grid::DetailedGridInfo;
+}
+
#[cfg(test)]
mod tests {
use super::compute_hidden_layout;
diff --git a/src/lib.rs b/src/lib.rs
index baae4f79f..a86d80176 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -102,6 +102,9 @@ pub use crate::compute::compute_flexbox_layout;
#[cfg(feature = "grid")]
#[doc(inline)]
pub use crate::compute::compute_grid_layout;
+#[cfg(feature = "detailed_layout_info")]
+#[doc(inline)]
+pub use crate::compute::detailed_info::*;
#[doc(inline)]
pub use crate::compute::{
compute_cached_layout, compute_hidden_layout, compute_leaf_layout, compute_root_layout, round_layout,
diff --git a/src/tree/layout.rs b/src/tree/layout.rs
index 5e6364a9d..06166c79e 100644
--- a/src/tree/layout.rs
+++ b/src/tree/layout.rs
@@ -344,3 +344,14 @@ impl Layout {
)
}
}
+
+/// The additional information from layout algorithm
+#[cfg(feature = "detailed_layout_info")]
+#[derive(Debug, Clone, PartialEq)]
+pub enum DetailedLayoutInfo {
+ /// Enum variant for [`DetailedGridInfo`]
+ #[cfg(feature = "grid")]
+ Grid(Box<crate::compute::grid::DetailedGridInfo>),
+ /// For node that hasn't had any detailed information yet
+ None,
+}
diff --git a/src/tree/mod.rs b/src/tree/mod.rs
index ccb5b625e..637136bd7 100644
--- a/src/tree/mod.rs
+++ b/src/tree/mod.rs
@@ -28,3 +28,6 @@ pub use traits::LayoutBlockContainer;
mod taffy_tree;
#[cfg(feature = "taffy_tree")]
pub use taffy_tree::{TaffyError, TaffyResult, TaffyTree};
+
+#[cfg(feature = "detailed_layout_info")]
+pub use layout::DetailedLayoutInfo;
diff --git a/src/tree/taffy_tree.rs b/src/tree/taffy_tree.rs
index 9cb0a6a4a..9cb38a8de 100644
--- a/src/tree/taffy_tree.rs
+++ b/src/tree/taffy_tree.rs
@@ -25,6 +25,11 @@ use crate::{compute::compute_flexbox_layout, LayoutFlexboxContainer};
#[cfg(feature = "grid")]
use crate::{compute::compute_grid_layout, LayoutGridContainer};
+#[cfg(all(feature = "detailed_layout_info", feature = "grid"))]
+use crate::compute::grid::DetailedGridInfo;
+#[cfg(feature = "detailed_layout_info")]
+use crate::tree::layout::DetailedLayoutInfo;
+
/// The error Taffy generates on invalid operations
pub type TaffyResult<T> = Result<T, TaffyError>;
@@ -100,6 +105,10 @@ struct NodeData {
/// The cached results of the layout computation
pub(crate) cache: Cache,
+
+ /// The computation result from layout algorithm
+ #[cfg(feature = "detailed_layout_info")]
+ pub(crate) detailed_layout_info: DetailedLayoutInfo,
}
impl NodeData {
@@ -112,6 +121,8 @@ impl NodeData {
unrounded_layout: Layout::new(),
final_layout: Layout::new(),
has_context: false,
+ #[cfg(feature = "detailed_layout_info")]
+ detailed_layout_info: DetailedLayoutInfo::None,
}
}
@@ -482,6 +493,12 @@ where
fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> {
&self.taffy.nodes[child_node_id.into()].style
}
+
+ #[inline(always)]
+ #[cfg(feature = "detailed_layout_info")]
+ fn set_detailed_grid_info(&mut self, node_id: NodeId, detailed_grid_info: DetailedGridInfo) {
+ self.taffy.nodes[node_id.into()].detailed_layout_info = DetailedLayoutInfo::Grid(Box::new(detailed_grid_info));
+ }
}
// RoundTree impl for TaffyView
diff --git a/src/tree/traits.rs b/src/tree/traits.rs
index d70cc7416..05b0f7da2 100644
--- a/src/tree/traits.rs
+++ b/src/tree/traits.rs
@@ -127,6 +127,8 @@
//! ```
//!
use super::{Layout, LayoutInput, LayoutOutput, NodeId, RequestedAxis, RunMode, SizingMode};
+#[cfg(feature = "detailed_layout_info")]
+use crate::debug::debug_log;
use crate::geometry::{AbsoluteAxis, Line, Size};
use crate::style::{AvailableSpace, CoreStyle};
#[cfg(feature = "flexbox")]
@@ -136,6 +138,9 @@ use crate::style::{GridContainerStyle, GridItemStyle};
#[cfg(feature = "block_layout")]
use crate::{BlockContainerStyle, BlockItemStyle};
+#[cfg(all(feature = "grid", feature = "detailed_layout_info"))]
+use crate::compute::grid::DetailedGridInfo;
+
/// Taffy's abstraction for downward tree traversal.
///
/// However, this trait does *not* require access to any node's other than a single container node's immediate children unless you also intend to implement `TraverseTree`.
@@ -267,6 +272,12 @@ pub trait LayoutGridContainer: LayoutPartialTree {
/// Get the child's styles
fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_>;
+
+ /// Set the node's detailed grid information
+ #[cfg(feature = "detailed_layout_info")]
+ fn set_detailed_grid_info(&mut self, _node_id: NodeId, _detailed_grid_info: DetailedGridInfo) {
+ debug_log!("LayoutGridContainer::set_detailed_grid_info called");
+ }
}
#[cfg(feature = "block_layout")]