1use std::collections::HashMap;
9
10pub use taffy;
11pub use taffy::prelude::*;
12
13#[derive(Debug)]
15pub enum LayoutError {
16 Taffy(String),
17}
18
19impl std::fmt::Display for LayoutError {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 match self {
22 Self::Taffy(s) => write!(f, "taffy: {s}"),
23 }
24 }
25}
26
27impl std::error::Error for LayoutError {}
28
29#[derive(Debug, Clone, Copy, PartialEq)]
31pub struct Rect {
32 pub x: f32,
33 pub y: f32,
34 pub w: f32,
35 pub h: f32,
36}
37
38#[derive(Debug, Default)]
40pub struct ComputedLayout {
41 pub rects: HashMap<NodeId, Rect>,
42}
43
44impl ComputedLayout {
45 pub fn get(&self, node: NodeId) -> Option<Rect> {
46 self.rects.get(&node).copied()
47 }
48}
49
50pub struct LayoutTree {
52 inner: TaffyTree<()>,
53}
54
55impl Default for LayoutTree {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl LayoutTree {
62 pub fn new() -> Self {
63 Self {
64 inner: TaffyTree::new(),
65 }
66 }
67
68 pub fn clear(&mut self) {
75 self.inner.clear();
76 }
77
78 pub fn leaf(&mut self, style: Style) -> Result<NodeId, LayoutError> {
80 self.inner
81 .new_leaf(style)
82 .map_err(|e| LayoutError::Taffy(e.to_string()))
83 }
84
85 pub fn node(&mut self, style: Style, children: &[NodeId]) -> Result<NodeId, LayoutError> {
87 self.inner
88 .new_with_children(style, children)
89 .map_err(|e| LayoutError::Taffy(e.to_string()))
90 }
91
92 pub fn compute(
94 &mut self,
95 root: NodeId,
96 viewport: (f32, f32),
97 ) -> Result<ComputedLayout, LayoutError> {
98 self.inner
99 .compute_layout(
100 root,
101 taffy::Size {
102 width: AvailableSpace::Definite(viewport.0),
103 height: AvailableSpace::Definite(viewport.1),
104 },
105 )
106 .map_err(|e| LayoutError::Taffy(e.to_string()))?;
107 let mut out = ComputedLayout::default();
108 flatten(&self.inner, root, 0.0, 0.0, &mut out.rects)?;
109 Ok(out)
110 }
111
112 pub fn compute_with_measure<F>(
122 &mut self,
123 root: NodeId,
124 viewport: (f32, f32),
125 mut measure: F,
126 ) -> Result<ComputedLayout, LayoutError>
127 where
128 F: FnMut(NodeId, taffy::Size<Option<f32>>, taffy::Size<AvailableSpace>) -> taffy::Size<f32>,
129 {
130 self.inner
131 .compute_layout_with_measure(
132 root,
133 taffy::Size {
134 width: AvailableSpace::Definite(viewport.0),
135 height: AvailableSpace::Definite(viewport.1),
136 },
137 |known, available, node_id, _ctx, _style| {
138 measure(node_id, known, available)
139 },
140 )
141 .map_err(|e| LayoutError::Taffy(e.to_string()))?;
142 let mut out = ComputedLayout::default();
143 flatten(&self.inner, root, 0.0, 0.0, &mut out.rects)?;
144 Ok(out)
145 }
146
147 pub fn inner(&self) -> &TaffyTree<()> {
148 &self.inner
149 }
150
151 pub fn inner_mut(&mut self) -> &mut TaffyTree<()> {
152 &mut self.inner
153 }
154}
155
156fn flatten(
157 tree: &TaffyTree<()>,
158 node: NodeId,
159 ox: f32,
160 oy: f32,
161 out: &mut HashMap<NodeId, Rect>,
162) -> Result<(), LayoutError> {
163 let layout = tree
164 .layout(node)
165 .map_err(|e| LayoutError::Taffy(e.to_string()))?;
166 let x = ox + layout.location.x;
167 let y = oy + layout.location.y;
168 out.insert(
169 node,
170 Rect {
171 x,
172 y,
173 w: layout.size.width,
174 h: layout.size.height,
175 },
176 );
177 let children = tree
178 .children(node)
179 .map_err(|e| LayoutError::Taffy(e.to_string()))?;
180 for child in children {
181 flatten(tree, child, x, y, out)?;
182 }
183 Ok(())
184}