From 747dd703543c0344aadd83fac1fa83af7e0d56ed Mon Sep 17 00:00:00 2001 From: Simon Garnier Date: Mon, 17 Feb 2025 22:43:34 -0800 Subject: [PATCH 1/4] Introduced a builder pattern interface (#275) --- Cargo.toml | 3 + src/lib.rs | 3 + src/prelude.rs | 3 + src/style/builder.rs | 378 +++++++++++++++++++++++++++++++++++++++++++ src/style/mod.rs | 4 + src/util/sys.rs | 4 + 6 files changed, 395 insertions(+) create mode 100644 src/style/builder.rs diff --git a/Cargo.toml b/Cargo.toml index e4db4c8e4..5dc6b3190 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ default = [ "calc", "content_size", "detailed_layout_info", + "builder" ] #! ## Feature Flags #! @@ -62,6 +63,8 @@ content_size = [] detailed_layout_info = [] ## Use strict provenance APIs for pointer manipulation. Using this feature requires Rust 1.84 or higher. strict_provenance = [] +## Expose a builder pattern interface to construct layout tree +builder = ["alloc"] #! ### Taffy Tree diff --git a/src/lib.rs b/src/lib.rs index d3a252d25..e7213e38d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,6 +109,9 @@ pub use crate::compute::{ }; #[doc(inline)] pub use crate::style::Style; +#[cfg(feature = "builder")] +#[doc(inline)] +pub use crate::style::StyleBuilder; #[doc(inline)] pub use crate::tree::traits::*; #[cfg(feature = "taffy_tree")] diff --git a/src/prelude.rs b/src/prelude.rs index abf55061b..ea0e8b6b8 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -28,3 +28,6 @@ pub use crate::style_helpers::{ #[cfg(feature = "taffy_tree")] pub use crate::TaffyTree; + +#[cfg(feature = "builder")] +pub use crate::style::{NodeIdRef, StyleBuilder}; diff --git a/src/style/builder.rs b/src/style/builder.rs new file mode 100644 index 000000000..249b45db9 --- /dev/null +++ b/src/style/builder.rs @@ -0,0 +1,378 @@ +//! A builder pattern interface to facilitate the creation of [`super::Style`] +use super::{ + AlignContent, AlignItems, AlignSelf, BoxSizing, Dimension, Display, JustifyContent, LengthPercentage, + LengthPercentageAuto, Overflow, Position, Style, +}; +use crate::sys::Rc; +use crate::{util::sys::Vec, Line, NodeId, Point, Rect, Size}; +use core::cell::Cell; + +#[cfg(feature = "flexbox")] +use super::{FlexDirection, FlexWrap}; +#[cfg(feature = "taffy_tree")] +use crate::{TaffyResult, TaffyTree}; +#[cfg(feature = "grid")] +use { + super::{GridAutoFlow, GridPlacement, NonRepeatedTrackSizingFunction, TrackSizingFunction}, + crate::sys::GridTrackVec, +}; + +/// `NodeIdRef` can be passed to a [`StyleBuilder`] so that caller can later +/// retrieve the [`NodeId`] of a built tree node. +#[derive(Debug, Clone, Default)] +pub struct NodeIdRef(Rc>>); + +impl NodeIdRef { + /// Create an empty [`NodeIdRef`]. + pub fn new() -> Self { + Self(Rc::new(Cell::new(None))) + } + + /// Set the [`NodeId`]. + fn set(&self, node_id: NodeId) { + self.0.set(Some(node_id)); + } + + /// Get a copy of the inner [`NodeId`], if any is present. + pub fn get(&self) -> Option { + self.0.get() + } +} + +/// Given a builder name and associated fields, generate the following : +/// * A struct of the given name, with the following fields +/// * `children`: a vec of child builder +/// * `node_id_ref`: a field holding a [`Option`], wich allow for retrieving the [`NodeId`] of the built node +/// * A [`Option<_>`] field for each provided field +/// * An `impl`` block containing the following : +/// * A method named after the provided field, used to set said field +/// * A `build_style` method, used to generate a [`Style`](super::Style) based on data stored in the builder +macro_rules! gen_builder { + ($builder:ident, $(($field:ident: $type:ty $(, cfg: $($cfg:tt)+)?)),* $(,)?) => { + /// Use [`StyleBuilder`] to construct a tree of nested style nodes. + /// + /// Example : + /// ```rust + /// # use taffy::prelude::*; + /// let mut builder_tree: TaffyTree<()> = TaffyTree::new(); + /// let header_node_handle = NodeIdRef::new(); + /// let body_node_handle = NodeIdRef::new(); + /// + /// let builder_root_node = StyleBuilder::new() + /// .flex_direction(FlexDirection::Column) + /// .size(Size { width: length(800.0), height: length(600.0) }) + /// .child( + /// StyleBuilder::new().width(length(800.0)).height(length(100.0)).node_id_ref(header_node_handle.clone()), + /// ) + /// .child( + /// StyleBuilder::new() + /// .width(length(800.0)) + /// .height(auto()) + /// .flex_grow(1.0) + /// .node_id_ref(body_node_handle.clone()), + /// ) + /// .build(&mut builder_tree) + /// .unwrap(); + /// + /// builder_tree.compute_layout(builder_root_node, Size::MAX_CONTENT).unwrap(); + /// ``` + #[derive(Debug, Default)] + pub struct $builder<'a> { + children: Vec<&'a StyleBuilder<'a>>, + node_id_ref: Option, + $( + $(#[cfg($($cfg)+)])? + $field: Option<$type>, + )* + } + + impl<'a> $builder<'a> { + $( + $(#[cfg($($cfg)+)])? + #[doc = concat!("Will set the `", stringify!($field), "` field to the provided value in the")] + #[doc = "\nresulting [`Style`](super::Style) when the [`build`](StyleBuilder::build) method is called."] + #[doc = concat!("\n\nSee [`Style::", stringify!($field), "`](super::Style::", stringify!($field), ").")] + pub fn $field(&mut self, $field: impl Into<$type>) -> &mut Self { + self.$field = Some($field.into()); + self + } + )* + + /// Build an [`Style`](super::Style) based on provided cconfiguration. + /// Calling this without setting any field results in + /// [`Style::default()`](super::Style::default) + pub fn build_style(&self) -> Style { + let mut style = Style::default(); + + $( + $(#[cfg($($cfg)+)])? + if let Some(ref value) = self.$field { + style.$field = Clone::clone(value); + } + )* + + style + } + } + }; +} + +gen_builder!( + StyleBuilder, + (display: Display), + (item_is_table: bool), + (box_sizing: BoxSizing), + (overflow: Point), + (scrollbar_width: f32), + (position: Position), + (inset: Rect), + (size: Size), + (min_size: Size), + (max_size: Size), + (aspect_ratio: Option), + (margin: Rect), + (padding: Rect), + (border: Rect), + (align_items: Option, cfg: any(feature = "flexbox", feature = "grid")), + (align_self: Option, cfg: any(feature = "flexbox", feature = "grid")), + (justify_items: Option, cfg: feature = "grid"), + (justify_self: Option, cfg: feature = "grid"), + (align_content: Option, cfg: any(feature = "flexbox", feature = "grid")), + (justify_content: Option, cfg: any(feature = "flexbox", feature = "grid")), + (gap: Size, cfg: any(feature = "flexbox", feature = "grid")), + (text_align: super::TextAlign, cfg: feature = "block_layout"), + (flex_direction: FlexDirection, cfg: feature = "flexbox"), + (flex_wrap: FlexWrap, cfg: feature = "flexbox"), + (flex_basis: Dimension, cfg: feature = "flexbox"), + (flex_grow: f32, cfg: feature = "flexbox"), + (flex_shrink: f32, cfg: feature = "flexbox"), + (grid_template_rows: GridTrackVec, cfg: feature = "grid"), + (grid_template_columns: GridTrackVec, cfg: feature = "grid"), + (grid_auto_rows: GridTrackVec, cfg: feature = "grid"), + (grid_auto_columns: GridTrackVec, cfg: feature = "grid"), + (grid_auto_flow: GridAutoFlow, cfg: feature = "grid"), + (grid_row: Line, cfg: feature = "grid"), + (grid_column: Line, cfg: feature = "grid"), +); + +impl<'a> StyleBuilder<'a> { + /// Create a new [`StyleBuilder`]. + pub fn new() -> Self { + Self::default() + } + + #[cfg(feature = "flexbox")] + /// Create a new [`StyleBuilder`], pre-configured to use [`FlexDirection::Row`] + pub fn row() -> Self { + let mut row = Self::new(); + row.flex_direction(FlexDirection::Row); + row + } + + #[cfg(feature = "flexbox")] + /// Create a new [`StyleBuilder`], pre-configured to use [`FlexDirection::Column`] + pub fn column() -> Self { + let mut column = Self::new(); + column.flex_direction(FlexDirection::Column); + column + } + + /// Add a child [`StyleBuilder`] to this builder. Calling this method does not result + /// in the child [`StyleBuilder`] being built until the [`StyleBuilder::build`] method + /// is invoke on this builder. + pub fn child(&'a mut self, style_builder: &'a StyleBuilder) -> &'a mut StyleBuilder<'a> { + self.children.push(style_builder); + self + } + + #[cfg(feature = "taffy_tree")] + /// Create a new node for this builder and all child builder stored within. + /// This is done by creating a new node in the provided [`TaffyTree`]. + /// Return a [`TaffyResult`] for the root node. Child [`NodeId`] can be + /// retrieved once [`build`](StyleBuilder::build) is invoked via setting a [`NodeIdRef`] + /// in each of the desired child [`StyleBuilder`] + pub fn build(&self, tree: &mut TaffyTree) -> TaffyResult { + let style = self.build_style(); + let node_id = tree.new_leaf(style)?; + + if let Some(node_id_ref) = self.node_id_ref.as_ref() { + node_id_ref.set(node_id); + } + + let children_node_ids = self.children.iter().map(|child| child.build(tree)).collect::, _>>()?; + + tree.set_children(node_id, &children_node_ids)?; + + Ok(node_id) + } + + /// This setter can be used to set a [`NodeIdRef`]. If this is set, + /// the [`NodeIdRef`] can be used to retrieved to [`NodeId`] of the node + /// built via the [`build`](StyleBuilder::build) method + /// Example: + /// ```rust + /// # use taffy::prelude::*; + /// + /// let mut tree: TaffyTree<()> = TaffyTree::new(); + /// let child_node_id_ref = NodeIdRef::new(); + /// + /// let root_node_id = StyleBuilder::new() + /// .display(Display::Block) + /// .child( + /// StyleBuilder::new() + /// .display(Display::Block) + /// .node_id_ref(child_node_id_ref.clone()) + /// ) + /// .build(&mut tree) + /// .unwrap(); + /// + /// tree.compute_layout(root_node_id, Size::MAX_CONTENT).unwrap(); + /// + /// assert!( + /// matches!( + /// child_node_id_ref.get(), + /// Some(_) + /// ) + /// ); + /// + /// tree.layout(child_node_id_ref.get().unwrap()).unwrap(); + /// ``` + pub fn node_id_ref(&'a mut self, node_id_ref: NodeIdRef) -> &'a mut StyleBuilder<'a> { + self.node_id_ref = Some(node_id_ref); + self + } + + /// Shorthand method to set the width of the resulting [`Style`] + pub fn width(&'a mut self, width: Dimension) -> &'a mut StyleBuilder<'a> { + match self.size { + Some(size) => { + self.size = Some(Size { width, ..size }); + } + None => self.size = Some(Size { width, ..Style::DEFAULT.size }), + } + self + } + + /// Shorthand method to set the height of the resulting [`Style`] + pub fn height(&'a mut self, height: Dimension) -> &'a mut StyleBuilder<'a> { + match self.size { + Some(size) => { + self.size = Some(Size { height, ..size }); + } + None => self.size = Some(Size { height, ..Style::DEFAULT.size }), + } + self + } +} + +#[cfg(test)] +mod test { + #[cfg(feature = "flexbox")] + use crate::FlexDirection; + + use crate::{ + prelude::{auto, length, TaffyMaxContent}, + style::builder::NodeIdRef, + Size, TaffyTree, + }; + + use super::{Style, StyleBuilder}; + + #[test] + fn builder_defaults_match_defaults() { + assert_eq!(StyleBuilder::default().build_style(), Style::default()) + } + + #[test] + #[cfg(feature = "flexbox")] + fn readme_example() { + let mut tree: TaffyTree<()> = TaffyTree::new(); + let header_node = tree + .new_leaf(Style { size: Size { width: length(800.0), height: length(100.0) }, ..Default::default() }) + .unwrap(); + + let body_node = tree + .new_leaf(Style { + size: Size { width: length(800.0), height: auto() }, + flex_grow: 1.0, + ..Default::default() + }) + .unwrap(); + + let root_node = tree + .new_with_children( + Style { + flex_direction: FlexDirection::Column, + size: Size { width: length(800.0), height: length(600.0) }, + ..Default::default() + }, + &[header_node, body_node], + ) + .unwrap(); + + tree.compute_layout(root_node, Size::MAX_CONTENT).unwrap(); + + let mut builder_tree: TaffyTree<()> = TaffyTree::new(); + let header_node_handle = NodeIdRef::new(); + let body_node_handle = NodeIdRef::new(); + + let builder_root_node = StyleBuilder::new() + .flex_direction(FlexDirection::Column) + .size(Size { width: length(800.0), height: length(600.0) }) + .child( + StyleBuilder::new().width(length(800.0)).height(length(100.0)).node_id_ref(header_node_handle.clone()), + ) + .child( + StyleBuilder::new() + .width(length(800.0)) + .height(auto()) + .flex_grow(1.0) + .node_id_ref(body_node_handle.clone()), + ) + .build(&mut builder_tree) + .unwrap(); + + builder_tree.compute_layout(builder_root_node, Size::MAX_CONTENT).unwrap(); + + assert_eq!( + tree.layout(root_node).unwrap().size.width, + builder_tree.layout(builder_root_node).unwrap().size.width + ); + assert_eq!( + tree.layout(root_node).unwrap().size.height, + builder_tree.layout(builder_root_node).unwrap().size.height + ); + assert_eq!( + tree.layout(header_node).unwrap().size.width, + builder_tree.layout(header_node_handle.get().unwrap()).unwrap().size.width + ); + assert_eq!( + tree.layout(header_node).unwrap().size.height, + builder_tree.layout(header_node_handle.get().unwrap()).unwrap().size.height + ); + assert_eq!( + tree.layout(body_node).unwrap().size.width, + builder_tree.layout(body_node_handle.get().unwrap()).unwrap().size.width + ); + assert_eq!( + tree.layout(body_node).unwrap().size.height, + builder_tree.layout(body_node_handle.get().unwrap()).unwrap().size.height + ); + } + + #[test] + fn row() { + assert_eq!( + StyleBuilder::row().build_style(), + Style { flex_direction: FlexDirection::Row, ..Default::default() } + ) + } + + #[test] + fn column() { + assert_eq!( + StyleBuilder::column().build_style(), + Style { flex_direction: FlexDirection::Column, ..Default::default() } + ) + } +} diff --git a/src/style/mod.rs b/src/style/mod.rs index 4319675d0..b1042fb13 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -6,6 +6,8 @@ mod dimension; #[cfg(feature = "block_layout")] mod block; +#[cfg(feature = "builder")] +mod builder; #[cfg(feature = "flexbox")] mod flex; #[cfg(feature = "grid")] @@ -18,6 +20,8 @@ pub use self::dimension::{Dimension, LengthPercentage, LengthPercentageAuto}; #[cfg(feature = "block_layout")] pub use self::block::{BlockContainerStyle, BlockItemStyle, TextAlign}; +#[cfg(feature = "builder")] +pub use self::builder::{NodeIdRef, StyleBuilder}; #[cfg(feature = "flexbox")] pub use self::flex::{FlexDirection, FlexWrap, FlexboxContainerStyle, FlexboxItemStyle}; #[cfg(feature = "grid")] diff --git a/src/util/sys.rs b/src/util/sys.rs index 8e1d8d368..bc354893b 100644 --- a/src/util/sys.rs +++ b/src/util/sys.rs @@ -22,6 +22,8 @@ mod std { #[cfg(feature = "grid")] /// A vector of grid tracks pub(crate) type GridTrackVec = std::vec::Vec; + /// Rc + pub(crate) type Rc = std::rc::Rc; /// Creates a new vector with the capacity for the specified number of items before it must be resized #[must_use] @@ -83,6 +85,8 @@ mod alloc { #[cfg(feature = "grid")] /// A vector of grid tracks pub(crate) type GridTrackVec = alloc::vec::Vec; + /// Rc + pub(crate) type Rc = alloc::rc::Rc; /// Creates a new vector with the capacity for the specified number of items before it must be resized #[must_use] From 51141ace6bd4ec2049f478255bcd7b912179ffc5 Mon Sep 17 00:00:00 2001 From: Simon Garnier Date: Wed, 5 Mar 2025 19:39:59 -0800 Subject: [PATCH 2/4] StyleBuilder is now using Style internally (#275) --- src/style/builder.rs | 55 ++++++++------------------------------------ 1 file changed, 10 insertions(+), 45 deletions(-) diff --git a/src/style/builder.rs b/src/style/builder.rs index 249b45db9..9253b8ee6 100644 --- a/src/style/builder.rs +++ b/src/style/builder.rs @@ -43,8 +43,9 @@ impl NodeIdRef { /// * A struct of the given name, with the following fields /// * `children`: a vec of child builder /// * `node_id_ref`: a field holding a [`Option`], wich allow for retrieving the [`NodeId`] of the built node +/// * `style`: the [`Style`] that will be modified when calling the setters in the `impl` block /// * A [`Option<_>`] field for each provided field -/// * An `impl`` block containing the following : +/// * An `impl` block containing the following : /// * A method named after the provided field, used to set said field /// * A `build_style` method, used to generate a [`Style`](super::Style) based on data stored in the builder macro_rules! gen_builder { @@ -80,10 +81,7 @@ macro_rules! gen_builder { pub struct $builder<'a> { children: Vec<&'a StyleBuilder<'a>>, node_id_ref: Option, - $( - $(#[cfg($($cfg)+)])? - $field: Option<$type>, - )* + style: Style, } impl<'a> $builder<'a> { @@ -93,26 +91,10 @@ macro_rules! gen_builder { #[doc = "\nresulting [`Style`](super::Style) when the [`build`](StyleBuilder::build) method is called."] #[doc = concat!("\n\nSee [`Style::", stringify!($field), "`](super::Style::", stringify!($field), ").")] pub fn $field(&mut self, $field: impl Into<$type>) -> &mut Self { - self.$field = Some($field.into()); + self.style.$field = $field.into(); self } )* - - /// Build an [`Style`](super::Style) based on provided cconfiguration. - /// Calling this without setting any field results in - /// [`Style::default()`](super::Style::default) - pub fn build_style(&self) -> Style { - let mut style = Style::default(); - - $( - $(#[cfg($($cfg)+)])? - if let Some(ref value) = self.$field { - style.$field = Clone::clone(value); - } - )* - - style - } } }; } @@ -192,8 +174,7 @@ impl<'a> StyleBuilder<'a> { /// retrieved once [`build`](StyleBuilder::build) is invoked via setting a [`NodeIdRef`] /// in each of the desired child [`StyleBuilder`] pub fn build(&self, tree: &mut TaffyTree) -> TaffyResult { - let style = self.build_style(); - let node_id = tree.new_leaf(style)?; + let node_id = tree.new_leaf(self.style.clone())?; if let Some(node_id_ref) = self.node_id_ref.as_ref() { node_id_ref.set(node_id); @@ -244,23 +225,13 @@ impl<'a> StyleBuilder<'a> { /// Shorthand method to set the width of the resulting [`Style`] pub fn width(&'a mut self, width: Dimension) -> &'a mut StyleBuilder<'a> { - match self.size { - Some(size) => { - self.size = Some(Size { width, ..size }); - } - None => self.size = Some(Size { width, ..Style::DEFAULT.size }), - } + self.style.size.width = width; self } /// Shorthand method to set the height of the resulting [`Style`] pub fn height(&'a mut self, height: Dimension) -> &'a mut StyleBuilder<'a> { - match self.size { - Some(size) => { - self.size = Some(Size { height, ..size }); - } - None => self.size = Some(Size { height, ..Style::DEFAULT.size }), - } + self.style.size.height = height; self } } @@ -280,7 +251,7 @@ mod test { #[test] fn builder_defaults_match_defaults() { - assert_eq!(StyleBuilder::default().build_style(), Style::default()) + assert_eq!(StyleBuilder::default().style, Style::default()) } #[test] @@ -362,17 +333,11 @@ mod test { #[test] fn row() { - assert_eq!( - StyleBuilder::row().build_style(), - Style { flex_direction: FlexDirection::Row, ..Default::default() } - ) + assert_eq!(StyleBuilder::row().style, Style { flex_direction: FlexDirection::Row, ..Default::default() }) } #[test] fn column() { - assert_eq!( - StyleBuilder::column().build_style(), - Style { flex_direction: FlexDirection::Column, ..Default::default() } - ) + assert_eq!(StyleBuilder::column().style, Style { flex_direction: FlexDirection::Column, ..Default::default() }) } } From fec144797eccfd43faf2669a4acf3173b3dbc795 Mon Sep 17 00:00:00 2001 From: Simon Garnier Date: Mon, 10 Mar 2025 22:19:22 -0700 Subject: [PATCH 3/4] NodeIdRef no longer relies on interior mutability (#275) --- Cargo.toml | 2 +- src/style/builder.rs | 49 +++++++++++++++++++++----------------------- src/util/sys.rs | 4 ---- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5dc6b3190..41819f089 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ detailed_layout_info = [] ## Use strict provenance APIs for pointer manipulation. Using this feature requires Rust 1.84 or higher. strict_provenance = [] ## Expose a builder pattern interface to construct layout tree -builder = ["alloc"] +builder = [] #! ### Taffy Tree diff --git a/src/style/builder.rs b/src/style/builder.rs index 9253b8ee6..d505f982c 100644 --- a/src/style/builder.rs +++ b/src/style/builder.rs @@ -3,9 +3,7 @@ use super::{ AlignContent, AlignItems, AlignSelf, BoxSizing, Dimension, Display, JustifyContent, LengthPercentage, LengthPercentageAuto, Overflow, Position, Style, }; -use crate::sys::Rc; use crate::{util::sys::Vec, Line, NodeId, Point, Rect, Size}; -use core::cell::Cell; #[cfg(feature = "flexbox")] use super::{FlexDirection, FlexWrap}; @@ -20,22 +18,22 @@ use { /// `NodeIdRef` can be passed to a [`StyleBuilder`] so that caller can later /// retrieve the [`NodeId`] of a built tree node. #[derive(Debug, Clone, Default)] -pub struct NodeIdRef(Rc>>); +pub struct NodeIdRef(Option); impl NodeIdRef { /// Create an empty [`NodeIdRef`]. pub fn new() -> Self { - Self(Rc::new(Cell::new(None))) + Self(None) } /// Set the [`NodeId`]. - fn set(&self, node_id: NodeId) { - self.0.set(Some(node_id)); + fn set(&mut self, node_id: NodeId) { + self.0 = Some(node_id) } /// Get a copy of the inner [`NodeId`], if any is present. pub fn get(&self) -> Option { - self.0.get() + self.0 } } @@ -56,21 +54,21 @@ macro_rules! gen_builder { /// ```rust /// # use taffy::prelude::*; /// let mut builder_tree: TaffyTree<()> = TaffyTree::new(); - /// let header_node_handle = NodeIdRef::new(); - /// let body_node_handle = NodeIdRef::new(); + /// let mut header_node_handle = NodeIdRef::new(); + /// let mut body_node_handle = NodeIdRef::new(); /// /// let builder_root_node = StyleBuilder::new() /// .flex_direction(FlexDirection::Column) /// .size(Size { width: length(800.0), height: length(600.0) }) /// .child( - /// StyleBuilder::new().width(length(800.0)).height(length(100.0)).node_id_ref(header_node_handle.clone()), + /// StyleBuilder::new().width(length(800.0)).height(length(100.0)).node_id_ref(&mut header_node_handle), /// ) /// .child( /// StyleBuilder::new() /// .width(length(800.0)) /// .height(auto()) /// .flex_grow(1.0) - /// .node_id_ref(body_node_handle.clone()), + /// .node_id_ref(&mut body_node_handle), /// ) /// .build(&mut builder_tree) /// .unwrap(); @@ -79,8 +77,8 @@ macro_rules! gen_builder { /// ``` #[derive(Debug, Default)] pub struct $builder<'a> { - children: Vec<&'a StyleBuilder<'a>>, - node_id_ref: Option, + children: Vec<&'a mut StyleBuilder<'a>>, + node_id_ref: Option<&'a mut NodeIdRef>, style: Style, } @@ -162,7 +160,7 @@ impl<'a> StyleBuilder<'a> { /// Add a child [`StyleBuilder`] to this builder. Calling this method does not result /// in the child [`StyleBuilder`] being built until the [`StyleBuilder::build`] method /// is invoke on this builder. - pub fn child(&'a mut self, style_builder: &'a StyleBuilder) -> &'a mut StyleBuilder<'a> { + pub fn child(&'a mut self, style_builder: &'a mut StyleBuilder<'a>) -> &'a mut StyleBuilder<'a> { self.children.push(style_builder); self } @@ -173,14 +171,15 @@ impl<'a> StyleBuilder<'a> { /// Return a [`TaffyResult`] for the root node. Child [`NodeId`] can be /// retrieved once [`build`](StyleBuilder::build) is invoked via setting a [`NodeIdRef`] /// in each of the desired child [`StyleBuilder`] - pub fn build(&self, tree: &mut TaffyTree) -> TaffyResult { + pub fn build(&mut self, tree: &mut TaffyTree) -> TaffyResult { let node_id = tree.new_leaf(self.style.clone())?; - if let Some(node_id_ref) = self.node_id_ref.as_ref() { + if let Some(node_id_ref) = self.node_id_ref.as_mut() { node_id_ref.set(node_id); } - let children_node_ids = self.children.iter().map(|child| child.build(tree)).collect::, _>>()?; + let children_node_ids = + self.children.iter_mut().map(|child| child.build(tree)).collect::, _>>()?; tree.set_children(node_id, &children_node_ids)?; @@ -195,14 +194,14 @@ impl<'a> StyleBuilder<'a> { /// # use taffy::prelude::*; /// /// let mut tree: TaffyTree<()> = TaffyTree::new(); - /// let child_node_id_ref = NodeIdRef::new(); + /// let mut child_node_id_ref = NodeIdRef::new(); /// /// let root_node_id = StyleBuilder::new() /// .display(Display::Block) /// .child( /// StyleBuilder::new() /// .display(Display::Block) - /// .node_id_ref(child_node_id_ref.clone()) + /// .node_id_ref(&mut child_node_id_ref) /// ) /// .build(&mut tree) /// .unwrap(); @@ -218,7 +217,7 @@ impl<'a> StyleBuilder<'a> { /// /// tree.layout(child_node_id_ref.get().unwrap()).unwrap(); /// ``` - pub fn node_id_ref(&'a mut self, node_id_ref: NodeIdRef) -> &'a mut StyleBuilder<'a> { + pub fn node_id_ref(&'a mut self, node_id_ref: &'a mut NodeIdRef) -> &'a mut StyleBuilder<'a> { self.node_id_ref = Some(node_id_ref); self } @@ -284,21 +283,19 @@ mod test { tree.compute_layout(root_node, Size::MAX_CONTENT).unwrap(); let mut builder_tree: TaffyTree<()> = TaffyTree::new(); - let header_node_handle = NodeIdRef::new(); - let body_node_handle = NodeIdRef::new(); + let mut header_node_handle = NodeIdRef::new(); + let mut body_node_handle = NodeIdRef::new(); let builder_root_node = StyleBuilder::new() .flex_direction(FlexDirection::Column) .size(Size { width: length(800.0), height: length(600.0) }) - .child( - StyleBuilder::new().width(length(800.0)).height(length(100.0)).node_id_ref(header_node_handle.clone()), - ) + .child(StyleBuilder::new().width(length(800.0)).height(length(100.0)).node_id_ref(&mut header_node_handle)) .child( StyleBuilder::new() .width(length(800.0)) .height(auto()) .flex_grow(1.0) - .node_id_ref(body_node_handle.clone()), + .node_id_ref(&mut body_node_handle), ) .build(&mut builder_tree) .unwrap(); diff --git a/src/util/sys.rs b/src/util/sys.rs index bc354893b..8e1d8d368 100644 --- a/src/util/sys.rs +++ b/src/util/sys.rs @@ -22,8 +22,6 @@ mod std { #[cfg(feature = "grid")] /// A vector of grid tracks pub(crate) type GridTrackVec = std::vec::Vec; - /// Rc - pub(crate) type Rc = std::rc::Rc; /// Creates a new vector with the capacity for the specified number of items before it must be resized #[must_use] @@ -85,8 +83,6 @@ mod alloc { #[cfg(feature = "grid")] /// A vector of grid tracks pub(crate) type GridTrackVec = alloc::vec::Vec; - /// Rc - pub(crate) type Rc = alloc::rc::Rc; /// Creates a new vector with the capacity for the specified number of items before it must be resized #[must_use] From 2cb95be598e3c5d6fa81f4d38dc32bfcc8547713 Mon Sep 17 00:00:00 2001 From: Simon Garnier Date: Tue, 11 Mar 2025 21:43:10 -0700 Subject: [PATCH 4/4] `node_id_ref` now accepts &mut Option (#275) --- src/prelude.rs | 2 +- src/style/builder.rs | 59 +++++++++++++------------------------------- src/style/mod.rs | 2 +- 3 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/prelude.rs b/src/prelude.rs index ea0e8b6b8..8052d8c85 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -30,4 +30,4 @@ pub use crate::style_helpers::{ pub use crate::TaffyTree; #[cfg(feature = "builder")] -pub use crate::style::{NodeIdRef, StyleBuilder}; +pub use crate::style::StyleBuilder; diff --git a/src/style/builder.rs b/src/style/builder.rs index d505f982c..211301ead 100644 --- a/src/style/builder.rs +++ b/src/style/builder.rs @@ -15,37 +15,13 @@ use { crate::sys::GridTrackVec, }; -/// `NodeIdRef` can be passed to a [`StyleBuilder`] so that caller can later -/// retrieve the [`NodeId`] of a built tree node. -#[derive(Debug, Clone, Default)] -pub struct NodeIdRef(Option); - -impl NodeIdRef { - /// Create an empty [`NodeIdRef`]. - pub fn new() -> Self { - Self(None) - } - - /// Set the [`NodeId`]. - fn set(&mut self, node_id: NodeId) { - self.0 = Some(node_id) - } - - /// Get a copy of the inner [`NodeId`], if any is present. - pub fn get(&self) -> Option { - self.0 - } -} - /// Given a builder name and associated fields, generate the following : /// * A struct of the given name, with the following fields /// * `children`: a vec of child builder -/// * `node_id_ref`: a field holding a [`Option`], wich allow for retrieving the [`NodeId`] of the built node +/// * `node_id_ref`: a field holding an [`Option<&mut Option>`], wich allow for retrieving the [`NodeId`] of the built node /// * `style`: the [`Style`] that will be modified when calling the setters in the `impl` block -/// * A [`Option<_>`] field for each provided field /// * An `impl` block containing the following : /// * A method named after the provided field, used to set said field -/// * A `build_style` method, used to generate a [`Style`](super::Style) based on data stored in the builder macro_rules! gen_builder { ($builder:ident, $(($field:ident: $type:ty $(, cfg: $($cfg:tt)+)?)),* $(,)?) => { /// Use [`StyleBuilder`] to construct a tree of nested style nodes. @@ -54,21 +30,21 @@ macro_rules! gen_builder { /// ```rust /// # use taffy::prelude::*; /// let mut builder_tree: TaffyTree<()> = TaffyTree::new(); - /// let mut header_node_handle = NodeIdRef::new(); - /// let mut body_node_handle = NodeIdRef::new(); + /// let mut header_node_id = None; + /// let mut body_node_id = None; /// /// let builder_root_node = StyleBuilder::new() /// .flex_direction(FlexDirection::Column) /// .size(Size { width: length(800.0), height: length(600.0) }) /// .child( - /// StyleBuilder::new().width(length(800.0)).height(length(100.0)).node_id_ref(&mut header_node_handle), + /// StyleBuilder::new().width(length(800.0)).height(length(100.0)).node_id_ref(&mut header_node_id), /// ) /// .child( /// StyleBuilder::new() /// .width(length(800.0)) /// .height(auto()) /// .flex_grow(1.0) - /// .node_id_ref(&mut body_node_handle), + /// .node_id_ref(&mut body_node_id), /// ) /// .build(&mut builder_tree) /// .unwrap(); @@ -78,7 +54,7 @@ macro_rules! gen_builder { #[derive(Debug, Default)] pub struct $builder<'a> { children: Vec<&'a mut StyleBuilder<'a>>, - node_id_ref: Option<&'a mut NodeIdRef>, + node_id_ref: Option<&'a mut Option>, style: Style, } @@ -175,7 +151,7 @@ impl<'a> StyleBuilder<'a> { let node_id = tree.new_leaf(self.style.clone())?; if let Some(node_id_ref) = self.node_id_ref.as_mut() { - node_id_ref.set(node_id); + **node_id_ref = Some(node_id) } let children_node_ids = @@ -194,7 +170,7 @@ impl<'a> StyleBuilder<'a> { /// # use taffy::prelude::*; /// /// let mut tree: TaffyTree<()> = TaffyTree::new(); - /// let mut child_node_id_ref = NodeIdRef::new(); + /// let mut child_node_id_ref = None; /// /// let root_node_id = StyleBuilder::new() /// .display(Display::Block) @@ -210,14 +186,14 @@ impl<'a> StyleBuilder<'a> { /// /// assert!( /// matches!( - /// child_node_id_ref.get(), + /// child_node_id_ref, /// Some(_) /// ) /// ); /// - /// tree.layout(child_node_id_ref.get().unwrap()).unwrap(); + /// tree.layout(child_node_id_ref.unwrap()).unwrap(); /// ``` - pub fn node_id_ref(&'a mut self, node_id_ref: &'a mut NodeIdRef) -> &'a mut StyleBuilder<'a> { + pub fn node_id_ref(&'a mut self, node_id_ref: &'a mut Option) -> &'a mut StyleBuilder<'a> { self.node_id_ref = Some(node_id_ref); self } @@ -242,7 +218,6 @@ mod test { use crate::{ prelude::{auto, length, TaffyMaxContent}, - style::builder::NodeIdRef, Size, TaffyTree, }; @@ -283,8 +258,8 @@ mod test { tree.compute_layout(root_node, Size::MAX_CONTENT).unwrap(); let mut builder_tree: TaffyTree<()> = TaffyTree::new(); - let mut header_node_handle = NodeIdRef::new(); - let mut body_node_handle = NodeIdRef::new(); + let mut header_node_handle = None; + let mut body_node_handle = None; let builder_root_node = StyleBuilder::new() .flex_direction(FlexDirection::Column) @@ -312,19 +287,19 @@ mod test { ); assert_eq!( tree.layout(header_node).unwrap().size.width, - builder_tree.layout(header_node_handle.get().unwrap()).unwrap().size.width + builder_tree.layout(header_node_handle.unwrap()).unwrap().size.width ); assert_eq!( tree.layout(header_node).unwrap().size.height, - builder_tree.layout(header_node_handle.get().unwrap()).unwrap().size.height + builder_tree.layout(header_node_handle.unwrap()).unwrap().size.height ); assert_eq!( tree.layout(body_node).unwrap().size.width, - builder_tree.layout(body_node_handle.get().unwrap()).unwrap().size.width + builder_tree.layout(body_node_handle.unwrap()).unwrap().size.width ); assert_eq!( tree.layout(body_node).unwrap().size.height, - builder_tree.layout(body_node_handle.get().unwrap()).unwrap().size.height + builder_tree.layout(body_node_handle.unwrap()).unwrap().size.height ); } diff --git a/src/style/mod.rs b/src/style/mod.rs index b1042fb13..73dbb3a7a 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -21,7 +21,7 @@ pub use self::dimension::{Dimension, LengthPercentage, LengthPercentageAuto}; #[cfg(feature = "block_layout")] pub use self::block::{BlockContainerStyle, BlockItemStyle, TextAlign}; #[cfg(feature = "builder")] -pub use self::builder::{NodeIdRef, StyleBuilder}; +pub use self::builder::StyleBuilder; #[cfg(feature = "flexbox")] pub use self::flex::{FlexDirection, FlexWrap, FlexboxContainerStyle, FlexboxItemStyle}; #[cfg(feature = "grid")]