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