1use crate::{
2 AbsoluteLength, App, Bounds, DefiniteLength, Edges, GridTemplate, Length, Pixels, Point, Size,
3 Style, Window, point, size,
4 util::{
5 ceil_to_device_pixel, round_half_toward_zero, round_stroke_to_device_pixel,
6 round_to_device_pixel,
7 },
8};
9use collections::{FxHashMap, FxHashSet};
10use smallvec::SmallVec;
11use stacksafe::{StackSafe, stacksafe};
12use std::{fmt::Debug, ops::Range};
13use taffy::{
14 TaffyTree, TraversePartialTree as _,
15 geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
16 prelude::{max_content, min_content},
17 style::AvailableSpace as TaffyAvailableSpace,
18 tree::NodeId,
19};
20
21type NodeMeasureFn = StackSafe<
22 Box<
23 dyn FnMut(
24 Size<Option<Pixels>>,
25 Size<AvailableSpace>,
26 &mut Window,
27 &mut App,
28 ) -> Size<Pixels>,
29 >,
30>;
31
32struct NodeContext {
33 measure: NodeMeasureFn,
34}
35pub struct TaffyLayoutEngine {
36 taffy: TaffyTree<NodeContext>,
37 absolute_layout_bounds: FxHashMap<LayoutId, Bounds<Pixels>>,
38 absolute_outer_origins: FxHashMap<LayoutId, Point<f32>>,
39 computed_layouts: FxHashSet<LayoutId>,
40}
41
42const EXPECT_MESSAGE: &str = "we should avoid taffy layout errors by construction if possible";
43
44impl TaffyLayoutEngine {
45 pub fn new() -> Self {
46 let mut taffy = TaffyTree::new();
47 taffy.disable_rounding();
48 TaffyLayoutEngine {
49 taffy,
50 absolute_layout_bounds: FxHashMap::default(),
51 absolute_outer_origins: FxHashMap::default(),
52 computed_layouts: FxHashSet::default(),
53 }
54 }
55
56 pub fn clear(&mut self) {
57 self.taffy.clear();
58 self.absolute_layout_bounds.clear();
59 self.absolute_outer_origins.clear();
60 self.computed_layouts.clear();
61 }
62
63 pub fn request_layout(
64 &mut self,
65 style: Style,
66 rem_size: Pixels,
67 scale_factor: f32,
68 children: &[LayoutId],
69 ) -> LayoutId {
70 let taffy_style = style.to_taffy(rem_size, scale_factor);
71
72 if children.is_empty() {
73 self.taffy
74 .new_leaf(taffy_style)
75 .expect(EXPECT_MESSAGE)
76 .into()
77 } else {
78 self.taffy
79 .new_with_children(taffy_style, unsafe {
81 std::mem::transmute::<&[LayoutId], &[taffy::NodeId]>(children)
82 })
83 .expect(EXPECT_MESSAGE)
84 .into()
85 }
86 }
87
88 pub fn request_measured_layout(
89 &mut self,
90 style: Style,
91 rem_size: Pixels,
92 scale_factor: f32,
93 measure: impl FnMut(
94 Size<Option<Pixels>>,
95 Size<AvailableSpace>,
96 &mut Window,
97 &mut App,
98 ) -> Size<Pixels>
99 + 'static,
100 ) -> LayoutId {
101 let taffy_style = style.to_taffy(rem_size, scale_factor);
102
103 self.taffy
104 .new_leaf_with_context(
105 taffy_style,
106 NodeContext {
107 measure: StackSafe::new(Box::new(measure)),
108 },
109 )
110 .expect(EXPECT_MESSAGE)
111 .into()
112 }
113
114 pub fn stretch_auto_size_to_fill(
117 &mut self,
118 id: LayoutId,
119 size: Size<Pixels>,
120 scale_factor: f32,
121 ) {
122 let style = self.taffy.style(id.0).expect(EXPECT_MESSAGE);
123 let stretch_width = style.size.width.is_auto();
124 let stretch_height = style.size.height.is_auto();
125 if !stretch_width && !stretch_height {
126 return;
127 }
128
129 let mut style = style.clone();
130 if stretch_width {
131 style.size.width =
132 taffy::style::Dimension::length(round_to_device_pixel(size.width.0, scale_factor));
133 }
134 if stretch_height {
135 style.size.height =
136 taffy::style::Dimension::length(round_to_device_pixel(size.height.0, scale_factor));
137 }
138 self.taffy.set_style(id.0, style).expect(EXPECT_MESSAGE);
139 }
140
141 #[allow(dead_code)]
143 fn count_all_children(&self, parent: LayoutId) -> anyhow::Result<u32> {
144 let mut count = 0;
145
146 for child in self.taffy.children(parent.0)? {
147 count += 1;
149
150 count += self.count_all_children(LayoutId(child))?
152 }
153
154 Ok(count)
155 }
156
157 #[allow(dead_code)]
159 fn max_depth(&self, depth: u32, parent: LayoutId) -> anyhow::Result<u32> {
160 println!(
161 "{parent:?} at depth {depth} has {} children",
162 self.taffy.child_count(parent.0)
163 );
164
165 let mut max_child_depth = 0;
166
167 for child in self.taffy.children(parent.0)? {
168 max_child_depth = std::cmp::max(max_child_depth, self.max_depth(0, LayoutId(child))?);
169 }
170
171 Ok(depth + 1 + max_child_depth)
172 }
173
174 #[allow(dead_code)]
176 fn get_edges(&self, parent: LayoutId) -> anyhow::Result<Vec<(LayoutId, LayoutId)>> {
177 let mut edges = Vec::new();
178
179 for child in self.taffy.children(parent.0)? {
180 edges.push((parent, LayoutId(child)));
181
182 edges.extend(self.get_edges(LayoutId(child))?);
183 }
184
185 Ok(edges)
186 }
187
188 #[stacksafe]
189 pub fn compute_layout(
190 &mut self,
191 id: LayoutId,
192 available_space: Size<AvailableSpace>,
193 window: &mut Window,
194 cx: &mut App,
195 ) {
196 if !self.computed_layouts.insert(id) {
208 let mut stack = SmallVec::<[LayoutId; 64]>::new();
209 stack.push(id);
210 while let Some(id) = stack.pop() {
211 self.absolute_layout_bounds.remove(&id);
212 self.absolute_outer_origins.remove(&id);
213 stack.extend(
214 self.taffy
215 .children(id.into())
216 .expect(EXPECT_MESSAGE)
217 .into_iter()
218 .map(Into::into),
219 );
220 }
221 }
222
223 let scale_factor = window.scale_factor();
224
225 let transform = |v: AvailableSpace| match v {
226 AvailableSpace::Definite(pixels) => {
227 AvailableSpace::Definite(Pixels(pixels.0 * scale_factor))
228 }
229 AvailableSpace::MinContent => AvailableSpace::MinContent,
230 AvailableSpace::MaxContent => AvailableSpace::MaxContent,
231 };
232 let available_space = size(
233 transform(available_space.width),
234 transform(available_space.height),
235 );
236
237 self.taffy
238 .compute_layout_with_measure(
239 id.into(),
240 available_space.into(),
241 |known_dimensions, available_space, _id, node_context, _style| {
242 let Some(node_context) = node_context else {
243 return taffy::geometry::Size::default();
244 };
245
246 let known_dimensions = Size {
247 width: known_dimensions.width.map(|e| Pixels(e / scale_factor)),
248 height: known_dimensions.height.map(|e| Pixels(e / scale_factor)),
249 };
250
251 let available_space: Size<AvailableSpace> = available_space.into();
252 let untransform = |ev: AvailableSpace| match ev {
253 AvailableSpace::Definite(pixels) => {
254 AvailableSpace::Definite(Pixels(pixels.0 / scale_factor))
255 }
256 AvailableSpace::MinContent => AvailableSpace::MinContent,
257 AvailableSpace::MaxContent => AvailableSpace::MaxContent,
258 };
259 let available_space = size(
260 untransform(available_space.width),
261 untransform(available_space.height),
262 );
263
264 let measured_size: Size<Pixels> =
265 (node_context.measure)(known_dimensions, available_space, window, cx);
266 snap_measured_size_to_device_pixels(measured_size, scale_factor).into()
267 },
268 )
269 .expect(EXPECT_MESSAGE);
270 }
271
272 pub fn layout_bounds(&mut self, id: LayoutId, scale_factor: f32) -> Bounds<Pixels> {
277 if let Some(layout) = self.absolute_layout_bounds.get(&id).cloned() {
278 return layout;
279 }
280
281 let layout = self.taffy.layout(id.into()).expect(EXPECT_MESSAGE);
282 let layout_location = layout.location;
283 let layout_size = layout.size;
284 let parent = self.taffy.parent(id.0);
285
286 let absolute_outer_origin = match parent {
287 Some(parent_id) => {
288 let parent_id = LayoutId::from(parent_id);
289 self.layout_bounds(parent_id, scale_factor);
290 let parent_origin = *self
291 .absolute_outer_origins
292 .get(&parent_id)
293 .expect("parent absolute outer origin should be cached");
294 parent_origin + Point::from(layout_location)
295 }
296 None => Point::from(layout_location),
297 };
298 self.absolute_outer_origins
299 .insert(id, absolute_outer_origin);
300
301 let layout_size = Size::from(layout_size);
302 let absolute_far = absolute_outer_origin + point(layout_size.width, layout_size.height);
303 let snapped_bounds = Bounds::from_corners(
304 absolute_outer_origin.map(round_half_toward_zero),
305 absolute_far.map(round_half_toward_zero),
306 );
307
308 let bounds = (snapped_bounds / scale_factor).map(Pixels);
309 self.absolute_layout_bounds.insert(id, bounds);
310
311 bounds
312 }
313}
314
315#[derive(Copy, Clone, Eq, PartialEq, Debug)]
317#[repr(transparent)]
318pub struct LayoutId(NodeId);
319
320impl std::hash::Hash for LayoutId {
321 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
322 u64::from(self.0).hash(state);
323 }
324}
325
326impl From<NodeId> for LayoutId {
327 fn from(node_id: NodeId) -> Self {
328 Self(node_id)
329 }
330}
331
332impl From<LayoutId> for NodeId {
333 fn from(layout_id: LayoutId) -> NodeId {
334 layout_id.0
335 }
336}
337
338trait ToTaffy<Output> {
339 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> Output;
340}
341
342fn snap_measured_size_to_device_pixels(size: Size<Pixels>, scale_factor: f32) -> Size<f32> {
343 size.map(|d| ceil_to_device_pixel(d.0.max(0.0), scale_factor))
344}
345
346fn border_widths_to_taffy(
347 widths: &Edges<AbsoluteLength>,
348 rem_size: Pixels,
349 scale_factor: f32,
350) -> TaffyRect<taffy::style::LengthPercentage> {
351 let snap = |w: &AbsoluteLength| {
352 taffy::style::LengthPercentage::length(round_stroke_to_device_pixel(
353 w.to_pixels(rem_size).0,
354 scale_factor,
355 ))
356 };
357 TaffyRect {
358 top: snap(&widths.top),
359 right: snap(&widths.right),
360 bottom: snap(&widths.bottom),
361 left: snap(&widths.left),
362 }
363}
364
365impl ToTaffy<taffy::style::Style> for Style {
366 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Style {
367 use taffy::style_helpers::{fr, length, minmax, repeat};
368
369 fn to_grid_line(
370 placement: &Range<crate::GridPlacement>,
371 ) -> taffy::Line<taffy::GridPlacement> {
372 taffy::Line {
373 start: placement.start.into(),
374 end: placement.end.into(),
375 }
376 }
377
378 fn to_grid_repeat<T: taffy::style::CheapCloneStr>(
379 unit: &Option<GridTemplate>,
380 ) -> Vec<taffy::GridTemplateComponent<T>> {
381 unit.map(|template| match template.min_size {
382 crate::TemplateColumnMinSize::Zero => {
383 vec![repeat(
384 template.repeat,
385 vec![minmax(length(0.0_f32), fr(1.0_f32))],
386 )]
387 }
388 crate::TemplateColumnMinSize::MinContent => {
389 vec![repeat(
390 template.repeat,
391 vec![minmax(min_content(), fr(1.0_f32))],
392 )]
393 }
394 crate::TemplateColumnMinSize::MaxContent => {
395 vec![repeat(
396 template.repeat,
397 vec![minmax(length(0.0_f32), max_content())],
398 )]
399 }
400 })
401 .unwrap_or_default()
402 }
403
404 taffy::style::Style {
405 display: self.display.into(),
406 overflow: self.overflow.into(),
407 scrollbar_width: self.scrollbar_width.to_taffy(rem_size, scale_factor),
408 position: self.position.into(),
409 inset: self.inset.to_taffy(rem_size, scale_factor),
410 size: self.size.to_taffy(rem_size, scale_factor),
411 min_size: self.min_size.to_taffy(rem_size, scale_factor),
412 max_size: self.max_size.to_taffy(rem_size, scale_factor),
413 aspect_ratio: self.aspect_ratio,
414 margin: self.margin.to_taffy(rem_size, scale_factor),
415 padding: self.padding.to_taffy(rem_size, scale_factor),
416 border: border_widths_to_taffy(&self.border_widths, rem_size, scale_factor),
417 align_items: self.align_items.map(|x| x.into()),
418 align_self: self.align_self.map(|x| x.into()),
419 align_content: self.align_content.map(|x| x.into()),
420 justify_content: self.justify_content.map(|x| x.into()),
421 gap: self.gap.to_taffy(rem_size, scale_factor),
422 flex_direction: self.flex_direction.into(),
423 flex_wrap: self.flex_wrap.into(),
424 flex_basis: self.flex_basis.to_taffy(rem_size, scale_factor),
425 flex_grow: self.flex_grow,
426 flex_shrink: self.flex_shrink,
427 grid_template_rows: to_grid_repeat(&self.grid_rows),
428 grid_template_columns: to_grid_repeat(&self.grid_cols),
429 grid_row: self
430 .grid_location
431 .as_ref()
432 .map(|location| to_grid_line(&location.row))
433 .unwrap_or_default(),
434 grid_column: self
435 .grid_location
436 .as_ref()
437 .map(|location| to_grid_line(&location.column))
438 .unwrap_or_default(),
439 ..Default::default()
440 }
441 }
442}
443
444impl ToTaffy<f32> for AbsoluteLength {
445 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> f32 {
446 round_to_device_pixel(self.to_pixels(rem_size).0, scale_factor)
447 }
448}
449
450impl ToTaffy<taffy::style::LengthPercentageAuto> for Length {
451 fn to_taffy(
452 &self,
453 rem_size: Pixels,
454 scale_factor: f32,
455 ) -> taffy::prelude::LengthPercentageAuto {
456 match self {
457 Length::Definite(length) => length.to_taffy(rem_size, scale_factor),
458 Length::Auto => taffy::prelude::LengthPercentageAuto::auto(),
459 }
460 }
461}
462
463impl ToTaffy<taffy::style::Dimension> for Length {
464 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::prelude::Dimension {
465 match self {
466 Length::Definite(length) => length.to_taffy(rem_size, scale_factor),
467 Length::Auto => taffy::prelude::Dimension::auto(),
468 }
469 }
470}
471
472impl ToTaffy<taffy::style::LengthPercentage> for DefiniteLength {
473 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage {
474 match self {
475 DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor),
476 DefiniteLength::Fraction(fraction) => {
477 taffy::style::LengthPercentage::percent(*fraction)
478 }
479 }
480 }
481}
482
483impl ToTaffy<taffy::style::LengthPercentageAuto> for DefiniteLength {
484 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto {
485 match self {
486 DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor),
487 DefiniteLength::Fraction(fraction) => {
488 taffy::style::LengthPercentageAuto::percent(*fraction)
489 }
490 }
491 }
492}
493
494impl ToTaffy<taffy::style::Dimension> for DefiniteLength {
495 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension {
496 match self {
497 DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor),
498 DefiniteLength::Fraction(fraction) => taffy::style::Dimension::percent(*fraction),
499 }
500 }
501}
502
503impl ToTaffy<taffy::style::LengthPercentage> for AbsoluteLength {
504 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage {
505 taffy::style::LengthPercentage::length(self.to_taffy(rem_size, scale_factor))
506 }
507}
508
509impl ToTaffy<taffy::style::LengthPercentageAuto> for AbsoluteLength {
510 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto {
511 taffy::style::LengthPercentageAuto::length(self.to_taffy(rem_size, scale_factor))
512 }
513}
514
515impl ToTaffy<taffy::style::Dimension> for AbsoluteLength {
516 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension {
517 taffy::style::Dimension::length(self.to_taffy(rem_size, scale_factor))
518 }
519}
520
521impl<T, T2> From<TaffyPoint<T>> for Point<T2>
522where
523 T: Into<T2>,
524 T2: Clone + Debug + Default + PartialEq,
525{
526 fn from(point: TaffyPoint<T>) -> Point<T2> {
527 Point {
528 x: point.x.into(),
529 y: point.y.into(),
530 }
531 }
532}
533
534impl<T, T2> From<Point<T>> for TaffyPoint<T2>
535where
536 T: Into<T2> + Clone + Debug + Default + PartialEq,
537{
538 fn from(val: Point<T>) -> Self {
539 TaffyPoint {
540 x: val.x.into(),
541 y: val.y.into(),
542 }
543 }
544}
545
546impl<T, U> ToTaffy<TaffySize<U>> for Size<T>
547where
548 T: ToTaffy<U> + Clone + Debug + Default + PartialEq,
549{
550 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffySize<U> {
551 TaffySize {
552 width: self.width.to_taffy(rem_size, scale_factor),
553 height: self.height.to_taffy(rem_size, scale_factor),
554 }
555 }
556}
557
558impl<T, U> ToTaffy<TaffyRect<U>> for Edges<T>
559where
560 T: ToTaffy<U> + Clone + Debug + Default + PartialEq,
561{
562 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffyRect<U> {
563 TaffyRect {
564 top: self.top.to_taffy(rem_size, scale_factor),
565 right: self.right.to_taffy(rem_size, scale_factor),
566 bottom: self.bottom.to_taffy(rem_size, scale_factor),
567 left: self.left.to_taffy(rem_size, scale_factor),
568 }
569 }
570}
571
572impl<T, U> From<TaffySize<T>> for Size<U>
573where
574 T: Into<U>,
575 U: Clone + Debug + Default + PartialEq,
576{
577 fn from(taffy_size: TaffySize<T>) -> Self {
578 Size {
579 width: taffy_size.width.into(),
580 height: taffy_size.height.into(),
581 }
582 }
583}
584
585impl<T, U> From<Size<T>> for TaffySize<U>
586where
587 T: Into<U> + Clone + Debug + Default + PartialEq,
588{
589 fn from(size: Size<T>) -> Self {
590 TaffySize {
591 width: size.width.into(),
592 height: size.height.into(),
593 }
594 }
595}
596
597#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
599pub enum AvailableSpace {
600 Definite(Pixels),
602 #[default]
604 MinContent,
605 MaxContent,
607}
608
609pub(crate) fn available_space_for_optional_width(width: Option<Pixels>) -> AvailableSpace {
610 width.map_or(AvailableSpace::MaxContent, AvailableSpace::Definite)
611}
612
613impl AvailableSpace {
614 pub const fn min_size() -> Size<Self> {
628 Size {
629 width: Self::MinContent,
630 height: Self::MinContent,
631 }
632 }
633}
634
635impl From<AvailableSpace> for TaffyAvailableSpace {
636 fn from(space: AvailableSpace) -> TaffyAvailableSpace {
637 match space {
638 AvailableSpace::Definite(Pixels(value)) => TaffyAvailableSpace::Definite(value),
639 AvailableSpace::MinContent => TaffyAvailableSpace::MinContent,
640 AvailableSpace::MaxContent => TaffyAvailableSpace::MaxContent,
641 }
642 }
643}
644
645impl From<TaffyAvailableSpace> for AvailableSpace {
646 fn from(space: TaffyAvailableSpace) -> AvailableSpace {
647 match space {
648 TaffyAvailableSpace::Definite(value) => AvailableSpace::Definite(Pixels(value)),
649 TaffyAvailableSpace::MinContent => AvailableSpace::MinContent,
650 TaffyAvailableSpace::MaxContent => AvailableSpace::MaxContent,
651 }
652 }
653}
654
655impl From<Pixels> for AvailableSpace {
656 fn from(pixels: Pixels) -> Self {
657 AvailableSpace::Definite(pixels)
658 }
659}
660
661impl From<Size<Pixels>> for Size<AvailableSpace> {
662 fn from(size: Size<Pixels>) -> Self {
663 Size {
664 width: AvailableSpace::Definite(size.width),
665 height: AvailableSpace::Definite(size.height),
666 }
667 }
668}
669
670#[cfg(test)]
671mod tests {
672 use super::*;
673 use crate::px;
674
675 #[test]
676 fn optional_width_uses_max_content_when_unbounded() {
677 assert_eq!(
678 available_space_for_optional_width(None),
679 AvailableSpace::MaxContent
680 );
681 assert_eq!(
682 available_space_for_optional_width(Some(px(320.0))),
683 AvailableSpace::Definite(px(320.0))
684 );
685 }
686}