use slotmap::{DefaultKey, SlotMap, SparseSecondaryMap};
pub type Node = slotmap::DefaultKey;
use crate::error::{TaffyError, TaffyResult};
use crate::geometry::Size;
use crate::layout::{Cache, Layout};
use crate::prelude::LayoutTree;
use crate::style::{AvailableSpace, Style};
#[cfg(any(feature = "std", feature = "alloc"))]
use crate::sys::Box;
use crate::sys::{new_vec_with_capacity, ChildrenVec, Vec};
use crate::{data::NodeData, error};
pub trait Measurable: Send + Sync + Fn(Size<Option<f32>>, Size<AvailableSpace>) -> Size<f32> {}
impl<F: Send + Sync + Fn(Size<Option<f32>>, Size<AvailableSpace>) -> Size<f32>> Measurable for F {}
pub enum MeasureFunc {
Raw(fn(Size<Option<f32>>, Size<AvailableSpace>) -> Size<f32>),
#[cfg(any(feature = "std", feature = "alloc"))]
Boxed(Box<dyn Measurable>),
}
pub(crate) struct TaffyConfig {
pub(crate) use_rounding: bool,
}
impl Default for TaffyConfig {
fn default() -> Self {
Self { use_rounding: true }
}
}
pub struct Taffy {
pub(crate) nodes: SlotMap<Node, NodeData>,
pub(crate) measure_funcs: SparseSecondaryMap<Node, MeasureFunc>,
pub(crate) children: SlotMap<Node, ChildrenVec<Node>>,
pub(crate) parents: SlotMap<Node, Option<Node>>,
pub(crate) config: TaffyConfig,
pub(crate) is_layouting: bool,
}
impl Default for Taffy {
fn default() -> Self {
Taffy::new()
}
}
impl LayoutTree for Taffy {
type ChildIter<'a> = core::slice::Iter<'a, DefaultKey>;
fn children(&self, node: Node) -> Self::ChildIter<'_> {
self.children[node].iter()
}
fn child_count(&self, node: Node) -> usize {
self.children[node].len()
}
fn is_childless(&self, node: Node) -> bool {
self.children[node].is_empty()
}
fn parent(&self, node: Node) -> Option<Node> {
self.parents.get(node).copied().flatten()
}
fn style(&self, node: Node) -> &Style {
&self.nodes[node].style
}
#[inline(always)]
fn layout(&self, node: Node) -> &Layout {
if self.is_layouting && self.config.use_rounding {
&self.nodes[node].unrounded_layout
} else {
&self.nodes[node].final_layout
}
}
#[inline(always)]
fn layout_mut(&mut self, node: Node) -> &mut Layout {
if self.is_layouting && self.config.use_rounding {
&mut self.nodes[node].unrounded_layout
} else {
&mut self.nodes[node].final_layout
}
}
#[inline(always)]
fn mark_dirty(&mut self, node: Node) -> TaffyResult<()> {
self.mark_dirty_internal(node)
}
fn measure_node(
&self,
node: Node,
known_dimensions: Size<Option<f32>>,
available_space: Size<AvailableSpace>,
) -> Size<f32> {
match &self.measure_funcs[node] {
MeasureFunc::Raw(measure) => measure(known_dimensions, available_space),
#[cfg(any(feature = "std", feature = "alloc"))]
MeasureFunc::Boxed(measure) => (measure as &dyn Fn(_, _) -> _)(known_dimensions, available_space),
}
}
fn needs_measure(&self, node: Node) -> bool {
self.nodes[node].needs_measure && self.measure_funcs.get(node).is_some()
}
fn cache_mut(&mut self, node: Node, index: usize) -> &mut Option<Cache> {
&mut self.nodes[node].size_cache[index]
}
fn child(&self, node: Node, id: usize) -> Node {
self.children[node][id]
}
}
#[allow(clippy::iter_cloned_collect)] impl Taffy {
#[must_use]
pub fn new() -> Self {
Self::with_capacity(16)
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
nodes: SlotMap::with_capacity(capacity),
children: SlotMap::with_capacity(capacity),
parents: SlotMap::with_capacity(capacity),
measure_funcs: SparseSecondaryMap::with_capacity(capacity),
config: TaffyConfig::default(),
is_layouting: false,
}
}
pub fn enable_rounding(&mut self) {
self.config.use_rounding = true;
}
pub fn disable_rounding(&mut self) {
self.config.use_rounding = false;
}
pub fn new_leaf(&mut self, layout: Style) -> TaffyResult<Node> {
let id = self.nodes.insert(NodeData::new(layout));
let _ = self.children.insert(new_vec_with_capacity(0));
let _ = self.parents.insert(None);
Ok(id)
}
pub fn new_leaf_with_measure(&mut self, layout: Style, measure: MeasureFunc) -> TaffyResult<Node> {
let mut data = NodeData::new(layout);
data.needs_measure = true;
let id = self.nodes.insert(data);
self.measure_funcs.insert(id, measure);
let _ = self.children.insert(new_vec_with_capacity(0));
let _ = self.parents.insert(None);
Ok(id)
}
pub fn new_with_children(&mut self, layout: Style, children: &[Node]) -> TaffyResult<Node> {
let id = self.nodes.insert(NodeData::new(layout));
for child in children {
self.parents[*child] = Some(id);
}
let _ = self.children.insert(children.iter().copied().collect::<_>());
let _ = self.parents.insert(None);
Ok(id)
}
pub fn clear(&mut self) {
self.nodes.clear();
self.children.clear();
self.parents.clear();
}
pub fn remove(&mut self, node: Node) -> TaffyResult<Node> {
if let Some(parent) = self.parents[node] {
if let Some(children) = self.children.get_mut(parent) {
children.retain(|f| *f != node);
}
}
if let Some(children) = self.children.get(node) {
for child in children.iter().copied() {
self.parents[child] = None;
}
}
let _ = self.children.remove(node);
let _ = self.parents.remove(node);
let _ = self.nodes.remove(node);
Ok(node)
}
pub fn set_measure(&mut self, node: Node, measure: Option<MeasureFunc>) -> TaffyResult<()> {
if let Some(measure) = measure {
self.nodes[node].needs_measure = true;
self.measure_funcs.insert(node, measure);
} else {
self.nodes[node].needs_measure = false;
self.measure_funcs.remove(node);
}
self.mark_dirty_internal(node)?;
Ok(())
}
pub fn add_child(&mut self, parent: Node, child: Node) -> TaffyResult<()> {
self.parents[child] = Some(parent);
self.children[parent].push(child);
self.mark_dirty_internal(parent)?;
Ok(())
}
pub fn set_children(&mut self, parent: Node, children: &[Node]) -> TaffyResult<()> {
for child in &self.children[parent] {
self.parents[*child] = None;
}
for child in children {
self.parents[*child] = Some(parent);
}
self.children[parent] = children.iter().copied().collect::<_>();
self.mark_dirty_internal(parent)?;
Ok(())
}
pub fn remove_child(&mut self, parent: Node, child: Node) -> TaffyResult<Node> {
let index = self.children[parent].iter().position(|n| *n == child).unwrap();
self.remove_child_at_index(parent, index)
}
pub fn remove_child_at_index(&mut self, parent: Node, child_index: usize) -> TaffyResult<Node> {
let child_count = self.children[parent].len();
if child_index >= child_count {
return Err(error::TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
}
let child = self.children[parent].remove(child_index);
self.parents[child] = None;
self.mark_dirty_internal(parent)?;
Ok(child)
}
pub fn replace_child_at_index(&mut self, parent: Node, child_index: usize, new_child: Node) -> TaffyResult<Node> {
let child_count = self.children[parent].len();
if child_index >= child_count {
return Err(error::TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
}
self.parents[new_child] = Some(parent);
let old_child = core::mem::replace(&mut self.children[parent][child_index], new_child);
self.parents[old_child] = None;
self.mark_dirty_internal(parent)?;
Ok(old_child)
}
pub fn child_at_index(&self, parent: Node, child_index: usize) -> TaffyResult<Node> {
let child_count = self.children[parent].len();
if child_index >= child_count {
return Err(error::TaffyError::ChildIndexOutOfBounds { parent, child_index, child_count });
}
Ok(self.children[parent][child_index])
}
pub fn total_node_count(&self) -> usize {
self.nodes.len()
}
pub fn child_count(&self, parent: Node) -> TaffyResult<usize> {
Ok(self.children[parent].len())
}
pub fn children(&self, parent: Node) -> TaffyResult<Vec<Node>> {
Ok(self.children[parent].iter().copied().collect::<_>())
}
pub fn set_style(&mut self, node: Node, style: Style) -> TaffyResult<()> {
self.nodes[node].style = style;
self.mark_dirty_internal(node)?;
Ok(())
}
pub fn style(&self, node: Node) -> TaffyResult<&Style> {
Ok(&self.nodes[node].style)
}
pub fn layout(&self, node: Node) -> TaffyResult<&Layout> {
Ok(&self.nodes[node].final_layout)
}
fn mark_dirty_internal(&mut self, node: Node) -> TaffyResult<()> {
fn mark_dirty_recursive(
nodes: &mut SlotMap<Node, NodeData>,
parents: &SlotMap<Node, Option<Node>>,
node_id: Node,
) {
nodes[node_id].mark_dirty();
if let Some(Some(node)) = parents.get(node_id) {
mark_dirty_recursive(nodes, parents, *node);
}
}
mark_dirty_recursive(&mut self.nodes, &self.parents, node);
Ok(())
}
pub fn dirty(&self, node: Node) -> TaffyResult<bool> {
Ok(self.nodes[node].size_cache.iter().all(|entry| entry.is_none()))
}
pub fn compute_layout(&mut self, node: Node, available_space: Size<AvailableSpace>) -> Result<(), TaffyError> {
crate::compute::compute_layout(self, node, available_space)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::bool_assert_comparison)]
use super::*;
use crate::style::{Dimension, Display, FlexDirection};
use crate::style_helpers::*;
use crate::sys;
#[test]
fn new_should_allocate_default_capacity() {
const DEFAULT_CAPACITY: usize = 16; let taffy = Taffy::new();
assert!(taffy.children.capacity() >= DEFAULT_CAPACITY);
assert!(taffy.parents.capacity() >= DEFAULT_CAPACITY);
assert!(taffy.nodes.capacity() >= DEFAULT_CAPACITY);
}
#[test]
fn test_with_capacity() {
const CAPACITY: usize = 8;
let taffy = Taffy::with_capacity(CAPACITY);
assert!(taffy.children.capacity() >= CAPACITY);
assert!(taffy.parents.capacity() >= CAPACITY);
assert!(taffy.nodes.capacity() >= CAPACITY);
}
#[test]
fn test_new_leaf() {
let mut taffy = Taffy::new();
let res = taffy.new_leaf(Style::default());
assert!(res.is_ok());
let node = res.unwrap();
assert!(taffy.child_count(node).unwrap() == 0);
}
#[test]
fn new_leaf_with_measure() {
let mut taffy = Taffy::new();
let res = taffy.new_leaf_with_measure(Style::default(), MeasureFunc::Raw(|_, _| Size::ZERO));
assert!(res.is_ok());
let node = res.unwrap();
assert!(taffy.child_count(node).unwrap() == 0);
}
#[test]
fn test_new_with_children() {
let mut taffy = Taffy::new();
let child0 = taffy.new_leaf(Style::default()).unwrap();
let child1 = taffy.new_leaf(Style::default()).unwrap();
let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 2);
assert_eq!(taffy.children(node).unwrap()[0], child0);
assert_eq!(taffy.children(node).unwrap()[1], child1);
}
#[test]
fn remove_node_should_remove() {
let mut taffy = Taffy::new();
let node = taffy.new_leaf(Style::default()).unwrap();
let _ = taffy.remove(node).unwrap();
}
#[test]
fn remove_node_should_detach_herarchy() {
let mut taffy = Taffy::new();
let node2 = taffy.new_leaf(Style::default()).unwrap();
let node1 = taffy.new_with_children(Style::default(), &[node2]).unwrap();
let node0 = taffy.new_with_children(Style::default(), &[node1]).unwrap();
assert_eq!(taffy.children(node0).unwrap().as_slice(), &[node1]);
assert_eq!(taffy.children(node1).unwrap().as_slice(), &[node2]);
let _ = taffy.remove(node1).unwrap();
assert!(taffy.children(node0).unwrap().is_empty());
assert!(taffy.children(node2).unwrap().is_empty());
}
#[test]
fn remove_last_node() {
let mut taffy = Taffy::new();
let parent = taffy.new_leaf(Style::default()).unwrap();
let child = taffy.new_leaf(Style::default()).unwrap();
taffy.add_child(parent, child).unwrap();
taffy.remove(child).unwrap();
taffy.remove(parent).unwrap();
}
#[test]
fn set_measure() {
let mut taffy = Taffy::new();
let node = taffy
.new_leaf_with_measure(Style::default(), MeasureFunc::Raw(|_, _| Size { width: 200.0, height: 200.0 }))
.unwrap();
taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
assert_eq!(taffy.layout(node).unwrap().size.width, 200.0);
taffy.set_measure(node, Some(MeasureFunc::Raw(|_, _| Size { width: 100.0, height: 100.0 }))).unwrap();
taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
assert_eq!(taffy.layout(node).unwrap().size.width, 100.0);
}
#[test]
fn set_measure_of_previously_unmeasured_node() {
let mut taffy = Taffy::new();
let node = taffy.new_leaf(Style::default()).unwrap();
taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
assert_eq!(taffy.layout(node).unwrap().size.width, 0.0);
taffy.set_measure(node, Some(MeasureFunc::Raw(|_, _| Size { width: 100.0, height: 100.0 }))).unwrap();
taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
assert_eq!(taffy.layout(node).unwrap().size.width, 100.0);
}
#[test]
fn add_child() {
let mut taffy = Taffy::new();
let node = taffy.new_leaf(Style::default()).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 0);
let child0 = taffy.new_leaf(Style::default()).unwrap();
taffy.add_child(node, child0).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 1);
let child1 = taffy.new_leaf(Style::default()).unwrap();
taffy.add_child(node, child1).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 2);
}
#[test]
fn set_children() {
let mut taffy = Taffy::new();
let child0 = taffy.new_leaf(Style::default()).unwrap();
let child1 = taffy.new_leaf(Style::default()).unwrap();
let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 2);
assert_eq!(taffy.children(node).unwrap()[0], child0);
assert_eq!(taffy.children(node).unwrap()[1], child1);
let child2 = taffy.new_leaf(Style::default()).unwrap();
let child3 = taffy.new_leaf(Style::default()).unwrap();
taffy.set_children(node, &[child2, child3]).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 2);
assert_eq!(taffy.children(node).unwrap()[0], child2);
assert_eq!(taffy.children(node).unwrap()[1], child3);
}
#[test]
fn remove_child() {
let mut taffy = Taffy::new();
let child0 = taffy.new_leaf(Style::default()).unwrap();
let child1 = taffy.new_leaf(Style::default()).unwrap();
let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 2);
taffy.remove_child(node, child0).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 1);
assert_eq!(taffy.children(node).unwrap()[0], child1);
taffy.remove_child(node, child1).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 0);
}
#[test]
fn remove_child_at_index() {
let mut taffy = Taffy::new();
let child0 = taffy.new_leaf(Style::default()).unwrap();
let child1 = taffy.new_leaf(Style::default()).unwrap();
let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 2);
taffy.remove_child_at_index(node, 0).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 1);
assert_eq!(taffy.children(node).unwrap()[0], child1);
taffy.remove_child_at_index(node, 0).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 0);
}
#[test]
fn replace_child_at_index() {
let mut taffy = Taffy::new();
let child0 = taffy.new_leaf(Style::default()).unwrap();
let child1 = taffy.new_leaf(Style::default()).unwrap();
let node = taffy.new_with_children(Style::default(), &[child0]).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 1);
assert_eq!(taffy.children(node).unwrap()[0], child0);
taffy.replace_child_at_index(node, 0, child1).unwrap();
assert_eq!(taffy.child_count(node).unwrap(), 1);
assert_eq!(taffy.children(node).unwrap()[0], child1);
}
#[test]
fn test_child_at_index() {
let mut taffy = Taffy::new();
let child0 = taffy.new_leaf(Style::default()).unwrap();
let child1 = taffy.new_leaf(Style::default()).unwrap();
let child2 = taffy.new_leaf(Style::default()).unwrap();
let node = taffy.new_with_children(Style::default(), &[child0, child1, child2]).unwrap();
assert!(if let Ok(result) = taffy.child_at_index(node, 0) { result == child0 } else { false });
assert!(if let Ok(result) = taffy.child_at_index(node, 1) { result == child1 } else { false });
assert!(if let Ok(result) = taffy.child_at_index(node, 2) { result == child2 } else { false });
}
#[test]
fn test_child_count() {
let mut taffy = Taffy::new();
let child0 = taffy.new_leaf(Style::default()).unwrap();
let child1 = taffy.new_leaf(Style::default()).unwrap();
let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
assert!(if let Ok(count) = taffy.child_count(node) { count == 2 } else { false });
assert!(if let Ok(count) = taffy.child_count(child0) { count == 0 } else { false });
assert!(if let Ok(count) = taffy.child_count(child1) { count == 0 } else { false });
}
#[allow(clippy::vec_init_then_push)]
#[test]
fn test_children() {
let mut taffy = Taffy::new();
let child0 = taffy.new_leaf(Style::default()).unwrap();
let child1 = taffy.new_leaf(Style::default()).unwrap();
let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
let mut children: sys::Vec<Node> = sys::Vec::new();
children.push(child0);
children.push(child1);
let children_result = taffy.children(node).unwrap();
assert_eq!(children_result, children);
assert!(taffy.children(child0).unwrap().is_empty());
}
#[test]
fn test_set_style() {
let mut taffy = Taffy::new();
let node = taffy.new_leaf(Style::default()).unwrap();
assert_eq!(taffy.style(node).unwrap().display, Display::Flex);
taffy.set_style(node, Style { display: Display::None, ..Style::default() }).unwrap();
assert_eq!(taffy.style(node).unwrap().display, Display::None);
}
#[test]
fn test_style() {
let mut taffy = Taffy::new();
let style = Style { display: Display::None, flex_direction: FlexDirection::RowReverse, ..Default::default() };
let node = taffy.new_leaf(style.clone()).unwrap();
let res = taffy.style(node);
assert!(res.is_ok());
assert!(res.unwrap() == &style);
}
#[test]
fn test_layout() {
let mut taffy = Taffy::new();
let node = taffy.new_leaf(Style::default()).unwrap();
let res = taffy.layout(node);
assert!(res.is_ok());
}
#[test]
fn test_mark_dirty() {
let mut taffy = Taffy::new();
let child0 = taffy.new_leaf(Style::default()).unwrap();
let child1 = taffy.new_leaf(Style::default()).unwrap();
let node = taffy.new_with_children(Style::default(), &[child0, child1]).unwrap();
taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
assert_eq!(taffy.dirty(child0).unwrap(), false);
assert_eq!(taffy.dirty(child1).unwrap(), false);
assert_eq!(taffy.dirty(node).unwrap(), false);
taffy.mark_dirty(node).unwrap();
assert_eq!(taffy.dirty(child0).unwrap(), false);
assert_eq!(taffy.dirty(child1).unwrap(), false);
assert_eq!(taffy.dirty(node).unwrap(), true);
taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
taffy.mark_dirty(child0).unwrap();
assert_eq!(taffy.dirty(child0).unwrap(), true);
assert_eq!(taffy.dirty(child1).unwrap(), false);
assert_eq!(taffy.dirty(node).unwrap(), true);
}
#[test]
fn compute_layout_should_produce_valid_result() {
let mut taffy = Taffy::new();
let node_result = taffy.new_leaf(Style {
size: Size { width: Dimension::Points(10f32), height: Dimension::Points(10f32) },
..Default::default()
});
assert!(node_result.is_ok());
let node = node_result.unwrap();
let layout_result = taffy.compute_layout(
node,
Size { width: AvailableSpace::Definite(100.), height: AvailableSpace::Definite(100.) },
);
assert!(layout_result.is_ok());
}
#[test]
fn measure_func_is_send_and_sync() {
fn is_send_and_sync<T: Send + Sync>() {}
is_send_and_sync::<MeasureFunc>();
}
}