1use crate::{
2 AbsoluteLength, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style,
3 WindowContext,
4};
5use collections::{FxHashMap, FxHashSet};
6use smallvec::SmallVec;
7use std::fmt::Debug;
8use taffy::{
9 geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
10 style::AvailableSpace as TaffyAvailableSpace,
11 tree::NodeId,
12 Taffy,
13};
14
15pub struct TaffyLayoutEngine {
16 taffy: Taffy,
17 styles: FxHashMap<LayoutId, Style>,
18 children_to_parents: FxHashMap<LayoutId, LayoutId>,
19 absolute_layout_bounds: FxHashMap<LayoutId, Bounds<Pixels>>,
20 computed_layouts: FxHashSet<LayoutId>,
21 nodes_to_measure: FxHashMap<
22 LayoutId,
23 Box<
24 dyn FnMut(
25 Size<Option<Pixels>>,
26 Size<AvailableSpace>,
27 &mut WindowContext,
28 ) -> Size<Pixels>,
29 >,
30 >,
31}
32
33static EXPECT_MESSAGE: &str = "we should avoid taffy layout errors by construction if possible";
34
35impl TaffyLayoutEngine {
36 pub fn new() -> Self {
37 TaffyLayoutEngine {
38 taffy: Taffy::new(),
39 styles: FxHashMap::default(),
40 children_to_parents: FxHashMap::default(),
41 absolute_layout_bounds: FxHashMap::default(),
42 computed_layouts: FxHashSet::default(),
43 nodes_to_measure: FxHashMap::default(),
44 }
45 }
46
47 pub fn clear(&mut self) {
48 self.taffy.clear();
49 self.children_to_parents.clear();
50 self.absolute_layout_bounds.clear();
51 self.computed_layouts.clear();
52 self.nodes_to_measure.clear();
53 self.styles.clear();
54 }
55
56 pub fn requested_style(&self, layout_id: LayoutId) -> Option<&Style> {
57 self.styles.get(&layout_id)
58 }
59
60 pub fn request_layout(
61 &mut self,
62 style: &Style,
63 rem_size: Pixels,
64 children: &[LayoutId],
65 ) -> LayoutId {
66 let taffy_style = style.to_taffy(rem_size);
67 let layout_id = if children.is_empty() {
68 self.taffy
69 .new_leaf(taffy_style)
70 .expect(EXPECT_MESSAGE)
71 .into()
72 } else {
73 let parent_id = self
74 .taffy
75 // This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId.
76 .new_with_children(taffy_style, unsafe { std::mem::transmute(children) })
77 .expect(EXPECT_MESSAGE)
78 .into();
79 for child_id in children {
80 self.children_to_parents.insert(*child_id, parent_id);
81 }
82 parent_id
83 };
84 self.styles.insert(layout_id, style.clone());
85 layout_id
86 }
87
88 pub fn request_measured_layout(
89 &mut self,
90 style: Style,
91 rem_size: Pixels,
92 measure: impl FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut WindowContext) -> Size<Pixels>
93 + 'static,
94 ) -> LayoutId {
95 let style = style.clone();
96 let taffy_style = style.to_taffy(rem_size);
97
98 let layout_id = self
99 .taffy
100 .new_leaf_with_context(taffy_style, ())
101 .expect(EXPECT_MESSAGE)
102 .into();
103 self.nodes_to_measure.insert(layout_id, Box::new(measure));
104 self.styles.insert(layout_id, style.clone());
105 layout_id
106 }
107
108 // Used to understand performance
109 #[allow(dead_code)]
110 fn count_all_children(&self, parent: LayoutId) -> anyhow::Result<u32> {
111 let mut count = 0;
112
113 for child in self.taffy.children(parent.0)? {
114 // Count this child.
115 count += 1;
116
117 // Count all of this child's children.
118 count += self.count_all_children(LayoutId(child))?
119 }
120
121 Ok(count)
122 }
123
124 // Used to understand performance
125 #[allow(dead_code)]
126 fn max_depth(&self, depth: u32, parent: LayoutId) -> anyhow::Result<u32> {
127 println!(
128 "{parent:?} at depth {depth} has {} children",
129 self.taffy.child_count(parent.0)?
130 );
131
132 let mut max_child_depth = 0;
133
134 for child in self.taffy.children(parent.0)? {
135 max_child_depth = std::cmp::max(max_child_depth, self.max_depth(0, LayoutId(child))?);
136 }
137
138 Ok(depth + 1 + max_child_depth)
139 }
140
141 // Used to understand performance
142 #[allow(dead_code)]
143 fn get_edges(&self, parent: LayoutId) -> anyhow::Result<Vec<(LayoutId, LayoutId)>> {
144 let mut edges = Vec::new();
145
146 for child in self.taffy.children(parent.0)? {
147 edges.push((parent, LayoutId(child)));
148
149 edges.extend(self.get_edges(LayoutId(child))?);
150 }
151
152 Ok(edges)
153 }
154
155 pub fn compute_layout(
156 &mut self,
157 id: LayoutId,
158 available_space: Size<AvailableSpace>,
159 cx: &mut WindowContext,
160 ) {
161 // Leaving this here until we have a better instrumentation approach.
162 // println!("Laying out {} children", self.count_all_children(id)?);
163 // println!("Max layout depth: {}", self.max_depth(0, id)?);
164
165 // Output the edges (branches) of the tree in Mermaid format for visualization.
166 // println!("Edges:");
167 // for (a, b) in self.get_edges(id)? {
168 // println!("N{} --> N{}", u64::from(a), u64::from(b));
169 // }
170 // println!("");
171 //
172
173 if !self.computed_layouts.insert(id) {
174 let mut stack = SmallVec::<[LayoutId; 64]>::new();
175 stack.push(id);
176 while let Some(id) = stack.pop() {
177 self.absolute_layout_bounds.remove(&id);
178 stack.extend(
179 self.taffy
180 .children(id.into())
181 .expect(EXPECT_MESSAGE)
182 .into_iter()
183 .map(Into::into),
184 );
185 }
186 }
187
188 // let started_at = std::time::Instant::now();
189 self.taffy
190 .compute_layout_with_measure(
191 id.into(),
192 available_space.into(),
193 |known_dimensions, available_space, node_id, _context| {
194 let Some(measure) = self.nodes_to_measure.get_mut(&node_id.into()) else {
195 return taffy::geometry::Size::default();
196 };
197
198 let known_dimensions = Size {
199 width: known_dimensions.width.map(Pixels),
200 height: known_dimensions.height.map(Pixels),
201 };
202
203 measure(known_dimensions, available_space.into(), cx).into()
204 },
205 )
206 .expect(EXPECT_MESSAGE);
207
208 // println!("compute_layout took {:?}", started_at.elapsed());
209 }
210
211 pub fn layout_bounds(&mut self, id: LayoutId) -> Bounds<Pixels> {
212 if let Some(layout) = self.absolute_layout_bounds.get(&id).cloned() {
213 return layout;
214 }
215
216 let layout = self.taffy.layout(id.into()).expect(EXPECT_MESSAGE);
217 let mut bounds = Bounds {
218 origin: layout.location.into(),
219 size: layout.size.into(),
220 };
221
222 if let Some(parent_id) = self.children_to_parents.get(&id).copied() {
223 let parent_bounds = self.layout_bounds(parent_id);
224 bounds.origin += parent_bounds.origin;
225 }
226 self.absolute_layout_bounds.insert(id, bounds);
227
228 bounds
229 }
230}
231
232#[derive(Copy, Clone, Eq, PartialEq, Debug)]
233#[repr(transparent)]
234pub struct LayoutId(NodeId);
235
236impl std::hash::Hash for LayoutId {
237 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
238 u64::from(self.0).hash(state);
239 }
240}
241
242impl From<NodeId> for LayoutId {
243 fn from(node_id: NodeId) -> Self {
244 Self(node_id)
245 }
246}
247
248impl From<LayoutId> for NodeId {
249 fn from(layout_id: LayoutId) -> NodeId {
250 layout_id.0
251 }
252}
253
254trait ToTaffy<Output> {
255 fn to_taffy(&self, rem_size: Pixels) -> Output;
256}
257
258impl ToTaffy<taffy::style::Style> for Style {
259 fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Style {
260 taffy::style::Style {
261 display: self.display,
262 overflow: self.overflow.into(),
263 scrollbar_width: self.scrollbar_width,
264 position: self.position,
265 inset: self.inset.to_taffy(rem_size),
266 size: self.size.to_taffy(rem_size),
267 min_size: self.min_size.to_taffy(rem_size),
268 max_size: self.max_size.to_taffy(rem_size),
269 aspect_ratio: self.aspect_ratio,
270 margin: self.margin.to_taffy(rem_size),
271 padding: self.padding.to_taffy(rem_size),
272 border: self.border_widths.to_taffy(rem_size),
273 align_items: self.align_items,
274 align_self: self.align_self,
275 align_content: self.align_content,
276 justify_content: self.justify_content,
277 gap: self.gap.to_taffy(rem_size),
278 flex_direction: self.flex_direction,
279 flex_wrap: self.flex_wrap,
280 flex_basis: self.flex_basis.to_taffy(rem_size),
281 flex_grow: self.flex_grow,
282 flex_shrink: self.flex_shrink,
283 ..Default::default() // Ignore grid properties for now
284 }
285 }
286}
287
288impl ToTaffy<taffy::style::LengthPercentageAuto> for Length {
289 fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::LengthPercentageAuto {
290 match self {
291 Length::Definite(length) => length.to_taffy(rem_size),
292 Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
293 }
294 }
295}
296
297impl ToTaffy<taffy::style::Dimension> for Length {
298 fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::Dimension {
299 match self {
300 Length::Definite(length) => length.to_taffy(rem_size),
301 Length::Auto => taffy::prelude::Dimension::Auto,
302 }
303 }
304}
305
306impl ToTaffy<taffy::style::LengthPercentage> for DefiniteLength {
307 fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentage {
308 match self {
309 DefiniteLength::Absolute(length) => match length {
310 AbsoluteLength::Pixels(pixels) => {
311 taffy::style::LengthPercentage::Length(pixels.into())
312 }
313 AbsoluteLength::Rems(rems) => {
314 taffy::style::LengthPercentage::Length((*rems * rem_size).into())
315 }
316 },
317 DefiniteLength::Fraction(fraction) => {
318 taffy::style::LengthPercentage::Percent(*fraction)
319 }
320 }
321 }
322}
323
324impl ToTaffy<taffy::style::LengthPercentageAuto> for DefiniteLength {
325 fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentageAuto {
326 match self {
327 DefiniteLength::Absolute(length) => match length {
328 AbsoluteLength::Pixels(pixels) => {
329 taffy::style::LengthPercentageAuto::Length(pixels.into())
330 }
331 AbsoluteLength::Rems(rems) => {
332 taffy::style::LengthPercentageAuto::Length((*rems * rem_size).into())
333 }
334 },
335 DefiniteLength::Fraction(fraction) => {
336 taffy::style::LengthPercentageAuto::Percent(*fraction)
337 }
338 }
339 }
340}
341
342impl ToTaffy<taffy::style::Dimension> for DefiniteLength {
343 fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Dimension {
344 match self {
345 DefiniteLength::Absolute(length) => match length {
346 AbsoluteLength::Pixels(pixels) => taffy::style::Dimension::Length(pixels.into()),
347 AbsoluteLength::Rems(rems) => {
348 taffy::style::Dimension::Length((*rems * rem_size).into())
349 }
350 },
351 DefiniteLength::Fraction(fraction) => taffy::style::Dimension::Percent(*fraction),
352 }
353 }
354}
355
356impl ToTaffy<taffy::style::LengthPercentage> for AbsoluteLength {
357 fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentage {
358 match self {
359 AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(pixels.into()),
360 AbsoluteLength::Rems(rems) => {
361 taffy::style::LengthPercentage::Length((*rems * rem_size).into())
362 }
363 }
364 }
365}
366
367impl<T, T2> From<TaffyPoint<T>> for Point<T2>
368where
369 T: Into<T2>,
370 T2: Clone + Default + Debug,
371{
372 fn from(point: TaffyPoint<T>) -> Point<T2> {
373 Point {
374 x: point.x.into(),
375 y: point.y.into(),
376 }
377 }
378}
379
380impl<T, T2> From<Point<T>> for TaffyPoint<T2>
381where
382 T: Into<T2> + Clone + Default + Debug,
383{
384 fn from(val: Point<T>) -> Self {
385 TaffyPoint {
386 x: val.x.into(),
387 y: val.y.into(),
388 }
389 }
390}
391
392impl<T, U> ToTaffy<TaffySize<U>> for Size<T>
393where
394 T: ToTaffy<U> + Clone + Default + Debug,
395{
396 fn to_taffy(&self, rem_size: Pixels) -> TaffySize<U> {
397 TaffySize {
398 width: self.width.to_taffy(rem_size),
399 height: self.height.to_taffy(rem_size),
400 }
401 }
402}
403
404impl<T, U> ToTaffy<TaffyRect<U>> for Edges<T>
405where
406 T: ToTaffy<U> + Clone + Default + Debug,
407{
408 fn to_taffy(&self, rem_size: Pixels) -> TaffyRect<U> {
409 TaffyRect {
410 top: self.top.to_taffy(rem_size),
411 right: self.right.to_taffy(rem_size),
412 bottom: self.bottom.to_taffy(rem_size),
413 left: self.left.to_taffy(rem_size),
414 }
415 }
416}
417
418impl<T, U> From<TaffySize<T>> for Size<U>
419where
420 T: Into<U>,
421 U: Clone + Default + Debug,
422{
423 fn from(taffy_size: TaffySize<T>) -> Self {
424 Size {
425 width: taffy_size.width.into(),
426 height: taffy_size.height.into(),
427 }
428 }
429}
430
431impl<T, U> From<Size<T>> for TaffySize<U>
432where
433 T: Into<U> + Clone + Default + Debug,
434{
435 fn from(size: Size<T>) -> Self {
436 TaffySize {
437 width: size.width.into(),
438 height: size.height.into(),
439 }
440 }
441}
442
443#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
444pub enum AvailableSpace {
445 /// The amount of space available is the specified number of pixels
446 Definite(Pixels),
447 /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
448 #[default]
449 MinContent,
450 /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
451 MaxContent,
452}
453
454impl From<AvailableSpace> for TaffyAvailableSpace {
455 fn from(space: AvailableSpace) -> TaffyAvailableSpace {
456 match space {
457 AvailableSpace::Definite(Pixels(value)) => TaffyAvailableSpace::Definite(value),
458 AvailableSpace::MinContent => TaffyAvailableSpace::MinContent,
459 AvailableSpace::MaxContent => TaffyAvailableSpace::MaxContent,
460 }
461 }
462}
463
464impl From<TaffyAvailableSpace> for AvailableSpace {
465 fn from(space: TaffyAvailableSpace) -> AvailableSpace {
466 match space {
467 TaffyAvailableSpace::Definite(value) => AvailableSpace::Definite(Pixels(value)),
468 TaffyAvailableSpace::MinContent => AvailableSpace::MinContent,
469 TaffyAvailableSpace::MaxContent => AvailableSpace::MaxContent,
470 }
471 }
472}
473
474impl From<Pixels> for AvailableSpace {
475 fn from(pixels: Pixels) -> Self {
476 AvailableSpace::Definite(pixels)
477 }
478}
479
480impl From<Size<Pixels>> for Size<AvailableSpace> {
481 fn from(size: Size<Pixels>) -> Self {
482 Size {
483 width: AvailableSpace::Definite(size.width),
484 height: AvailableSpace::Definite(size.height),
485 }
486 }
487}