tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5544Rust 88Markdown 11TOML 91926 Sutichaykuna

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 · (1 wata ñawpaq) · tayta b5a5f8001 · kay commitpi sach'ata qhaway

Semántico sayk'uy · 17

src/compute/grid/mod.rs

src/compute/mod.rs

src/tree/layout.rs

src/tree/taffy_tree.rs

src/tree/traits.rs

9 qillqakuna · +183 −1

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")]