use std::collections::HashMap;
pub use taffy;
pub use taffy::prelude::*;
#[derive(Debug)]
pub enum LayoutError {
Taffy(String),
}
impl std::fmt::Display for LayoutError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Taffy(s) => write!(f, "taffy: {s}"),
}
}
}
impl std::error::Error for LayoutError {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rect {
pub x: f32,
pub y: f32,
pub w: f32,
pub h: f32,
}
#[derive(Debug, Default)]
pub struct ComputedLayout {
pub rects: HashMap<NodeId, Rect>,
}
impl ComputedLayout {
pub fn get(&self, node: NodeId) -> Option<Rect> {
self.rects.get(&node).copied()
}
}
pub struct LayoutTree {
inner: TaffyTree<()>,
}
impl Default for LayoutTree {
fn default() -> Self {
Self::new()
}
}
impl LayoutTree {
pub fn new() -> Self {
Self {
inner: TaffyTree::new(),
}
}
pub fn clear(&mut self) {
self.inner.clear();
}
pub fn leaf(&mut self, style: Style) -> Result<NodeId, LayoutError> {
self.inner
.new_leaf(style)
.map_err(|e| LayoutError::Taffy(e.to_string()))
}
pub fn node(&mut self, style: Style, children: &[NodeId]) -> Result<NodeId, LayoutError> {
self.inner
.new_with_children(style, children)
.map_err(|e| LayoutError::Taffy(e.to_string()))
}
pub fn compute(
&mut self,
root: NodeId,
viewport: (f32, f32),
) -> Result<ComputedLayout, LayoutError> {
self.inner
.compute_layout(
root,
taffy::Size {
width: AvailableSpace::Definite(viewport.0),
height: AvailableSpace::Definite(viewport.1),
},
)
.map_err(|e| LayoutError::Taffy(e.to_string()))?;
let mut out = ComputedLayout::default();
flatten(&self.inner, root, 0.0, 0.0, &mut out.rects)?;
Ok(out)
}
pub fn compute_with_measure<F>(
&mut self,
root: NodeId,
viewport: (f32, f32),
mut measure: F,
) -> Result<ComputedLayout, LayoutError>
where
F: FnMut(NodeId, taffy::Size<Option<f32>>, taffy::Size<AvailableSpace>) -> taffy::Size<f32>,
{
self.inner
.compute_layout_with_measure(
root,
taffy::Size {
width: AvailableSpace::Definite(viewport.0),
height: AvailableSpace::Definite(viewport.1),
},
|known, available, node_id, _ctx, _style| {
measure(node_id, known, available)
},
)
.map_err(|e| LayoutError::Taffy(e.to_string()))?;
let mut out = ComputedLayout::default();
flatten(&self.inner, root, 0.0, 0.0, &mut out.rects)?;
Ok(out)
}
pub fn inner(&self) -> &TaffyTree<()> {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut TaffyTree<()> {
&mut self.inner
}
}
fn flatten(
tree: &TaffyTree<()>,
node: NodeId,
ox: f32,
oy: f32,
out: &mut HashMap<NodeId, Rect>,
) -> Result<(), LayoutError> {
let layout = tree
.layout(node)
.map_err(|e| LayoutError::Taffy(e.to_string()))?;
let x = ox + layout.location.x;
let y = oy + layout.location.y;
out.insert(
node,
Rect {
x,
y,
w: layout.size.width,
h: layout.size.height,
},
);
let children = tree
.children(node)
.map_err(|e| LayoutError::Taffy(e.to_string()))?;
for child in children {
flatten(tree, child, x, y, out)?;
}
Ok(())
}