Refactor `LayoutTree` + add examples of custom trees (#564)
* Rename layout_mut to unrounded_layout_mut
* Make TaffyView private
* Add cache_mut to LayoutTree + make ompute methods generic again
* Move provided LayoutTree methods to private LayoutTreeExt trait
* Add inline(always) to LayoutTreeExt methods
* Rename node parameters in the LayoutTree trait to node_id
* Make compute_layout function public
* Simplify function signature of compute_leaf_layout
* Remove cumulative_x and cumulative_y parameters from round_layout function
* Split measure functions out of the measure example
* Add example of custom layout tree (Vec based)
* Add example of custom layout tree (owned children based)
* Fix clippy lints
* Split LayoutTree trait into LayoutTree and PartialLayoutTree traits
* PartialLayoutTree: rename children method to child_ids
* PartialLayoutTree: rename child method to get_child_id
* PartialLayoutTree: rename parameters of children methods
* PartialLayoutTree: rename style method to get_style
* PartialLayoutTree: rename unrounded_layout_mut method to get_unrounded_layout_mut
* PartialLayoutTree: rename final_layout_mut method to get_final_layout_mut
* PartialLayoutTree: rename cache_mut method to get_cache_mut
* LayoutTree: rename final_layout method to get_final_layout
* Remove compute_layout_with_rounding method
* Move get_final_layout_mut method from PartialLayoutTree to LayoutTree
* Make compute_layout_cached function take callback
* Make compute_cached_layout public
* Push caching into compute_child_layout
* Add inline(always)
* Make TaffyChildIter private
* Make CacheEntry private
* Add caching to examples
d4374b93f0ec2c0ace42d3feafec22d3af1940d8 · por Nico Burns · 2023-10-22T19:41:36Z (hace 3 años) · padre 23ff47756 · ver el árbol en este commit
Delta semántico · 156
examples/custom_layout_tree_owned.rs
examples/custom_layout_tree_vec.rs
src/compute/grid/alignment.rs
src/compute/grid/track_sizing.rs
src/compute/grid/types/grid_item.rs
src/compute/taffy_tree.rs
src/tree/taffy_tree/tree.rs
diff --git a/RELEASES.md b/RELEASES.md
index 10f3bf388..3fb26f800 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -97,7 +97,7 @@ Example usage change:
### Removed
- `layout_flexbox()` has been removed from the prelude. Use `taffy::compute_flexbox_layout` instead.
-- The following methods have been removed from the `LayoutTree` trait: `parent`, `is_childless`, `layout`, `measure_node`, `needs_measure`, `cache_mut` and `mark_dirty`. These no longer need to be implemented in custom implementations of `LayoutTree`.
+- The following methods have been removed from the `LayoutTree` trait: `parent`, `is_childless`, `measure_node`, `needs_measure`, and `mark_dirty`. These no longer need to be implemented in custom implementations of `LayoutTree`.
### Changes
diff --git a/examples/common/image.rs b/examples/common/image.rs
new file mode 100644
index 000000000..889e6b38d
--- /dev/null
+++ b/examples/common/image.rs
@@ -0,0 +1,18 @@
+use taffy::geometry::Size;
+
+pub struct ImageContext {
+ pub width: f32,
+ pub height: f32,
+}
+
+pub fn image_measure_function(
+ known_dimensions: taffy::geometry::Size<Option<f32>>,
+ image_context: &ImageContext,
+) -> taffy::geometry::Size<f32> {
+ match (known_dimensions.width, known_dimensions.height) {
+ (Some(width), Some(height)) => Size { width, height },
+ (Some(width), None) => Size { width, height: (width / image_context.width) * image_context.height },
+ (None, Some(height)) => Size { width: (height / image_context.height) * image_context.width, height },
+ (None, None) => Size { width: image_context.width, height: image_context.height },
+ }
+}
diff --git a/examples/common/text.rs b/examples/common/text.rs
new file mode 100644
index 000000000..cde02c4cd
--- /dev/null
+++ b/examples/common/text.rs
@@ -0,0 +1,70 @@
+pub const LOREM_IPSUM : &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+
+pub struct FontMetrics {
+ pub char_width: f32,
+ pub char_height: f32,
+}
+
+#[allow(dead_code)]
+pub enum WritingMode {
+ Horizontal,
+ Vertical,
+}
+
+pub struct TextContext {
+ pub text_content: String,
+ pub writing_mode: WritingMode,
+}
+
+pub fn text_measure_function(
+ known_dimensions: taffy::geometry::Size<Option<f32>>,
+ available_space: taffy::geometry::Size<taffy::style::AvailableSpace>,
+ text_context: &TextContext,
+ font_metrics: &FontMetrics,
+) -> taffy::geometry::Size<f32> {
+ use taffy::geometry::AbsoluteAxis;
+ use taffy::prelude::*;
+
+ let inline_axis = match text_context.writing_mode {
+ WritingMode::Horizontal => AbsoluteAxis::Horizontal,
+ WritingMode::Vertical => AbsoluteAxis::Vertical,
+ };
+ let block_axis = inline_axis.other_axis();
+ let words: Vec<&str> = text_context.text_content.split_whitespace().collect();
+
+ if words.is_empty() {
+ return Size::ZERO;
+ }
+
+ let min_line_length: usize = words.iter().map(|line| line.len()).max().unwrap_or(0);
+ let max_line_length: usize = words.iter().map(|line| line.len()).sum();
+ let inline_size =
+ known_dimensions.get_abs(inline_axis).unwrap_or_else(|| match available_space.get_abs(inline_axis) {
+ AvailableSpace::MinContent => min_line_length as f32 * font_metrics.char_width,
+ AvailableSpace::MaxContent => max_line_length as f32 * font_metrics.char_width,
+ AvailableSpace::Definite(inline_size) => inline_size
+ .min(max_line_length as f32 * font_metrics.char_width)
+ .max(min_line_length as f32 * font_metrics.char_width),
+ });
+ let block_size = known_dimensions.get_abs(block_axis).unwrap_or_else(|| {
+ let inline_line_length = (inline_size / font_metrics.char_width).floor() as usize;
+ let mut line_count = 1;
+ let mut current_line_length = 0;
+ for word in &words {
+ if current_line_length + word.len() > inline_line_length {
+ if current_line_length > 0 {
+ line_count += 1
+ };
+ current_line_length = word.len();
+ } else {
+ current_line_length += word.len();
+ };
+ }
+ (line_count as f32) * font_metrics.char_height
+ });
+
+ match text_context.writing_mode {
+ WritingMode::Horizontal => Size { width: inline_size, height: block_size },
+ WritingMode::Vertical => Size { width: block_size, height: inline_size },
+ }
+}
diff --git a/examples/custom_layout_tree_owned.rs b/examples/custom_layout_tree_owned.rs
new file mode 100644
index 000000000..59a384a5e
--- /dev/null
+++ b/examples/custom_layout_tree_owned.rs
@@ -0,0 +1,200 @@
+mod common {
+ pub mod image;
+ pub mod text;
+}
+use common::image::{image_measure_function, ImageContext};
+use common::text::{text_measure_function, FontMetrics, TextContext, WritingMode, LOREM_IPSUM};
+use taffy::tree::{Cache, PartialLayoutTree};
+use taffy::util::print_tree;
+use taffy::{
+ compute_cached_layout, compute_flexbox_layout, compute_grid_layout, compute_layout, compute_leaf_layout,
+ prelude::*, round_layout,
+};
+
+#[derive(Debug, Copy, Clone)]
+#[allow(dead_code)]
+enum NodeKind {
+ Flexbox,
+ Grid,
+ Text,
+ Image,
+}
+
+struct Node {
+ kind: NodeKind,
+ style: Style,
+ text_data: Option<TextContext>,
+ image_data: Option<ImageContext>,
+ cache: Cache,
+ unrounded_layout: Layout,
+ final_layout: Layout,
+ children: Vec<Node>,
+}
+
+impl Default for Node {
+ fn default() -> Self {
+ Node {
+ kind: NodeKind::Flexbox,
+ style: Style::default(),
+ text_data: None,
+ image_data: None,
+ cache: Cache::new(),
+ unrounded_layout: Layout::with_order(0),
+ final_layout: Layout::with_order(0),
+ children: Vec::new(),
+ }
+ }
+}
+
+#[allow(dead_code)]
+impl Node {
+ pub fn new_row(style: Style) -> Node {
+ Node {
+ kind: NodeKind::Flexbox,
+ style: Style { display: Display::Flex, flex_direction: FlexDirection::Row, ..style },
+ ..Node::default()
+ }
+ }
+ pub fn new_column(style: Style) -> Node {
+ Node {
+ kind: NodeKind::Flexbox,
+ style: Style { display: Display::Flex, flex_direction: FlexDirection::Column, ..style },
+ ..Node::default()
+ }
+ }
+ pub fn new_grid(style: Style) -> Node {
+ Node { kind: NodeKind::Grid, style: Style { display: Display::Grid, ..style }, ..Node::default() }
+ }
+ pub fn new_text(style: Style, text_data: TextContext) -> Node {
+ Node { kind: NodeKind::Text, style, text_data: Some(text_data), ..Node::default() }
+ }
+ pub fn new_image(style: Style, image_data: ImageContext) -> Node {
+ Node { kind: NodeKind::Image, style, image_data: Some(image_data), ..Node::default() }
+ }
+ pub fn append_child(&mut self, node: Node) {
+ self.children.push(node);
+ }
+
+ unsafe fn as_id(&self) -> NodeId {
+ NodeId::from(self as *const Node as usize)
+ }
+
+ pub fn compute_layout(&mut self, available_space: Size<AvailableSpace>, use_rounding: bool) {
+ let root_node_id = unsafe { self.as_id() };
+ compute_layout(&mut StatelessLayoutTree, root_node_id, available_space);
+ if use_rounding {
+ round_layout(&mut StatelessLayoutTree, root_node_id)
+ }
+ }
+
+ pub fn print_tree(&mut self) {
+ print_tree(&mut StatelessLayoutTree, unsafe { self.as_id() });
+ }
+}
+
+struct ChildIter<'a>(std::slice::Iter<'a, Node>);
+impl<'a> Iterator for ChildIter<'a> {
+ type Item = NodeId;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next().map(|c| NodeId::from(c as *const Node as usize))
+ }
+}
+
+#[inline(always)]
+unsafe fn node_from_id<'a>(node_id: NodeId) -> &'a Node {
+ &*(usize::from(node_id) as *const Node)
+}
+
+#[inline(always)]
+unsafe fn node_from_id_mut<'a>(node_id: NodeId) -> &'a mut Node {
+ &mut *(usize::from(node_id) as *mut Node)
+}
+
+struct StatelessLayoutTree;
+impl PartialLayoutTree for StatelessLayoutTree {
+ type ChildIter<'a> = ChildIter<'a>;
+
+ fn child_ids(&self, node_id: NodeId) -> Self::ChildIter<'_> {
+ unsafe { ChildIter(node_from_id(node_id).children.iter()) }
+ }
+
+ fn child_count(&self, node_id: NodeId) -> usize {
+ unsafe { node_from_id(node_id).children.len() }
+ }
+
+ fn get_child_id(&self, node_id: NodeId, index: usize) -> NodeId {
+ unsafe { node_from_id(node_id).children[index].as_id() }
+ }
+
+ fn get_style(&self, node_id: NodeId) -> &Style {
+ unsafe { &node_from_id(node_id).style }
+ }
+
+ fn get_unrounded_layout_mut(&mut self, node_id: NodeId) -> &mut Layout {
+ unsafe { &mut node_from_id_mut(node_id).unrounded_layout }
+ }
+
+ fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache {
+ unsafe { &mut node_from_id_mut(node_id).cache }
+ }
+
+ fn compute_child_layout(&mut self, node_id: NodeId, inputs: taffy::tree::LayoutInput) -> taffy::tree::LayoutOutput {
+ compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
+ let node = unsafe { node_from_id_mut(node_id) };
+ let font_metrics = FontMetrics { char_width: 10.0, char_height: 10.0 };
+
+ match node.kind {
+ NodeKind::Flexbox => compute_flexbox_layout(tree, node_id, inputs),
+ NodeKind::Grid => compute_grid_layout(tree, node_id, inputs),
+ NodeKind::Text => compute_leaf_layout(
+ inputs,
+ &node.style,
+ Some(|known_dimensions, available_space| {
+ text_measure_function(
+ known_dimensions,
+ available_space,
+ node.text_data.as_ref().unwrap(),
+ &font_metrics,
+ )
+ }),
+ ),
+ NodeKind::Image => compute_leaf_layout(
+ inputs,
+ &node.style,
+ Some(|known_dimensions, _available_space| {
+ image_measure_function(known_dimensions, node.image_data.as_ref().unwrap())
+ }),
+ ),
+ }
+ })
+ }
+}
+
+impl LayoutTree for StatelessLayoutTree {
+ fn get_final_layout(&self, node_id: NodeId) -> &Layout {
+ unsafe { &node_from_id(node_id).final_layout }
+ }
+
+ fn get_final_layout_mut(&mut self, node_id: NodeId) -> &mut Layout {
+ unsafe { &mut node_from_id_mut(node_id).final_layout }
+ }
+}
+
+fn main() -> Result<(), taffy::TaffyError> {
+ let mut root = Node::new_column(Style::DEFAULT);
+
+ let text_node = Node::new_text(
+ Style::default(),
+ TextContext { text_content: LOREM_IPSUM.into(), writing_mode: WritingMode::Horizontal },
+ );
+ root.append_child(text_node);
+
+ let image_node = Node::new_image(Style::default(), ImageContext { width: 400.0, height: 300.0 });
+ root.append_child(image_node);
+
+ // Compute layout and print result
+ root.compute_layout(Size::MAX_CONTENT, true);
+ root.print_tree();
+
+ Ok(())
+}
diff --git a/examples/custom_layout_tree_vec.rs b/examples/custom_layout_tree_vec.rs
new file mode 100644
index 000000000..67b7ef94d
--- /dev/null
+++ b/examples/custom_layout_tree_vec.rs
@@ -0,0 +1,215 @@
+mod common {
+ pub mod image;
+ pub mod text;
+}
+use common::image::{image_measure_function, ImageContext};
+use common::text::{text_measure_function, FontMetrics, TextContext, WritingMode, LOREM_IPSUM};
+use taffy::tree::{Cache, PartialLayoutTree};
+use taffy::util::print_tree;
+use taffy::{
+ compute_cached_layout, compute_flexbox_layout, compute_grid_layout, compute_layout, compute_leaf_layout,
+ prelude::*, round_layout,
+};
+
+#[derive(Debug, Copy, Clone)]
+#[allow(dead_code)]
+enum NodeKind {
+ Flexbox,
+ Grid,
+ Text,
+ Image,
+}
+
+struct Node {
+ kind: NodeKind,
+ style: Style,
+ text_data: Option<TextContext>,
+ image_data: Option<ImageContext>,
+ cache: Cache,
+ unrounded_layout: Layout,
+ final_layout: Layout,
+ children: Vec<usize>,
+}
+
+impl Default for Node {
+ fn default() -> Self {
+ Node {
+ kind: NodeKind::Flexbox,
+ style: Style::default(),
+ text_data: None,
+ image_data: None,
+ cache: Cache::new(),
+ unrounded_layout: Layout::with_order(0),
+ final_layout: Layout::with_order(0),
+ children: Vec::new(),
+ }
+ }
+}
+
+#[allow(dead_code)]
+impl Node {
+ pub fn new_row(style: Style) -> Node {
+ Node {
+ kind: NodeKind::Flexbox,
+ style: Style { display: Display::Flex, flex_direction: FlexDirection::Row, ..style },
+ ..Node::default()
+ }
+ }
+ pub fn new_column(style: Style) -> Node {
+ Node {
+ kind: NodeKind::Flexbox,
+ style: Style { display: Display::Flex, flex_direction: FlexDirection::Column, ..style },
+ ..Node::default()
+ }
+ }
+ pub fn new_grid(style: Style) -> Node {
+ Node { kind: NodeKind::Grid, style: Style { display: Display::Grid, ..style }, ..Node::default() }
+ }
+ pub fn new_text(style: Style, text_data: TextContext) -> Node {
+ Node { kind: NodeKind::Text, style, text_data: Some(text_data), ..Node::default() }
+ }
+ pub fn new_image(style: Style, image_data: ImageContext) -> Node {
+ Node { kind: NodeKind::Image, style, image_data: Some(image_data), ..Node::default() }
+ }
+}
+
+struct Tree {
+ nodes: Vec<Node>,
+}
+
+impl Tree {
+ pub fn new() -> Tree {
+ Tree { nodes: Vec::new() }
+ }
+
+ pub fn add_node(&mut self, node: Node) -> usize {
+ self.nodes.push(node);
+ self.nodes.len() - 1
+ }
+
+ pub fn append_child(&mut self, parent: usize, child: usize) {
+ self.nodes[parent].children.push(child);
+ }
+
+ #[inline(always)]
+ fn node_from_id(&self, node_id: NodeId) -> &Node {
+ &self.nodes[usize::from(node_id)]
+ }
+
+ #[inline(always)]
+ fn node_from_id_mut(&mut self, node_id: NodeId) -> &mut Node {
+ &mut self.nodes[usize::from(node_id)]
+ }
+
+ pub fn compute_layout(&mut self, root: usize, available_space: Size<AvailableSpace>, use_rounding: bool) {
+ compute_layout(self, NodeId::from(root), available_space);
+ if use_rounding {
+ round_layout(self, NodeId::from(root))
+ }
+ }
+
+ pub fn print_tree(&mut self, root: usize) {
+ print_tree(self, NodeId::from(root));
+ }
+}
+
+struct ChildIter<'a>(std::slice::Iter<'a, usize>);
+impl<'a> Iterator for ChildIter<'a> {
+ type Item = NodeId;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next().copied().map(NodeId::from)
+ }
+}
+
+impl PartialLayoutTree for Tree {
+ type ChildIter<'a> = ChildIter<'a>;
+
+ fn child_ids(&self, node_id: NodeId) -> Self::ChildIter<'_> {
+ ChildIter(self.node_from_id(node_id).children.iter())
+ }
+
+ fn child_count(&self, node_id: NodeId) -> usize {
+ self.node_from_id(node_id).children.len()
+ }
+
+ fn get_child_id(&self, node_id: NodeId, index: usize) -> NodeId {
+ NodeId::from(self.node_from_id(node_id).children[index])
+ }
+
+ fn get_style(&self, node_id: NodeId) -> &Style {
+ &self.node_from_id(node_id).style
+ }
+
+ fn get_unrounded_layout_mut(&mut self, node_id: NodeId) -> &mut Layout {
+ &mut self.node_from_id_mut(node_id).unrounded_layout
+ }
+
+ fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache {
+ &mut self.node_from_id_mut(node_id).cache
+ }
+
+ fn compute_child_layout(&mut self, node_id: NodeId, inputs: taffy::tree::LayoutInput) -> taffy::tree::LayoutOutput {
+ compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
+ let node = tree.node_from_id_mut(node_id);
+ let font_metrics = FontMetrics { char_width: 10.0, char_height: 10.0 };
+
+ match node.kind {
+ NodeKind::Flexbox => compute_flexbox_layout(tree, node_id, inputs),
+ NodeKind::Grid => compute_grid_layout(tree, node_id, inputs),
+ NodeKind::Text => compute_leaf_layout(
+ inputs,
+ &node.style,
+ Some(|known_dimensions, available_space| {
+ text_measure_function(
+ known_dimensions,
+ available_space,
+ node.text_data.as_ref().unwrap(),
+ &font_metrics,
+ )
+ }),
+ ),
+ NodeKind::Image => compute_leaf_layout(
+ inputs,
+ &node.style,
+ Some(|known_dimensions, _available_space| {
+ image_measure_function(known_dimensions, node.image_data.as_ref().unwrap())
+ }),
+ ),
+ }
+ })
+ }
+}
+
+impl LayoutTree for Tree {
+ fn get_final_layout(&self, node_id: NodeId) -> &Layout {
+ &self.node_from_id(node_id).final_layout
+ }
+
+ fn get_final_layout_mut(&mut self, node_id: NodeId) -> &mut Layout {
+ &mut self.node_from_id_mut(node_id).final_layout
+ }
+}
+
+fn main() -> Result<(), taffy::TaffyError> {
+ let mut tree = Tree::new();
+
+ let root = Node::new_column(Style::DEFAULT);
+ let root_id = tree.add_node(root);
+
+ let text_node = Node::new_text(
+ Style::default(),
+ TextContext { text_content: LOREM_IPSUM.into(), writing_mode: WritingMode::Horizontal },
+ );
+ let text_node_id = tree.add_node(text_node);
+ tree.append_child(root_id, text_node_id);
+
+ let image_node = Node::new_image(Style::default(), ImageContext { width: 400.0, height: 300.0 });
+ let image_node_id = tree.add_node(image_node);
+ tree.append_child(root_id, image_node_id);
+
+ // Compute layout and print result
+ tree.compute_layout(root_id, Size::MAX_CONTENT, true);
+ tree.print_tree(root_id);
+
+ Ok(())
+}
diff --git a/examples/measure.rs b/examples/measure.rs
index 5df8b7222..48d67546b 100644
--- a/examples/measure.rs
+++ b/examples/measure.rs
@@ -1,28 +1,35 @@
+mod common {
+ pub mod image;
+ pub mod text;
+}
+use common::image::{image_measure_function, ImageContext};
+use common::text::{text_measure_function, FontMetrics, TextContext, WritingMode, LOREM_IPSUM};
use taffy::prelude::*;
-const LOREM_IPSUM : &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
-
-struct FontMetrics {
- char_width: f32,
- char_height: f32,
-}
-enum WritingMode {
- Horizontal,
- Vertical,
-}
-struct TextContext {
- text_content: String,
- writing_mode: WritingMode,
-}
-struct ImageContext {
- width: f32,
- height: f32,
-}
enum NodeContext {
Text(TextContext),
Image(ImageContext),
}
+fn measure_function(
+ known_dimensions: taffy::geometry::Size<Option<f32>>,
+ available_space: taffy::geometry::Size<taffy::style::AvailableSpace>,
+ node_context: Option<&mut NodeContext>,
+ font_metrics: &FontMetrics,
+) -> Size<f32> {
+ if let Size { width: Some(width), height: Some(height) } = known_dimensions {
+ return Size { width, height };
+ }
+
+ match node_context {
+ None => Size::ZERO,
+ Some(NodeContext::Text(text_context)) => {
+ text_measure_function(known_dimensions, available_space, &*text_context, font_metrics)
+ }
+ Some(NodeContext::Image(image_context)) => image_measure_function(known_dimensions, image_context),
+ }
+}
+
fn main() -> Result<(), taffy::TaffyError> {
let mut taffy: Taffy<NodeContext> = Taffy::new();
@@ -60,87 +67,3 @@ fn main() -> Result<(), taffy::TaffyError> {
Ok(())
}
-
-fn measure_function(
- known_dimensions: taffy::geometry::Size<Option<f32>>,
- available_space: taffy::geometry::Size<taffy::style::AvailableSpace>,
- node_context: Option<&mut NodeContext>,
- font_metrics: &FontMetrics,
-) -> Size<f32> {
- if let Size { width: Some(width), height: Some(height) } = known_dimensions {
- return Size { width, height };
- }
-
- match node_context {
- None => Size::ZERO,
- Some(NodeContext::Text(text_context)) => {
- text_measure_function(known_dimensions, available_space, &*text_context, font_metrics)
- }
- Some(NodeContext::Image(image_context)) => image_measure_function(known_dimensions, image_context),
- }
-}
-
-fn image_measure_function(
- known_dimensions: taffy::geometry::Size<Option<f32>>,
- image_context: &ImageContext,
-) -> taffy::geometry::Size<f32> {
- match (known_dimensions.width, known_dimensions.height) {
- (Some(width), Some(height)) => Size { width, height },
- (Some(width), None) => Size { width, height: (width / image_context.width) * image_context.height },
- (None, Some(height)) => Size { width: (height / image_context.height) * image_context.width, height },
- (None, None) => Size { width: image_context.width, height: image_context.height },
- }
-}
-
-fn text_measure_function(
- known_dimensions: taffy::geometry::Size<Option<f32>>,
- available_space: taffy::geometry::Size<taffy::style::AvailableSpace>,
- text_context: &TextContext,
- font_metrics: &FontMetrics,
-) -> taffy::geometry::Size<f32> {
- use taffy::geometry::AbsoluteAxis;
- use taffy::prelude::*;
-
- let inline_axis = match text_context.writing_mode {
- WritingMode::Horizontal => AbsoluteAxis::Horizontal,
- WritingMode::Vertical => AbsoluteAxis::Vertical,
- };
- let block_axis = inline_axis.other_axis();
- let words: Vec<&str> = text_context.text_content.split_whitespace().collect();
-
- if words.is_empty() {
- return Size::ZERO;
- }
-
- let min_line_length: usize = words.iter().map(|line| line.len()).max().unwrap_or(0);
- let max_line_length: usize = words.iter().map(|line| line.len()).sum();
- let inline_size =
- known_dimensions.get_abs(inline_axis).unwrap_or_else(|| match available_space.get_abs(inline_axis) {
- AvailableSpace::MinContent => min_line_length as f32 * font_metrics.char_width,
- AvailableSpace::MaxContent => max_line_length as f32 * font_metrics.char_width,
- AvailableSpace::Definite(inline_size) => inline_size
- .min(max_line_length as f32 * font_metrics.char_width)
- .max(min_line_length as f32 * font_metrics.char_width),
- });
- let block_size = known_dimensions.get_abs(block_axis).unwrap_or_else(|| {
- let inline_line_length = (inline_size / font_metrics.char_width).floor() as usize;
- let mut line_count = 1;
- let mut current_line_length = 0;
- for word in &words {
- if current_line_length + word.len() > inline_line_length {
- if current_line_length > 0 {
- line_count += 1
- };
- current_line_length = word.len();
- } else {
- current_line_length += word.len();
- };
- }
- (line_count as f32) * font_metrics.char_height
- });
-
- match text_context.writing_mode {
- WritingMode::Horizontal => Size { width: inline_size, height: block_size },
- WritingMode::Vertical => Size { width: block_size, height: inline_size },
- }
-}
diff --git a/src/compute/block.rs b/src/compute/block.rs
index 19fbb3baf..80c8e96e1 100644
--- a/src/compute/block.rs
+++ b/src/compute/block.rs
@@ -3,7 +3,7 @@ use crate::geometry::{Line, Point, Rect, Size};
use crate::style::{AvailableSpace, Display, LengthPercentageAuto, Overflow, Position};
use crate::style_helpers::TaffyMaxContent;
use crate::tree::{CollapsibleMarginSet, Layout, LayoutInput, LayoutOutput, RunMode, SizingMode};
-use crate::tree::{LayoutTree, NodeId};
+use crate::tree::{NodeId, PartialLayoutTree, PartialLayoutTreeExt};
use crate::util::debug::debug_log;
use crate::util::sys::f32_max;
use crate::util::sys::Vec;
@@ -44,10 +44,10 @@ struct BlockItem {
can_be_collapsed_through: bool,
}
-/// Computes the layout of [`LayoutTree`] according to the block layout algorithm
-pub fn compute_block_layout(tree: &mut impl LayoutTree, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
+/// Computes the layout of [`PartialLayoutTree`] according to the block layout algorithm
+pub fn compute_block_layout(tree: &mut impl PartialLayoutTree, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs;
- let style = tree.style(node_id);
+ let style = tree.get_style(node_id);
// Pull these out earlier to avoid borrowing issues
let aspect_ratio = style.aspect_ratio;
@@ -88,13 +88,13 @@ pub fn compute_block_layout(tree: &mut impl LayoutTree, node_id: NodeId, inputs:
compute_inner(tree, node_id, LayoutInput { known_dimensions: styled_based_known_dimensions, ..inputs })
}
-/// Computes the layout of [`LayoutTree`] according to the block layout algorithm
-fn compute_inner(tree: &mut impl LayoutTree, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
+/// Computes the layout of [`PartialLayoutTree`] according to the block layout algorithm
+fn compute_inner(tree: &mut impl PartialLayoutTree, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput {
let LayoutInput {
known_dimensions, parent_size, available_space, run_mode, vertical_margins_are_collapsible, ..
} = inputs;
- let style = tree.style(node_id);
+ let style = tree.get_style(node_id);
let raw_padding = style.padding;
let raw_border = style.border;
let raw_margin = style.margin;
@@ -195,9 +195,9 @@ fn compute_inner(tree: &mut impl LayoutTree, node_id: NodeId, inputs: LayoutInpu
// 5. Perform hidden layout on hidden children
let len = tree.child_count(node_id);
for order in 0..len {
- let child = tree.child(node_id, order);
- if tree.style(child).display == Display::None {
- *tree.layout_mut(child) = Layout::with_order(order as u32);
+ let child = tree.get_child_id(node_id, order);
+ if tree.get_style(child).display == Display::None {
+ *tree.get_unrounded_layout_mut(child) = Layout::with_order(order as u32);
tree.perform_child_layout(
child,
Size::NONE,
@@ -236,9 +236,13 @@ fn compute_inner(tree: &mut impl LayoutTree, node_id: NodeId, inputs: LayoutInpu
/// Create a `Vec` of `BlockItem` structs where each item in the `Vec` represents a child of the current node
#[inline]
-fn generate_item_list(tree: &impl LayoutTree, node: NodeId, node_inner_size: Size<Option<f32>>) -> Vec<BlockItem> {
- tree.children(node)
- .map(|child_node_id| (child_node_id, tree.style(child_node_id)))
+fn generate_item_list(
+ tree: &impl PartialLayoutTree,
+ node: NodeId,
+ node_inner_size: Size<Option<f32>>,
+) -> Vec<BlockItem> {
+ tree.child_ids(node)
+ .map(|child_node_id| (child_node_id, tree.get_style(child_node_id)))
.filter(|(_, style)| style.display != Display::None)
.enumerate()
.map(|(order, (child_node_id, child_style))| {
@@ -269,7 +273,7 @@ fn generate_item_list(tree: &impl LayoutTree, node: NodeId, node_inner_size: Siz
/// Compute the content-based width in the case that the width of the container is not known
#[inline]
fn determine_content_based_container_width(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
items: &[BlockItem],
available_width: AvailableSpace,
) -> f32 {
@@ -304,7 +308,7 @@ fn determine_content_based_container_width(
/// Compute each child's final size and position
#[inline]
fn perform_final_layout_on_in_flow_children(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
items: &mut [BlockItem],
container_outer_width: f32,
content_box_inset: Rect<f32>,
@@ -388,7 +392,7 @@ fn perform_final_layout_on_in_flow_children(
y: committed_y_offset + active_collapsible_margin_set.resolve(),
};
- *tree.layout_mut(item.node_id) = Layout {
+ *tree.get_unrounded_layout_mut(item.node_id) = Layout {
order: item.order,
size: item_layout.size,
location: Point {
@@ -433,7 +437,7 @@ fn perform_final_layout_on_in_flow_children(
/// Perform absolute layout on all absolutely positioned children.
#[inline]
fn perform_absolute_layout_on_absolute_children(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
items: &[BlockItem],
area_size: Size<f32>,
area_offset: Point<f32>,
@@ -442,7 +446,7 @@ fn perform_absolute_layout_on_absolute_children(
let area_height = area_size.height;
for item in items.iter().filter(|item| item.position == Position::Absolute) {
- let child_style = tree.style(item.node_id);
+ let child_style = tree.get_style(item.node_id);
// Skip items that are display:none or are not position:absolute
if child_style.display == Display::None || child_style.position != Position::Absolute {
@@ -587,7 +591,7 @@ fn perform_absolute_layout_on_absolute_children(
.unwrap_or(item.static_position.y + resolved_margin.top),
};
- *tree.layout_mut(item.node_id) =
+ *tree.get_unrounded_layout_mut(item.node_id) =
Layout { order: item.order, size: final_size, location: area_offset + item_offset };
}
}
diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs
index e440e4fad..94e543574 100644
--- a/src/compute/flexbox.rs
+++ b/src/compute/flexbox.rs
@@ -8,7 +8,7 @@ use crate::style::{
};
use crate::style::{FlexDirection, Style};
use crate::tree::{Layout, LayoutInput, LayoutOutput, RunMode, SizingMode};
-use crate::tree::{LayoutTree, NodeId};
+use crate::tree::{NodeId, PartialLayoutTree, PartialLayoutTreeExt};
use crate::util::debug::debug_log;
use crate::util::sys::Vec;
use crate::util::sys::{f32_max, new_vec_with_capacity};
@@ -147,10 +147,10 @@ struct AlgoConstants {
inner_container_size: Size<f32>,
}
-/// Computes the layout of [`LayoutTree`] according to the flexbox algorithm
-pub fn compute_flexbox_layout(tree: &mut impl LayoutTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
+/// Computes the layout of [`PartialLayoutTree`] according to the flexbox algorithm
+pub fn compute_flexbox_layout(tree: &mut impl PartialLayoutTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
let LayoutInput { known_dimensions, parent_size, run_mode, .. } = inputs;
- let style = tree.style(node);
+ let style = tree.get_style(node);
// Pull these out earlier to avoid borrowing issues
let aspect_ratio = style.aspect_ratio;
@@ -179,11 +179,11 @@ pub fn compute_flexbox_layout(tree: &mut impl LayoutTree, node: NodeId, inputs:
}
/// Compute a preliminary size for an item
-fn compute_preliminary(tree: &mut impl LayoutTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
+fn compute_preliminary(tree: &mut impl PartialLayoutTree, 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.
- let mut constants = compute_constants(tree.style(node), known_dimensions, parent_size);
+ let mut constants = compute_constants(tree.get_style(node), known_dimensions, parent_size);
// 9. Flex Layout Algorithm
@@ -239,7 +239,7 @@ fn compute_preliminary(tree: &mut impl LayoutTree, node: NodeId, inputs: LayoutI
debug_log!("constants.node_inner_size", dbg:constants.node_inner_size);
// Re-resolve percentage gaps
- let style = tree.style(node);
+ let style = tree.get_style(node);
let inner_container_size = constants.inner_container_size.main(constants.dir);
let new_gap = style.gap.main(constants.dir).maybe_resolve(inner_container_size).unwrap_or(0.0);
constants.gap.set_main(constants.dir, new_gap);
@@ -328,9 +328,9 @@ fn compute_preliminary(tree: &mut impl LayoutTree, node: NodeId, inputs: LayoutI
debug_log!("hidden_layout");
let len = tree.child_count(node);
for order in 0..len {
- let child = tree.child(node, order);
- if tree.style(child).display == Display::None {
- *tree.layout_mut(child) = Layout::with_order(order as u32);
+ let child = tree.get_child_id(node, order);
+ if tree.get_style(child).display == Display::None {
+ *tree.get_unrounded_layout_mut(child) = Layout::with_order(order as u32);
tree.perform_child_layout(
child,
Size::NONE,
@@ -430,10 +430,14 @@ fn compute_constants(
///
/// - [**Generate anonymous flex items**](https://www.w3.org/TR/css-flexbox-1/#algo-anon-box) as described in [§4 Flex Items](https://www.w3.org/TR/css-flexbox-1/#flex-items).
#[inline]
-fn generate_anonymous_flex_items(tree: &impl LayoutTree, node: NodeId, constants: &AlgoConstants) -> Vec<FlexItem> {
- tree.children(node)
+fn generate_anonymous_flex_items(
+ tree: &impl PartialLayoutTree,
+ node: NodeId,
+ constants: &AlgoConstants,
+) -> Vec<FlexItem> {
+ tree.child_ids(node)
.enumerate()
- .map(|(index, child)| (index, child, tree.style(child)))
+ .map(|(index, child)| (index, child, tree.get_style(child)))
.filter(|(_, _, style)| style.position != Position::Absolute)
.filter(|(_, _, style)| style.display != Display::None)
.map(|(index, child, child_style)| {
@@ -546,7 +550,7 @@ fn determine_available_space(
/// (For example, an item with a specified size of zero, positive padding, and box-sizing: border-box will have an outer flex base size of zero—and hence a negative inner flex base size.)
#[inline]
fn determine_flex_base_size(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
constants: &AlgoConstants,
available_space: Size<AvailableSpace>,
flex_items: &mut [FlexItem],
@@ -554,7 +558,7 @@ fn determine_flex_base_size(
let dir = constants.dir;
for child in flex_items.iter_mut() {
- let child_style = tree.style(child.node);
+ let child_style = tree.get_style(child.node);
// Parent size for child sizing
let cross_axis_parent_size = constants.node_inner_size.cross(dir);
@@ -785,7 +789,7 @@ fn collect_flex_lines<'a>(
/// Determine the container's main size (if not already known)
fn determine_container_main_size(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
available_space: Size<AvailableSpace>,
lines: &mut Vec<FlexLine<'_>>,
constants: &mut AlgoConstants,
@@ -1193,7 +1197,7 @@ fn resolve_flexible_lengths(line: &mut FlexLine, constants: &AlgoConstants, orig
/// by performing layout with the used main size and the available space, treating auto as fit-content.
#[inline]
fn determine_hypothetical_cross_size(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
line: &mut FlexLine,
constants: &AlgoConstants,
available_space: Size<AvailableSpace>,
@@ -1243,7 +1247,7 @@ fn determine_hypothetical_cross_size(
/// Calculate the base lines of the children.
#[inline]
fn calculate_children_base_lines(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
node_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
flex_lines: &mut [FlexLine],
@@ -1428,12 +1432,12 @@ fn handle_align_content_stretch(flex_lines: &mut [FlexLine], node_size: Size<Opt
///
/// **Note that this step does not affect the main size of the flex item, even if it has an intrinsic aspect ratio**.
#[inline]
-fn determine_used_cross_size(tree: &impl LayoutTree, flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
+fn determine_used_cross_size(tree: &impl PartialLayoutTree, flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
for line in flex_lines {
let line_cross_size = line.cross_size;
for child in line.items.iter_mut() {
- let child_style = tree.style(child.node);
+ let child_style = tree.get_style(child.node);
child.target_size.set_cross(
constants.dir,
if child.align_self == AlignSelf::Stretch
@@ -1697,7 +1701,7 @@ fn align_flex_lines_per_align_content(flex_lines: &mut [FlexLine], constants: &A
/// Calculates the layout for a flex-item
#[allow(clippy::too_many_arguments)]
fn calculate_flex_item(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
item: &mut FlexItem,
total_offset_main: &mut f32,
total_offset_cross: f32,
@@ -1737,7 +1741,7 @@ fn calculate_flex_item(
item.baseline = baseline_offset_main + inner_baseline;
}
- *tree.layout_mut(item.node) = Layout {
+ *tree.get_unrounded_layout_mut(item.node) = Layout {
order: item.order,
size: preliminary_size_and_baselines.size,
location: Point {
@@ -1752,7 +1756,7 @@ fn calculate_flex_item(
/// Calculates the layout line
#[allow(clippy::too_many_arguments)]
fn calculate_layout_line(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
line: &mut FlexLine,
total_offset_cross: &mut f32,
container_size: Size<f32>,
@@ -1796,7 +1800,7 @@ fn calculate_layout_line(
/// Do a final layout pass and collect the resulting layouts.
#[inline]
-fn final_layout_pass(tree: &mut impl LayoutTree, flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
+fn final_layout_pass(tree: &mut impl PartialLayoutTree, flex_lines: &mut [FlexLine], constants: &AlgoConstants) {
let mut total_offset_cross = constants.content_box_inset.cross_start(constants.dir);
if constants.is_wrap_reverse {
@@ -1828,13 +1832,17 @@ fn final_layout_pass(tree: &mut impl LayoutTree, flex_lines: &mut [FlexLine], co
/// Perform absolute layout on all absolutely positioned children.
#[inline]
-fn perform_absolute_layout_on_absolute_children(tree: &mut impl LayoutTree, node: NodeId, constants: &AlgoConstants) {
+fn perform_absolute_layout_on_absolute_children(
+ tree: &mut impl PartialLayoutTree,
+ node: NodeId,
+ constants: &AlgoConstants,
+) {
let container_width = constants.container_size.width;
let container_height = constants.container_size.height;
for order in 0..tree.child_count(node) {
- let child = tree.child(node, order);
- let child_style = tree.style(child);
+ let child = tree.get_child_id(node, order);
+ let child_style = tree.get_style(child);
// Skip items that are display:none or are not position:absolute
if child_style.display == Display::None || child_style.position != Position::Absolute {
@@ -2022,7 +2030,7 @@ fn perform_absolute_layout_on_absolute_children(tree: &mut impl LayoutTree, node
}
};
- *tree.layout_mut(child) = Layout {
+ *tree.get_unrounded_layout_mut(child) = Layout {
order: order as u32,
size: final_size,
location: Point {
diff --git a/src/compute/grid/alignment.rs b/src/compute/grid/alignment.rs
index 144aa3316..eaa683bec 100644
--- a/src/compute/grid/alignment.rs
+++ b/src/compute/grid/alignment.rs
@@ -1,14 +1,11 @@
//! Alignment of tracks and final positioning of items
use super::types::GridTrack;
use crate::compute::common::alignment::compute_alignment_offset;
-use crate::geometry::InBothAbsAxis;
-use crate::geometry::{Line, Point, Rect, Size};
+use crate::geometry::{InBothAbsAxis, Line, Point, Rect, Size};
use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, Position};
-use crate::tree::{Layout, SizingMode};
-use crate::tree::{LayoutTree, NodeId};
+use crate::tree::{Layout, NodeId, PartialLayoutTree, PartialLayoutTreeExt, SizingMode};
use crate::util::sys::f32_max;
-use crate::util::MaybeMath;
-use crate::util::{MaybeResolve, ResolveOrZero};
+use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero};
/// Align the grid tracks within the grid according to the align-content (rows) or
/// justify-content (columns) property. This only does anything if the size of the
@@ -54,7 +51,7 @@ pub(super) fn align_tracks(
/// Align and size a grid item into it's final position
pub(super) fn align_and_position_item(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
node: NodeId,
order: u32,
grid_area: Rect<f32>,
@@ -63,7 +60,7 @@ pub(super) fn align_and_position_item(
) {
let grid_area_size = Size { width: grid_area.right - grid_area.left, height: grid_area.bottom - grid_area.top };
- let style = tree.style(node);
+ let style = tree.get_style(node);
let aspect_ratio = style.aspect_ratio;
let justify_self = style.justify_self;
let align_self = style.align_self;
@@ -202,7 +199,7 @@ pub(super) fn align_and_position_item(
baseline_shim,
);
- *tree.layout_mut(node) = Layout { order, size: Size { width, height }, location: Point { x, y } };
+ *tree.get_unrounded_layout_mut(node) = Layout { order, size: Size { width, height }, location: Point { x, y } };
}
/// Align and size a grid item along a single axis
diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs
index 5342102fd..19c11a0ee 100644
--- a/src/compute/grid/mod.rs
+++ b/src/compute/grid/mod.rs
@@ -5,7 +5,7 @@ use crate::geometry::{Line, Point, Rect, Size};
use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, Display, Overflow, Position};
use crate::style_helpers::*;
use crate::tree::{Layout, LayoutInput, LayoutOutput, RunMode, SizingMode};
-use crate::tree::{LayoutTree, NodeId};
+use crate::tree::{NodeId, PartialLayoutTree, PartialLayoutTreeExt};
use crate::util::debug::debug_log;
use crate::util::sys::{f32_max, GridTrackVec, Vec};
use crate::util::MaybeMath;
@@ -35,11 +35,11 @@ mod util;
/// - Placing items (which also resolves the implicit grid)
/// - Track (row/column) sizing
/// - Alignment & Final item placement
-pub fn compute_grid_layout(tree: &mut impl LayoutTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
+pub fn compute_grid_layout(tree: &mut impl PartialLayoutTree, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
let LayoutInput { known_dimensions, parent_size, available_space, run_mode, .. } = inputs;
- let get_child_styles_iter = |node| tree.children(node).map(|child_node: NodeId| tree.style(child_node));
- let style = tree.style(node).clone();
+ let get_child_styles_iter = |node| tree.child_ids(node).map(|child_node: NodeId| tree.get_style(child_node));
+ let style = tree.get_style(node).clone();
let child_styles_iter = get_child_styles_iter(node);
// 1. Resolve the explicit grid
@@ -58,9 +58,9 @@ pub fn compute_grid_layout(tree: &mut impl LayoutTree, node: NodeId, inputs: Lay
let mut items = Vec::with_capacity(tree.child_count(node));
let mut cell_occupancy_matrix = CellOccupancyMatrix::with_track_counts(est_col_counts, est_row_counts);
let in_flow_children_iter = || {
- tree.children(node)
+ tree.child_ids(node)
.enumerate()
- .map(|(index, child_node)| (index, child_node, tree.style(child_node)))
+ .map(|(index, child_node)| (index, child_node, tree.get_style(child_node)))
.filter(|(_, _, style)| style.display != Display::None && style.position != Position::Absolute)
};
place_grid_items(
@@ -419,12 +419,12 @@ pub fn compute_grid_layout(tree: &mut impl LayoutTree, node: NodeId, inputs: Lay
// Position hidden and absolutely positioned children
let mut order = items.len() as u32;
(0..tree.child_count(node)).for_each(|index| {
- let child = tree.child(node, index);
- let child_style = tree.style(child);
+ let child = tree.get_child_id(node, index);
+ let child_style = tree.get_style(child);
// Position hidden child
if child_style.display == Display::None {
- *tree.layout_mut(child) = Layout::with_order(order);
+ *tree.get_unrounded_layout_mut(child) = Layout::with_order(order);
tree.perform_child_layout(
child,
Size::NONE,
@@ -501,7 +501,7 @@ pub fn compute_grid_layout(tree: &mut impl LayoutTree, node: NodeId, inputs: Lay
&first_row_items[0]
};
- let layout = tree.layout_mut(item.node);
+ let layout = tree.get_unrounded_layout_mut(item.node);
layout.location.y + item.baseline.unwrap_or(layout.size.height)
};
diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs
index 2fecfcf08..7ae7e688a 100644
--- a/src/compute/grid/track_sizing.rs
+++ b/src/compute/grid/track_sizing.rs
@@ -1,16 +1,14 @@
//! Implements the track sizing algorithm
//! <https://www.w3.org/TR/css-grid-1/#layout-algorithm>
use super::types::{GridItem, GridTrack, TrackCounts};
-use crate::geometry::AbstractAxis;
-use crate::geometry::{Line, Size};
-use crate::prelude::{LayoutTree, TaffyMinContent};
+use crate::geometry::{AbstractAxis, Line, Size};
use crate::style::{
AlignContent, AlignSelf, AvailableSpace, LengthPercentage, MaxTrackSizingFunction, MinTrackSizingFunction,
};
-use crate::tree::SizingMode;
+use crate::style_helpers::TaffyMinContent;
+use crate::tree::{PartialLayoutTree, PartialLayoutTreeExt, SizingMode};
use crate::util::sys::{f32_max, f32_min, Vec};
-use crate::util::MaybeMath;
-use crate::util::ResolveOrZero;
+use crate::util::{MaybeMath, ResolveOrZero};
use core::cmp::Ordering;
/// Takes an axis, and a list of grid items sorted firstly by whether they cross a flex track
@@ -69,7 +67,7 @@ impl ItemBatcher {
/// don't have to be passed around all over the place below. It then has methods that implement the intrinsic sizing computations
struct IntrisicSizeMeasurer<'tree, 'oat, Tree, EstimateFunction>
where
- Tree: LayoutTree,
+ Tree: PartialLayoutTree,
EstimateFunction: Fn(&GridTrack, Option<f32>) -> Option<f32>,
{
/// The layout tree
@@ -87,7 +85,7 @@ where
impl<'tree, 'oat, Tree, EstimateFunction> IntrisicSizeMeasurer<'tree, 'oat, Tree, EstimateFunction>
where
- Tree: LayoutTree,
+ Tree: PartialLayoutTree,
EstimateFunction: Fn(&GridTrack, Option<f32>) -> Option<f32>,
{
/// Compute the available_space to be passed to the child sizing functions
@@ -263,7 +261,7 @@ pub(super) fn determine_if_item_crosses_flexible_or_intrinsic_tracks(
/// Note: Gutters are treated as empty fixed-size tracks for the purpose of the track sizing algorithm.
#[allow(clippy::too_many_arguments)]
#[inline(always)]
-pub(super) fn track_sizing_algorithm<Tree: LayoutTree>(
+pub(super) fn track_sizing_algorithm<Tree: PartialLayoutTree>(
tree: &mut Tree,
axis: AbstractAxis,
axis_min_size: Option<f32>,
@@ -438,7 +436,7 @@ fn initialize_track_sizes(axis_tracks: &mut [GridTrack], axis_inner_node_size: O
/// 11.5.1 Shim baseline-aligned items so their intrinsic size contributions reflect their baseline alignment.
fn resolve_item_baselines(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
axis: AbstractAxis,
items: &mut [GridItem],
inner_node_size: Size<Option<f32>>,
@@ -511,7 +509,7 @@ fn resolve_item_baselines(
/// 11.5 Resolve Intrinsic Track Sizes
#[allow(clippy::too_many_arguments)]
fn resolve_intrinsic_track_sizes(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
axis: AbstractAxis,
axis_tracks: &mut [GridTrack],
other_axis_tracks: &[GridTrack],
@@ -1140,7 +1138,7 @@ fn maximise_tracks(
#[allow(clippy::too_many_arguments)]
#[inline(always)]
fn expand_flexible_tracks(
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
axis: AbstractAxis,
axis_tracks: &mut [GridTrack],
items: &mut [GridItem],
diff --git a/src/compute/grid/types/grid_item.rs b/src/compute/grid/types/grid_item.rs
index ef776d22a..e80cd4c87 100644
--- a/src/compute/grid/types/grid_item.rs
+++ b/src/compute/grid/types/grid_item.rs
@@ -3,15 +3,12 @@ use super::GridTrack;
use crate::compute::grid::OriginZeroLine;
use crate::geometry::AbstractAxis;
use crate::geometry::{Line, Point, Rect, Size};
-use crate::prelude::LayoutTree;
use crate::style::{
AlignItems, AlignSelf, AvailableSpace, Dimension, LengthPercentageAuto, MaxTrackSizingFunction,
MinTrackSizingFunction, Overflow, Style,
};
-use crate::tree::NodeId;
-use crate::tree::SizingMode;
-use crate::util::MaybeMath;
-use crate::util::{MaybeResolve, ResolveOrZero};
+use crate::tree::{NodeId, PartialLayoutTree, PartialLayoutTreeExt, SizingMode};
+use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero};
use core::ops::Range;
/// Represents a single grid item
@@ -336,7 +333,7 @@ impl GridItem {
pub fn min_content_contribution(
&self,
axis: AbstractAxis,
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
available_space: Size<Option<f32>>,
inner_node_size: Size<Option<f32>>,
) -> f32 {
@@ -360,7 +357,7 @@ impl GridItem {
pub fn min_content_contribution_cached(
&mut self,
axis: AbstractAxis,
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
available_space: Size<Option<f32>>,
inner_node_size: Size<Option<f32>>,
) -> f32 {
@@ -375,7 +372,7 @@ impl GridItem {
pub fn max_content_contribution(
&self,
axis: AbstractAxis,
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
available_space: Size<Option<f32>>,
inner_node_size: Size<Option<f32>>,
) -> f32 {
@@ -399,7 +396,7 @@ impl GridItem {
pub fn max_content_contribution_cached(
&mut self,
axis: AbstractAxis,
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
available_space: Size<Option<f32>>,
inner_node_size: Size<Option<f32>>,
) -> f32 {
@@ -419,7 +416,7 @@ impl GridItem {
/// See: https://www.w3.org/TR/css-grid-1/#min-size-auto
pub fn minimum_contribution(
&mut self,
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
axis: AbstractAxis,
axis_tracks: &[GridTrack],
known_dimensions: Size<Option<f32>>,
@@ -478,7 +475,7 @@ impl GridItem {
#[inline(always)]
pub fn minimum_contribution_cached(
&mut self,
- tree: &mut impl LayoutTree,
+ tree: &mut impl PartialLayoutTree,
axis: AbstractAxis,
axis_tracks: &[GridTrack],
known_dimensions: Size<Option<f32>>,
diff --git a/src/compute/leaf.rs b/src/compute/leaf.rs
index 7d4b03d87..eda6f6b31 100644
--- a/src/compute/leaf.rs
+++ b/src/compute/leaf.rs
@@ -3,7 +3,6 @@
use crate::geometry::{Point, Size};
use crate::style::{AvailableSpace, Display, Overflow, Position, Style};
use crate::tree::CollapsibleMarginSet;
-use crate::tree::NodeId;
use crate::tree::{LayoutInput, LayoutOutput, SizingMode};
use crate::util::debug::debug_log;
use crate::util::sys::f32_max;
@@ -11,15 +10,13 @@ use crate::util::MaybeMath;
use crate::util::{MaybeResolve, ResolveOrZero};
/// Compute the size of a leaf node (node with no children)
-pub fn compute_leaf_layout<NodeContext, MeasureFunction>(
- mut measure_function: MeasureFunction,
- node_id: NodeId,
+pub fn compute_leaf_layout<MeasureFunction>(
inputs: LayoutInput,
style: &Style,
- context: Option<&mut NodeContext>,
+ measure_function: Option<MeasureFunction>,
) -> LayoutOutput
where
- MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
+ MeasureFunction: FnOnce(Size<Option<f32>>, Size<AvailableSpace>) -> Size<f32>,
{
let LayoutInput { known_dimensions, parent_size, available_space, sizing_mode, .. } = inputs;
@@ -93,11 +90,11 @@ where
bottom_margin: CollapsibleMarginSet::ZERO,
margins_can_collapse_through: !has_styles_preventing_being_collapsed_through
&& size.height == 0.0
- && context.is_none(),
+ && measure_function.is_none(),
};
};
- if let Some(context) = context {
+ if let Some(measure_function) = measure_function {
// Compute available space
let available_space = Size {
width: available_space
@@ -119,7 +116,7 @@ where
};
// Measure node
- let measured_size = measure_function(known_dimensions, available_space, node_id, Some(context));
+ let measured_size = measure_function(known_dimensions, available_space);
let clamped_size =
node_size.unwrap_or(measured_size + content_box_inset.sum_axes()).maybe_clamp(node_min_size, node_max_size);
let size = Size {
diff --git a/src/compute/mod.rs b/src/compute/mod.rs
index c1e43dcd4..66ffefa97 100644
--- a/src/compute/mod.rs
+++ b/src/compute/mod.rs
@@ -23,24 +23,112 @@ pub use self::flexbox::compute_flexbox_layout;
#[cfg(feature = "grid")]
pub use self::grid::compute_grid_layout;
-#[cfg(feature = "taffy_tree")]
-pub(crate) mod taffy_tree;
+use crate::geometry::{Line, Point, Size};
+use crate::style::AvailableSpace;
+use crate::tree::{
+ Layout, LayoutInput, LayoutOutput, LayoutTree, NodeId, PartialLayoutTree, PartialLayoutTreeExt, RunMode, SizingMode,
+};
+use crate::util::debug::{debug_log, debug_log_node, debug_pop_node, debug_push_node};
+use crate::util::sys::round;
+
+/// Updates the stored layout of the provided `node` and its children
+pub fn compute_layout(tree: &mut impl PartialLayoutTree, root: NodeId, available_space: Size<AvailableSpace>) {
+ // Recursively compute node layout
+ let size_and_baselines = tree.perform_child_layout(
+ root,
+ Size::NONE,
+ available_space.into_options(),
+ available_space,
+ SizingMode::InherentSize,
+ Line::FALSE,
+ );
+
+ let layout = Layout { order: 0, size: size_and_baselines.size, location: Point::ZERO };
+ *tree.get_unrounded_layout_mut(root) = layout;
+}
-use crate::tree::{Layout, LayoutOutput, LayoutTree, NodeId};
+/// Updates the stored layout of the provided `node` and its children
+#[inline(always)]
+pub fn compute_cached_layout<Tree: PartialLayoutTree + ?Sized, ComputeFunction>(
+ tree: &mut Tree,
+ node: NodeId,
+ inputs: LayoutInput,
+ mut compute_uncached: ComputeFunction,
+) -> LayoutOutput
+where
+ ComputeFunction: FnMut(&mut Tree, NodeId, LayoutInput) -> LayoutOutput,
+{
+ debug_push_node!(node);
+
+ let LayoutInput { known_dimensions, available_space, run_mode, .. } = inputs;
+ let has_children = tree.child_count(node) > 0;
+
+ // First we check if we have a cached result for the given input
+ let cache_run_mode = if !has_children { RunMode::PerformLayout } else { run_mode };
+ let cache_entry = tree.get_cache_mut(node).get(known_dimensions, available_space, cache_run_mode);
+ if let Some(cached_size_and_baselines) = cache_entry {
+ debug_log!("CACHE", dbg:cached_size_and_baselines.size);
+ debug_log_node!(known_dimensions, parent_size, available_space, run_mode, sizing_mode);
+ debug_pop_node!();
+ return cached_size_and_baselines;
+ }
-/// Creates a layout for this node and its children, recursively.
-/// Each hidden node has zero size and is placed at the origin
-pub fn compute_hidden_layout(tree: &mut impl LayoutTree, node: NodeId) -> LayoutOutput {
- /// Recursive function to apply hidden layout to all descendents
- fn perform_hidden_layout_inner(tree: &mut impl LayoutTree, node: NodeId, order: u32) {
- *tree.layout_mut(node) = Layout::with_order(order);
- for order in 0..tree.child_count(node) {
- perform_hidden_layout_inner(tree, tree.child(node, order), order as _);
+ let computed_size_and_baselines = compute_uncached(tree, node, inputs);
+
+ // Cache result
+ tree.get_cache_mut(node).store(known_dimensions, available_space, cache_run_mode, computed_size_and_baselines);
+
+ debug_log!("RESULT", dbg:computed_size_and_baselines.size);
+ debug_pop_node!();
+
+ computed_size_and_baselines
+}
+
+/// Rounds the calculated [`Layout`] to exact pixel values
+///
+/// In order to ensure that no gaps in the layout are introduced we:
+/// - Always round based on the cumulative x/y coordinates (relative to the viewport) rather than
+/// parent-relative coordinates
+/// - Compute width/height by first rounding the top/bottom/left/right and then computing the difference
+/// rather than rounding the width/height directly
+/// See <https://github.com/facebook/yoga/commit/aa5b296ac78f7a22e1aeaf4891243c6bb76488e2> for more context
+///
+/// In order to prevent innacuracies caused by rounding already-rounded values, we read from `unrounded_layout`
+/// and write to `final_layout`.
+pub fn round_layout(tree: &mut impl LayoutTree, node_id: NodeId) {
+ return round_layout_inner(tree, node_id, 0.0, 0.0);
+ /// Recursive function to apply rounding to all descendents
+ fn round_layout_inner(tree: &mut impl LayoutTree, node_id: NodeId, cumulative_x: f32, cumulative_y: f32) {
+ let unrounded_layout = *tree.get_unrounded_layout_mut(node_id);
+ let layout = &mut tree.get_final_layout_mut(node_id);
+
+ let cumulative_x = cumulative_x + unrounded_layout.location.x;
+ let cumulative_y = cumulative_y + unrounded_layout.location.y;
+
+ layout.location.x = round(unrounded_layout.location.x);
+ layout.location.y = round(unrounded_layout.location.y);
+ layout.size.width = round(cumulative_x + unrounded_layout.size.width) - round(cumulative_x);
+ layout.size.height = round(cumulative_y + unrounded_layout.size.height) - round(cumulative_y);
+
+ let child_count = tree.child_count(node_id);
+ for index in 0..child_count {
+ let child = tree.get_child_id(node_id, index);
+ round_layout_inner(tree, child, cumulative_x, cumulative_y);
}
}
+}
- for order in 0..tree.child_count(node) {
- perform_hidden_layout_inner(tree, tree.child(node, order), order as _);
+/// Creates a layout for this node and its children, recursively.
+/// Each hidden node has zero size and is placed at the origin
+pub fn compute_hidden_layout(tree: &mut impl PartialLayoutTree, node: NodeId) -> LayoutOutput {
+ // Clear cache and set zeroed-out layout for the node
+ tree.get_cache_mut(node).clear();
+ *tree.get_unrounded_layout_mut(node) = Layout::with_order(0);
+
+ // Perform hidden layout on all children
+ for index in 0..tree.child_count(node) {
+ let child_id = tree.get_child_id(node, index);
+ tree.compute_child_layout(child_id, LayoutInput::HIDDEN);
}
LayoutOutput::HIDDEN
@@ -51,7 +139,6 @@ mod tests {
use super::compute_hidden_layout;
use crate::geometry::{Point, Size};
use crate::style::{Display, Style};
- use crate::tree::TaffyView;
use crate::Taffy;
#[test]
@@ -74,10 +161,7 @@ mod tests {
)
.unwrap();
- compute_hidden_layout(
- &mut TaffyView { taffy: &mut taffy, measure_function: |_, _, _, _| Size::ZERO },
- root.into(),
- );
+ compute_hidden_layout(&mut taffy.as_layout_tree(), root.into());
// Whatever size and display-mode the nodes had previously,
// all layouts should resolve to ZERO due to the root's DISPLAY::NONE
diff --git a/src/compute/taffy_tree.rs b/src/compute/taffy_tree.rs
deleted file mode 100644
index 916b6e3a7..000000000
--- a/src/compute/taffy_tree.rs
+++ /dev/null
@@ -1,165 +0,0 @@
-//! Computation specific for the default `Taffy` tree implementation
-use crate::compute::leaf::compute_leaf_layout;
-use crate::geometry::{Line, Point, Size};
-use crate::style::{AvailableSpace, Display};
-use crate::tree::{
- Layout, LayoutInput, LayoutOutput, LayoutTree, NodeId, RunMode, SizingMode, Taffy, TaffyError, TaffyView,
-};
-use crate::util::debug::{debug_log, debug_log_node, debug_pop_node, debug_push_node};
-use crate::util::sys::round;
-
-#[cfg(feature = "block_layout")]
-use crate::compute::compute_block_layout;
-#[cfg(feature = "flexbox")]
-use crate::compute::compute_flexbox_layout;
-#[cfg(feature = "grid")]
-use crate::compute::compute_grid_layout;
-
-/// Updates the stored layout of the provided `node` and its children
-pub(crate) fn compute_layout<NodeContext, MeasureFunction>(
- taffy_view: &mut TaffyView<NodeContext, MeasureFunction>,
- root: NodeId,
- available_space: Size<AvailableSpace>,
-) -> Result<(), TaffyError>
-where
- MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
-{
- // Recursively compute node layout
- let size_and_baselines = compute_node_layout(
- taffy_view,
- root,
- LayoutInput {
- known_dimensions: Size::NONE,
- parent_size: available_space.into_options(),
- available_space,
- sizing_mode: SizingMode::InherentSize,
- run_mode: RunMode::PerformLayout,
- vertical_margins_are_collapsible: Line::FALSE,
- },
- );
-
- let layout = Layout { order: 0, size: size_and_baselines.size, location: Point::ZERO };
- *taffy_view.layout_mut(root) = layout;
-
- // If rounding is enabled, recursively round the layout's of this node and all children
- if taffy_view.taffy.config.use_rounding {
- round_layout(taffy_view.taffy, root, 0.0, 0.0);
- }
-
- Ok(())
-}
-
-/// Updates the stored layout of the provided `node` and its children
-pub(crate) fn compute_node_layout<NodeContext, MeasureFunction>(
- taffy_view: &mut TaffyView<NodeContext, MeasureFunction>,
- node: NodeId,
- inputs: LayoutInput,
-) -> LayoutOutput
-where
- MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
-{
- debug_push_node!(node);
-
- let LayoutInput { known_dimensions, available_space, run_mode, .. } = inputs;
- let node_key = node.into();
- let has_children = !taffy_view.taffy.children[node_key].is_empty();
-
- // First we check if we have a cached result for the given input
- let cache_run_mode = if !has_children { RunMode::PerformLayout } else { run_mode };
- let cache = &taffy_view.taffy.nodes[node_key].cache;
- let cache_entry = cache.get(known_dimensions, available_space, cache_run_mode);
- if let Some(cached_size_and_baselines) = cache_entry {
- debug_log!("CACHE", dbg:cached_size_and_baselines.size);
- debug_log_node!(known_dimensions, parent_size, available_space, run_mode, sizing_mode);
- debug_pop_node!();
- return cached_size_and_baselines;
- }
-
- // Fetch `Display` style (+ debug log)
- let display_mode = taffy_view.taffy.nodes[node_key].style.display;
- debug_log!(display_mode);
- debug_log_node!(known_dimensions, parent_size, available_space, run_mode, sizing_mode);
-
- let computed_size_and_baselines = match (display_mode, has_children) {
- (Display::None, _) => perform_taffy_tree_hidden_layout(taffy_view.taffy, node),
- #[cfg(feature = "block_layout")]
- (Display::Block, true) => compute_block_layout(taffy_view, node, inputs),
- #[cfg(feature = "flexbox")]
- (Display::Flex, true) => compute_flexbox_layout(taffy_view, node, inputs),
- #[cfg(feature = "grid")]
- (Display::Grid, true) => compute_grid_layout(taffy_view, node, inputs),
- (_, false) => compute_leaf_layout(
- &mut taffy_view.measure_function,
- node,
- inputs,
- &taffy_view.taffy.nodes[node_key].style,
- taffy_view.taffy.nodes[node_key].needs_measure.then(|| &mut taffy_view.taffy.node_context_data[node_key]),
- ),
- };
-
- // Cache result
- taffy_view.taffy.nodes[node_key].cache.store(
- known_dimensions,
- available_space,
- cache_run_mode,
- computed_size_and_baselines,
- );
-
- debug_log!("RESULT", dbg:computed_size_and_baselines.size);
- debug_pop_node!();
-
- computed_size_and_baselines
-}
-
-/// Creates a layout for this node and its children, recursively.
-/// Each hidden node has zero size and is placed at the origin
-fn perform_taffy_tree_hidden_layout<NodeContext>(tree: &mut Taffy<NodeContext>, node: NodeId) -> LayoutOutput {
- /// Recursive function to apply hidden layout to all descendents
- fn perform_hidden_layout_inner<NodeContext>(tree: &mut Taffy<NodeContext>, node: NodeId, order: u32) {
- let node_key = node.into();
- tree.nodes[node_key].unrounded_layout = Layout::with_order(order);
- tree.nodes[node_key].final_layout = Layout::with_order(order);
- tree.nodes[node_key].cache.clear();
- for order in 0..tree.children[node_key].len() {
- perform_hidden_layout_inner(tree, tree.children[node_key][order], order as _);
- }
- }
-
- let node_key = node.into();
- for order in 0..tree.children[node.into()].len() {
- perform_hidden_layout_inner(tree, tree.children[node_key][order], order as _);
- }
-
- LayoutOutput::HIDDEN
-}
-
-/// Rounds the calculated [`Layout`] to exact pixel values
-///
-/// In order to ensure that no gaps in the layout are introduced we:
-/// - Always round based on the cumulative x/y coordinates (relative to the viewport) rather than
-/// parent-relative coordinates
-/// - Compute width/height by first rounding the top/bottom/left/right and then computing the difference
-/// rather than rounding the width/height directly
-/// See <https://github.com/facebook/yoga/commit/aa5b296ac78f7a22e1aeaf4891243c6bb76488e2> for more context
-///
-/// In order to prevent innacuracies caused by rounding already-rounded values, we read from `unrounded_layout`
-/// and write to `final_layout`.
-fn round_layout<NodeContext>(tree: &mut Taffy<NodeContext>, node_id: NodeId, cumulative_x: f32, cumulative_y: f32) {
- let node = &mut tree.nodes[node_id.into()];
- let unrounded_layout = node.unrounded_layout;
- let layout = &mut node.final_layout;
-
- let cumulative_x = cumulative_x + unrounded_layout.location.x;
- let cumulative_y = cumulative_y + unrounded_layout.location.y;
-
- layout.location.x = round(unrounded_layout.location.x);
- layout.location.y = round(unrounded_layout.location.y);
- layout.size.width = round(cumulative_x + unrounded_layout.size.width) - round(cumulative_x);
- layout.size.height = round(cumulative_y + unrounded_layout.size.height) - round(cumulative_y);
-
- let child_count = tree.child_count(node_id).unwrap();
- for index in 0..child_count {
- let child = tree.children[node_id.into()][index];
- round_layout(tree, child, cumulative_x, cumulative_y);
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 3e2376d29..964f0bb4a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -33,7 +33,9 @@ pub use crate::compute::compute_block_layout;
pub use crate::compute::compute_flexbox_layout;
#[cfg(feature = "grid")]
pub use crate::compute::compute_grid_layout;
-pub use crate::compute::compute_leaf_layout;
-pub use crate::tree::LayoutTree;
+pub use crate::compute::{
+ compute_cached_layout, compute_hidden_layout, compute_layout, compute_leaf_layout, round_layout,
+};
+pub use crate::tree::{LayoutTree, PartialLayoutTree};
#[cfg(feature = "taffy_tree")]
pub use crate::tree::{Taffy, TaffyError, TaffyResult};
diff --git a/src/tree/cache.rs b/src/tree/cache.rs
index 42e3a9953..2cda0d415 100644
--- a/src/tree/cache.rs
+++ b/src/tree/cache.rs
@@ -8,7 +8,7 @@ const CACHE_SIZE: usize = 9;
/// Cached intermediate layout results
#[derive(Debug, Clone, Copy)]
-pub struct CacheEntry {
+pub(crate) struct CacheEntry {
/// The initial cached size of the node itself
known_dimensions: Size<Option<f32>>,
/// The initial cached size of the parent's node
diff --git a/src/tree/layout.rs b/src/tree/layout.rs
index 2f0d5ced3..27bd43571 100644
--- a/src/tree/layout.rs
+++ b/src/tree/layout.rs
@@ -1,5 +1,6 @@
//! Final data structures that represent the high-level UI layout
use crate::geometry::{Line, Point, Size};
+use crate::prelude::TaffyMaxContent;
use crate::style::AvailableSpace;
use crate::util::sys::{f32_max, f32_min};
@@ -11,6 +12,8 @@ pub enum RunMode {
/// The layout algorithm should be executed such that an accurate container size for the node can be determined.
/// Layout steps that aren't necessary for determining the container size of the current node can be skipped.
ComputeSize,
+ /// This node should have a null layout set as it has been hidden (i.e. using `Display::None`)
+ PerformHiddenLayout,
}
/// Whether styles should be taken into account when computing size
@@ -94,6 +97,20 @@ pub struct LayoutInput {
pub vertical_margins_are_collapsible: Line<bool>,
}
+impl LayoutInput {
+ /// A LayoutInput that can be used to request hidden layout
+ pub const HIDDEN: LayoutInput = LayoutInput {
+ // The important property for hidden layout
+ run_mode: RunMode::PerformHiddenLayout,
+ // The rest will be ignored
+ known_dimensions: Size::NONE,
+ parent_size: Size::NONE,
+ available_space: Size::MAX_CONTENT,
+ sizing_mode: SizingMode::InherentSize,
+ vertical_margins_are_collapsible: Line::FALSE,
+ };
+}
+
/// A struct containing the result of laying a single node, which is returned up to the parent node
///
/// A baseline is the line on which text sits. Your node likely has a baseline if it is a text node, or contains
diff --git a/src/tree/mod.rs b/src/tree/mod.rs
index 94751ce85..fb9fee7c5 100644
--- a/src/tree/mod.rs
+++ b/src/tree/mod.rs
@@ -5,7 +5,7 @@ use crate::style::{AvailableSpace, Style};
// Submodules
mod cache;
-pub use cache::{Cache, CacheEntry};
+pub use cache::Cache;
mod node;
#[cfg(feature = "taffy_tree")]
use node::NodeData;
@@ -13,57 +13,103 @@ pub use node::NodeId;
#[cfg(feature = "taffy_tree")]
mod taffy_tree;
#[cfg(feature = "taffy_tree")]
-pub use taffy_tree::{Taffy, TaffyChildIter, TaffyError, TaffyResult, TaffyView};
+pub use taffy_tree::{Taffy, TaffyError, TaffyResult};
mod layout;
pub use layout::{CollapsibleMarginSet, Layout, LayoutInput, LayoutOutput, RunMode, SizingMode};
-/// Any item that implements the LayoutTree can be layed out using Taffy's algorithms.
+/// This if the core abstraction in Taffy. Any type that *correctly* implements `PartialLayoutTree` can be laid out using Taffy's algorithms.
///
-/// Generally, Taffy expects your Node tree to be indexable by stable indices. A "stable" index means that the Node's ID
-/// remains the same between re-layouts.
-pub trait LayoutTree {
+/// The type implementing `PartialLayoutTree` would typically be an entire tree of nodes (or a view over an entire tree of nodes).
+/// However, `PartialLayoutTree` and Taffy's algorithm implementations have been designed such that they can be used for a laying out a single
+/// node that only has access to it's immediate children.
+pub trait PartialLayoutTree {
/// Type representing an iterator of the children of a node
type ChildIter<'a>: Iterator<Item = NodeId>
where
Self: 'a;
/// Get the list of children IDs for the given node
- fn children(&self, node: NodeId) -> Self::ChildIter<'_>;
+ fn child_ids(&self, parent_node_id: NodeId) -> Self::ChildIter<'_>;
/// Get the number of children for the given node
- fn child_count(&self, node: NodeId) -> usize;
+ fn child_count(&self, parent_node_id: NodeId) -> usize;
/// Get a specific child of a node, where the index represents the nth child
- fn child(&self, node: NodeId, index: usize) -> NodeId;
+ fn get_child_id(&self, parent_node_id: NodeId, child_index: usize) -> NodeId;
- /// Get the [`Style`] for this node.
- fn style(&self, node: NodeId) -> &Style;
+ /// Get a reference to the [`Style`] for this node.
+ fn get_style(&self, node_id: NodeId) -> &Style;
- /// Get a reference to the node's output layout
- fn layout(&self, node: NodeId) -> &Layout;
+ /// Get a mutable reference to the node's unrounded layout
+ fn get_unrounded_layout_mut(&mut self, node_id: NodeId) -> &mut Layout;
- /// Modify the node's output layout
- fn layout_mut(&mut self, node: NodeId) -> &mut Layout;
+ /// Get a mutable reference to the [`Cache`] for this node.
+ fn get_cache_mut(&mut self, node_id: NodeId) -> &mut Cache;
+ /// Compute the specified node's size or full layout given the specified constraints
+ fn compute_child_layout(&mut self, node_id: NodeId, inputs: LayoutInput) -> LayoutOutput;
+}
+
+/// Extends [`PartialLayoutTree`] with an additional guarantee: that the child/children methods can be used to recurse
+/// infinitely down the tree. Enables Taffy's rounding and debug printing methods to be used.
+pub trait LayoutTree: PartialLayoutTree {
+ /// Get a reference to the node's final layout
+ fn get_final_layout(&self, node_id: NodeId) -> &Layout;
+ /// Get a mutable reference to the node's final layout
+ fn get_final_layout_mut(&mut self, node_id: NodeId) -> &mut Layout;
+}
+
+/// A private trait which allows us to add extra convenience methods to types which implement
+/// LayoutTree without making those methods public.
+pub(crate) trait PartialLayoutTreeExt: PartialLayoutTree {
/// Compute the size of the node given the specified constraints
+ #[inline(always)]
fn measure_child_size(
&mut self,
- node: NodeId,
+ node_id: NodeId,
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
sizing_mode: SizingMode,
vertical_margins_are_collapsible: Line<bool>,
- ) -> Size<f32>;
+ ) -> Size<f32> {
+ self.compute_child_layout(
+ node_id,
+ LayoutInput {
+ known_dimensions,
+ parent_size,
+ available_space,
+ sizing_mode,
+ run_mode: RunMode::ComputeSize,
+ vertical_margins_are_collapsible,
+ },
+ )
+ .size
+ }
/// Perform a full layout on the node given the specified constraints
+ #[inline(always)]
fn perform_child_layout(
&mut self,
- node: NodeId,
+ node_id: NodeId,
known_dimensions: Size<Option<f32>>,
parent_size: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
sizing_mode: SizingMode,
vertical_margins_are_collapsible: Line<bool>,
- ) -> LayoutOutput;
+ ) -> LayoutOutput {
+ self.compute_child_layout(
+ node_id,
+ LayoutInput {
+ known_dimensions,
+ parent_size,
+ available_space,
+ sizing_mode,
+ run_mode: RunMode::PerformLayout,
+ vertical_margins_are_collapsible,
+ },
+ )
+ }
}
+
+impl<T: PartialLayoutTree> PartialLayoutTreeExt for T {}
diff --git a/src/tree/taffy_tree/mod.rs b/src/tree/taffy_tree/mod.rs
index 3aae953b9..b4afa5f44 100644
--- a/src/tree/taffy_tree/mod.rs
+++ b/src/tree/taffy_tree/mod.rs
@@ -4,4 +4,4 @@ mod error;
mod tree;
pub use error::{TaffyError, TaffyResult};
-pub use tree::{Taffy, TaffyChildIter, TaffyView};
+pub use tree::Taffy;
diff --git a/src/tree/taffy_tree/tree.rs b/src/tree/taffy_tree/tree.rs
index dcdc55b89..85f83e7c7 100644
--- a/src/tree/taffy_tree/tree.rs
+++ b/src/tree/taffy_tree/tree.rs
@@ -3,13 +3,20 @@
//! Layouts are composed of multiple nodes, which live in a tree-like data structure.
use slotmap::{DefaultKey, SlotMap, SparseSecondaryMap};
-use crate::compute::taffy_tree::{compute_layout, compute_node_layout};
-use crate::geometry::{Line, Size};
-use crate::prelude::LayoutTree;
-use crate::style::{AvailableSpace, Style};
-use crate::tree::{Layout, LayoutInput, LayoutOutput, NodeData, NodeId, RunMode, SizingMode};
+use crate::geometry::Size;
+use crate::style::{AvailableSpace, Display, Style};
+use crate::tree::{Cache, Layout, LayoutInput, LayoutOutput, LayoutTree, NodeData, NodeId, PartialLayoutTree, RunMode};
+use crate::util::debug::{debug_log, debug_log_node};
use crate::util::sys::{new_vec_with_capacity, ChildrenVec, Vec};
+#[cfg(feature = "block_layout")]
+use crate::compute::compute_block_layout;
+#[cfg(feature = "flexbox")]
+use crate::compute::compute_flexbox_layout;
+#[cfg(feature = "grid")]
+use crate::compute::compute_grid_layout;
+use crate::compute::{compute_cached_layout, compute_hidden_layout, compute_layout, compute_leaf_layout, round_layout};
+
use super::{TaffyError, TaffyResult};
/// Global configuration values for a Taffy instance
@@ -53,7 +60,7 @@ impl Default for Taffy {
}
/// Iterator that wraps a slice of nodes, lazily converting them to u64
-pub struct TaffyChildIter<'a>(core::slice::Iter<'a, NodeId>);
+pub(crate) struct TaffyChildIter<'a>(core::slice::Iter<'a, NodeId>);
impl<'a> Iterator for TaffyChildIter<'a> {
type Item = NodeId;
@@ -65,7 +72,7 @@ impl<'a> Iterator for TaffyChildIter<'a> {
/// View over the Taffy tree that holds the tree itself along with a reference to the context
/// and implements LayoutTree. This allows the context to be stored outside of the Taffy struct
/// which makes the lifetimes of the context much more flexible.
-pub struct TaffyView<'t, NodeContext, MeasureFunction>
+pub(crate) struct TaffyView<'t, NodeContext, MeasureFunction>
where
MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
{
@@ -75,14 +82,14 @@ where
pub(crate) measure_function: MeasureFunction,
}
-impl<'t, NodeContext, MeasureFunction> LayoutTree for TaffyView<'t, NodeContext, MeasureFunction>
+impl<'t, NodeContext, MeasureFunction> PartialLayoutTree for TaffyView<'t, NodeContext, MeasureFunction>
where
MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
{
type ChildIter<'a> = TaffyChildIter<'a> where Self: 'a;
#[inline(always)]
- fn children(&self, node: NodeId) -> Self::ChildIter<'_> {
+ fn child_ids(&self, node: NodeId) -> Self::ChildIter<'_> {
TaffyChildIter(self.taffy.children[node.into()].iter())
}
@@ -92,80 +99,99 @@ where
}
#[inline(always)]
- fn style(&self, node: NodeId) -> &Style {
+ fn get_child_id(&self, node: NodeId, id: usize) -> NodeId {
+ self.taffy.children[node.into()][id]
+ }
+
+ #[inline(always)]
+ fn get_style(&self, node: NodeId) -> &Style {
&self.taffy.nodes[node.into()].style
}
#[inline(always)]
- fn layout(&self, node: NodeId) -> &Layout {
- if self.taffy.config.use_rounding {
- &self.taffy.nodes[node.into()].unrounded_layout
- } else {
- &self.taffy.nodes[node.into()].final_layout
- }
+ fn get_cache_mut(&mut self, node: NodeId) -> &mut Cache {
+ &mut self.taffy.nodes[node.into()].cache
}
#[inline(always)]
- fn layout_mut(&mut self, node: NodeId) -> &mut Layout {
- if self.taffy.config.use_rounding {
- &mut self.taffy.nodes[node.into()].unrounded_layout
- } else {
- &mut self.taffy.nodes[node.into()].final_layout
- }
+ fn get_unrounded_layout_mut(&mut self, node: NodeId) -> &mut Layout {
+ &mut self.taffy.nodes[node.into()].unrounded_layout
}
#[inline(always)]
- fn child(&self, node: NodeId, id: usize) -> NodeId {
- self.taffy.children[node.into()][id]
+ fn compute_child_layout(&mut self, node: NodeId, inputs: LayoutInput) -> LayoutOutput {
+ // If RunMode is PerformHiddenLayout then this indicates that an ancestor node is `Display::None`
+ // and thus that we should lay out this node using hidden layout regardless of it's own display style.
+ if inputs.run_mode == RunMode::PerformHiddenLayout {
+ debug_log!("HIDDEN");
+ return compute_hidden_layout(self, node);
+ }
+
+ // We run the following wrapped in "compute_cached_layout", which will check the cache for an entry matching the node and inputs and:
+ // - Return that entry if exists
+ // - Else call the passed closure (below) to compute the result
+ //
+ // If there was no cache match and a new result needs to be computed then that result will be added to the cache
+ compute_cached_layout(self, node, inputs, |tree, node, inputs| {
+ let display_mode = tree.get_style(node).display;
+ let has_children = tree.child_count(node) > 0;
+
+ debug_log!(display_mode);
+ debug_log_node!(
+ inputs.known_dimensions,
+ inputs.parent_size,
+ inputs.available_space,
+ inputs.run_mode,
+ inputs.sizing_mode
+ );
+
+ // Dispatch to a layout algorithm based on the node's display style and whether the node has children or not.
+ match (display_mode, has_children) {
+ (Display::None, _) => compute_hidden_layout(tree, node),
+ #[cfg(feature = "block_layout")]
+ (Display::Block, true) => compute_block_layout(tree, node, inputs),
+ #[cfg(feature = "flexbox")]
+ (Display::Flex, true) => compute_flexbox_layout(tree, node, inputs),
+ #[cfg(feature = "grid")]
+ (Display::Grid, true) => compute_grid_layout(tree, node, inputs),
+ (_, false) => {
+ let node_key = node.into();
+ let style = &tree.taffy.nodes[node_key].style;
+ let needs_measure = tree.taffy.nodes[node_key].needs_measure;
+ if needs_measure {
+ let node_context = Some(&mut tree.taffy.node_context_data[node_key]);
+ compute_leaf_layout(
+ inputs,
+ style,
+ Some(|known_dimensions, available_space| {
+ (tree.measure_function)(known_dimensions, available_space, node, node_context)
+ }),
+ )
+ } else {
+ /// Type inference gets confused because we're just passing None here. So we give
+ /// it a concrete type to work with (even though we never construct the inner type)
+ type DummyMeasureFunction = Option<fn(Size<Option<f32>>, Size<AvailableSpace>) -> Size<f32>>;
+ let measure_function: DummyMeasureFunction = None;
+ compute_leaf_layout(inputs, style, measure_function)
+ }
+ }
+ }
+ })
}
+}
+impl<'t, NodeContext, MeasureFunction> LayoutTree for TaffyView<'t, NodeContext, MeasureFunction>
+where
+ MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
+{
#[inline(always)]
- fn measure_child_size(
- &mut self,
- node: NodeId,
- known_dimensions: Size<Option<f32>>,
- parent_size: Size<Option<f32>>,
- available_space: Size<AvailableSpace>,
- sizing_mode: SizingMode,
- vertical_margins_are_collapsible: Line<bool>,
- ) -> Size<f32> {
- compute_node_layout(
- self,
- node,
- LayoutInput {
- known_dimensions,
- parent_size,
- available_space,
- sizing_mode,
- run_mode: RunMode::ComputeSize,
- vertical_margins_are_collapsible,
- },
- )
- .size
+ fn get_final_layout(&self, node: NodeId) -> &Layout {
+ &self.taffy.nodes[node.into()].final_layout
}
#[inline(always)]
- fn perform_child_layout(
- &mut self,
- node: NodeId,
- known_dimensions: Size<Option<f32>>,
- parent_size: Size<Option<f32>>,
- available_space: Size<AvailableSpace>,
- sizing_mode: SizingMode,
- vertical_margins_are_collapsible: Line<bool>,
- ) -> LayoutOutput {
- compute_node_layout(
- self,
- node,
- LayoutInput {
- known_dimensions,
- parent_size,
- available_space,
- sizing_mode,
- run_mode: RunMode::PerformLayout,
- vertical_margins_are_collapsible,
- },
- )
+ fn get_final_layout_mut(&mut self, node: NodeId) -> &mut Layout {
+ &mut self.taffy.nodes[node.into()].final_layout
}
}
@@ -471,15 +497,20 @@ impl<NodeContext> Taffy<NodeContext> {
/// Updates the stored layout of the provided `node` and its children
pub fn compute_layout_with_measure<MeasureFunction>(
&mut self,
- node: NodeId,
+ node_id: NodeId,
available_space: Size<AvailableSpace>,
measure_function: MeasureFunction,
) -> Result<(), TaffyError>
where
MeasureFunction: FnMut(Size<Option<f32>>, Size<AvailableSpace>, NodeId, Option<&mut NodeContext>) -> Size<f32>,
{
+ let use_rounding = self.config.use_rounding;
let mut taffy_view = TaffyView { taffy: self, measure_function };
- compute_layout(&mut taffy_view, node, available_space)
+ compute_layout(&mut taffy_view, node_id, available_space);
+ if use_rounding {
+ round_layout(&mut taffy_view, node_id);
+ }
+ Ok(())
}
/// Updates the stored layout of the provided `node` and its children
@@ -493,6 +524,12 @@ impl<NodeContext> Taffy<NodeContext> {
let taffy_view = TaffyView { taffy: self, measure_function: |_, _, _, _| Size::ZERO };
crate::util::print_tree(&taffy_view, root)
}
+
+ /// Returns an instance of LayoutTree representing the Taffy
+ #[cfg(test)]
+ pub(crate) fn as_layout_tree(&mut self) -> impl LayoutTree + '_ {
+ TaffyView { taffy: self, measure_function: |_, _, _, _| Size::ZERO }
+ }
}
#[cfg(test)]
diff --git a/src/util/debug.rs b/src/util/debug.rs
index 0a17069fa..6962b8215 100644
--- a/src/util/debug.rs
+++ b/src/util/debug.rs
@@ -17,8 +17,8 @@ pub fn print_tree(tree: &impl LayoutTree, root: NodeId) {
#[cfg(feature = "std")]
fn print_node(tree: &impl LayoutTree, node: NodeId, has_sibling: bool, lines_string: String) {
- let layout = &tree.layout(node);
- let style = &tree.style(node);
+ let layout = &tree.get_final_layout(node);
+ let style = &tree.get_style(node);
let num_children = tree.child_count(node);
let display = match (num_children, style.display) {
@@ -48,7 +48,7 @@ fn print_node(tree: &impl LayoutTree, node: NodeId, has_sibling: bool, lines_str
let new_string = lines_string + bar;
// Recurse into children
- for (index, child) in tree.children(node).enumerate() {
+ for (index, child) in tree.child_ids(node).enumerate() {
let has_sibling = index < num_children - 1;
print_node(tree, child, has_sibling, new_string.clone());
}