1use crate::{
2 AbsoluteLength, App, Bounds, DefiniteLength, Edges, GridTemplate, Length, Pixels, Point, Size,
3 Style, Window, 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 std::{fmt::Debug, ops::Range};
11use taffy::{
12 TaffyTree, TraversePartialTree as _,
13 geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
14 prelude::{max_content, min_content},
15 style::AvailableSpace as TaffyAvailableSpace,
16 tree::NodeId,
17};
18
19#[cfg(feature = "stacker")]
20type StackSafe<T> = stacksafe::StackSafe<T>;
21#[cfg(not(feature = "stacker"))]
22type StackSafe<T> = T;
23
24type MeasureFn =
25 dyn FnMut(Size<Option<Pixels>>, Size<AvailableSpace>, &mut Window, &mut App) -> Size<Pixels>;
26type NodeMeasureFn = StackSafe<Box<MeasureFn>>;
27
28struct NodeContext {
29 measure: NodeMeasureFn,
30}
31pub struct TaffyLayoutEngine {
32 taffy: TaffyTree<NodeContext>,
33 absolute_layout_bounds: FxHashMap<LayoutId, Bounds<Pixels>>,
34 absolute_outer_origins: FxHashMap<LayoutId, Point<f32>>,
36 computed_layouts: FxHashSet<LayoutId>,
37 layout_bounds_scratch_space: Vec<LayoutId>,
38}
39
40const EXPECT_MESSAGE: &str = "we should avoid taffy layout errors by construction if possible";
41
42impl TaffyLayoutEngine {
43 pub fn new() -> Self {
44 let mut taffy = TaffyTree::new();
45 taffy.disable_rounding();
46 TaffyLayoutEngine {
47 taffy,
48 absolute_layout_bounds: FxHashMap::default(),
49 absolute_outer_origins: FxHashMap::default(),
50 computed_layouts: FxHashSet::default(),
51 layout_bounds_scratch_space: Vec::new(),
52 }
53 }
54
55 pub fn clear(&mut self) {
56 self.taffy.clear();
57 self.absolute_layout_bounds.clear();
58 self.absolute_outer_origins.clear();
59 self.computed_layouts.clear();
60 }
61
62 pub fn request_layout(
63 &mut self,
64 style: Style,
65 rem_size: Pixels,
66 scale_factor: f32,
67 children: &[LayoutId],
68 ) -> LayoutId {
69 let taffy_style = style.to_taffy(rem_size, scale_factor);
70
71 if children.is_empty() {
72 self.taffy
73 .new_leaf(taffy_style)
74 .expect(EXPECT_MESSAGE)
75 .into()
76 } else {
77 self.taffy
78 .new_with_children(taffy_style, LayoutId::to_taffy_slice(children))
80 .expect(EXPECT_MESSAGE)
81 .into()
82 }
83 }
84
85 pub fn request_measured_layout(
86 &mut self,
87 style: Style,
88 rem_size: Pixels,
89 scale_factor: f32,
90 measure: impl FnMut(
91 Size<Option<Pixels>>,
92 Size<AvailableSpace>,
93 &mut Window,
94 &mut App,
95 ) -> Size<Pixels>
96 + 'static,
97 ) -> LayoutId {
98 let taffy_style = style.to_taffy(rem_size, scale_factor);
99 let measure = Box::new(measure) as Box<MeasureFn>;
100 #[cfg(feature = "stacker")]
101 let measure = StackSafe::new(measure);
102
103 self.taffy
104 .new_leaf_with_context(taffy_style, NodeContext { measure })
105 .expect(EXPECT_MESSAGE)
106 .into()
107 }
108
109 pub fn stretch_auto_size_to_fill(
116 &mut self,
117 id: LayoutId,
118 size: Size<Pixels>,
119 scale_factor: f32,
120 ) {
121 let style = self.taffy.style(id.0).expect(EXPECT_MESSAGE);
122 let stretch_width = style.size.width.is_auto();
123 let stretch_height = style.size.height.is_auto();
124 if !stretch_width && !stretch_height {
125 return;
126 }
127 let mut style = style.clone();
128 if stretch_width {
129 style.size.width =
130 taffy::style::Dimension::length(round_to_device_pixel(size.width.0, scale_factor));
131 }
132 if stretch_height {
133 style.size.height =
134 taffy::style::Dimension::length(round_to_device_pixel(size.height.0, scale_factor));
135 }
136 self.taffy.set_style(id.0, style).expect(EXPECT_MESSAGE);
137 }
138
139 #[allow(dead_code)]
141 fn count_all_children(&self, parent: LayoutId) -> anyhow::Result<u32> {
142 let mut count = 0;
143
144 for child in self.taffy.children(parent.0)? {
145 count += 1;
147
148 count += self.count_all_children(LayoutId(child))?
150 }
151
152 Ok(count)
153 }
154
155 #[allow(dead_code)]
157 fn max_depth(&self, depth: u32, parent: LayoutId) -> anyhow::Result<u32> {
158 println!(
159 "{parent:?} at depth {depth} has {} children",
160 self.taffy.child_count(parent.0)
161 );
162
163 let mut max_child_depth = 0;
164
165 for child in self.taffy.children(parent.0)? {
166 max_child_depth = std::cmp::max(max_child_depth, self.max_depth(0, LayoutId(child))?);
167 }
168
169 Ok(depth + 1 + max_child_depth)
170 }
171
172 #[allow(dead_code)]
174 fn get_edges(&self, parent: LayoutId) -> anyhow::Result<Vec<(LayoutId, LayoutId)>> {
175 let mut edges = Vec::new();
176
177 for child in self.taffy.children(parent.0)? {
178 edges.push((parent, LayoutId(child)));
179
180 edges.extend(self.get_edges(LayoutId(child))?);
181 }
182
183 Ok(edges)
184 }
185
186 #[cfg_attr(feature = "stacker", stacksafe::stacksafe)]
187 pub fn compute_layout(
188 &mut self,
189 id: LayoutId,
190 available_space: Size<AvailableSpace>,
191 window: &mut Window,
192 cx: &mut App,
193 ) {
194 if !self.computed_layouts.insert(id) {
206 let stack = &mut self.layout_bounds_scratch_space;
207 stack.push(id);
208 while let Some(id) = stack.pop() {
209 self.absolute_layout_bounds.remove(&id);
210 self.absolute_outer_origins.remove(&id);
211 stack.extend(
212 self.taffy
213 .children(id.into())
214 .expect(EXPECT_MESSAGE)
215 .into_iter()
216 .map(LayoutId::from),
217 );
218 }
219 }
220
221 let scale_factor = window.scale_factor();
222
223 let transform = |v: AvailableSpace| match v {
224 AvailableSpace::Definite(pixels) => {
225 AvailableSpace::Definite(Pixels(pixels.0 * scale_factor))
226 }
227 AvailableSpace::MinContent => AvailableSpace::MinContent,
228 AvailableSpace::MaxContent => AvailableSpace::MaxContent,
229 };
230 let available_space = size(
231 transform(available_space.width),
232 transform(available_space.height),
233 );
234
235 self.taffy
236 .compute_layout_with_measure(
237 id.into(),
238 available_space.into(),
239 |known_dimensions, available_space, _id, node_context, _style| {
240 let Some(node_context) = node_context else {
241 return taffy::geometry::Size::default();
242 };
243
244 let known_dimensions = Size {
245 width: known_dimensions.width.map(|e| Pixels(e / scale_factor)),
246 height: known_dimensions.height.map(|e| Pixels(e / scale_factor)),
247 };
248
249 let available_space: Size<AvailableSpace> = available_space.into();
250 let untransform = |ev: AvailableSpace| match ev {
251 AvailableSpace::Definite(pixels) => {
252 AvailableSpace::Definite(Pixels(pixels.0 / scale_factor))
253 }
254 AvailableSpace::MinContent => AvailableSpace::MinContent,
255 AvailableSpace::MaxContent => AvailableSpace::MaxContent,
256 };
257 let available_space = size(
258 untransform(available_space.width),
259 untransform(available_space.height),
260 );
261
262 let measured_size: Size<Pixels> =
263 (node_context.measure)(known_dimensions, available_space, window, cx);
264 snap_measured_size_to_device_pixels(measured_size, scale_factor).into()
265 },
266 )
267 .expect(EXPECT_MESSAGE);
268 }
269
270 pub fn layout_bounds(&mut self, id: LayoutId, scale_factor: f32) -> Bounds<Pixels> {
346 if let Some(layout) = self.absolute_layout_bounds.get(&id).cloned() {
347 return layout;
348 }
349
350 let layout = self.taffy.layout(id.into()).expect(EXPECT_MESSAGE);
351 let layout_location = layout.location;
352 let layout_size = layout.size;
353 let parent = self.taffy.parent(id.0);
354
355 let absolute_outer_origin = match parent {
356 Some(parent_id) => {
357 let parent_id = LayoutId::from(parent_id);
358 self.layout_bounds(parent_id, scale_factor);
359 let parent_origin = *self
360 .absolute_outer_origins
361 .get(&parent_id)
362 .expect("parent absolute outer origin should be cached");
363 parent_origin + Point::from(layout_location)
364 }
365 None => Point::from(layout_location),
366 };
367 self.absolute_outer_origins
368 .insert(id, absolute_outer_origin);
369
370 let absolute_far = absolute_outer_origin + Point::from(Size::from(layout_size));
371 let snapped_bounds = Bounds::from_corners(
372 absolute_outer_origin.map(round_half_toward_zero),
373 absolute_far.map(round_half_toward_zero),
374 );
375
376 let bounds = (snapped_bounds / scale_factor).map(Pixels);
377 self.absolute_layout_bounds.insert(id, bounds);
378 bounds
379 }
380}
381
382#[derive(Copy, Clone, Eq, PartialEq, Debug)]
384#[repr(transparent)]
385pub struct LayoutId(NodeId);
386
387impl LayoutId {
388 fn to_taffy_slice(node_ids: &[Self]) -> &[taffy::NodeId] {
389 unsafe { std::mem::transmute::<&[LayoutId], &[taffy::NodeId]>(node_ids) }
391 }
392}
393
394impl std::hash::Hash for LayoutId {
395 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
396 u64::from(self.0).hash(state);
397 }
398}
399
400impl From<NodeId> for LayoutId {
401 fn from(node_id: NodeId) -> Self {
402 Self(node_id)
403 }
404}
405
406impl From<LayoutId> for NodeId {
407 fn from(layout_id: LayoutId) -> NodeId {
408 layout_id.0
409 }
410}
411
412fn snap_measured_size_to_device_pixels(size: Size<Pixels>, scale_factor: f32) -> Size<f32> {
413 size.map(|d| ceil_to_device_pixel(d.0.max(0.0), scale_factor))
414}
415
416fn border_widths_to_taffy(
417 widths: &Edges<AbsoluteLength>,
418 rem_size: Pixels,
419 scale_factor: f32,
420) -> TaffyRect<taffy::style::LengthPercentage> {
421 let snap = |w: &AbsoluteLength| {
422 taffy::style::LengthPercentage::length(round_stroke_to_device_pixel(
423 w.to_pixels(rem_size).0,
424 scale_factor,
425 ))
426 };
427 TaffyRect {
428 top: snap(&widths.top),
429 right: snap(&widths.right),
430 bottom: snap(&widths.bottom),
431 left: snap(&widths.left),
432 }
433}
434
435trait ToTaffy<Output> {
436 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> Output;
437}
438
439impl ToTaffy<taffy::style::Style> for Style {
440 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Style {
441 use taffy::style_helpers::{fr, length, minmax, repeat};
442
443 fn to_grid_line(
444 placement: &Range<crate::GridPlacement>,
445 ) -> taffy::Line<taffy::GridPlacement> {
446 taffy::Line {
447 start: placement.start.into(),
448 end: placement.end.into(),
449 }
450 }
451
452 fn to_grid_repeat<T: taffy::style::CheapCloneStr>(
453 unit: &Option<GridTemplate>,
454 ) -> Vec<taffy::GridTemplateComponent<T>> {
455 unit.map(|template| {
456 match template.min_size {
457 crate::GridTemplateMinSize::Zero => {
459 vec![repeat(
460 template.repeat,
461 vec![minmax(length(0.0_f32), fr(1.0_f32))],
462 )]
463 }
464 crate::GridTemplateMinSize::MinContent => {
466 vec![repeat(
467 template.repeat,
468 vec![minmax(min_content(), fr(1.0_f32))],
469 )]
470 }
471 crate::GridTemplateMinSize::MaxContent => {
473 vec![repeat(
474 template.repeat,
475 vec![minmax(length(0.0_f32), max_content())],
476 )]
477 }
478 }
479 })
480 .unwrap_or_default()
481 }
482
483 taffy::style::Style {
484 display: self.display.into(),
485 overflow: self.overflow.into(),
486 scrollbar_width: self.scrollbar_width.to_taffy(rem_size, scale_factor),
487 position: self.position.into(),
488 inset: self.inset.to_taffy(rem_size, scale_factor),
489 size: self.size.to_taffy(rem_size, scale_factor),
490 min_size: self.min_size.to_taffy(rem_size, scale_factor),
491 max_size: self.max_size.to_taffy(rem_size, scale_factor),
492 aspect_ratio: self.aspect_ratio,
493 margin: self.margin.to_taffy(rem_size, scale_factor),
494 padding: self.padding.to_taffy(rem_size, scale_factor),
495 border: border_widths_to_taffy(&self.border_widths, rem_size, scale_factor),
496 align_items: self.align_items.map(|x| x.into()),
497 align_self: self.align_self.map(|x| x.into()),
498 align_content: self.align_content.map(|x| x.into()),
499 justify_content: self.justify_content.map(|x| x.into()),
500 gap: self.gap.to_taffy(rem_size, scale_factor),
501 flex_direction: self.flex_direction.into(),
502 flex_wrap: self.flex_wrap.into(),
503 flex_basis: self.flex_basis.to_taffy(rem_size, scale_factor),
504 flex_grow: self.flex_grow,
505 flex_shrink: self.flex_shrink,
506 grid_template_rows: to_grid_repeat(&self.grid_rows),
507 grid_template_columns: to_grid_repeat(&self.grid_cols),
508 grid_row: self
509 .grid_location
510 .as_ref()
511 .map(|location| to_grid_line(&location.row))
512 .unwrap_or_default(),
513 grid_column: self
514 .grid_location
515 .as_ref()
516 .map(|location| to_grid_line(&location.column))
517 .unwrap_or_default(),
518 ..Default::default()
519 }
520 }
521}
522
523impl ToTaffy<f32> for AbsoluteLength {
524 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> f32 {
525 round_to_device_pixel(self.to_pixels(rem_size).0, scale_factor)
526 }
527}
528
529impl ToTaffy<taffy::style::LengthPercentageAuto> for Length {
530 fn to_taffy(
531 &self,
532 rem_size: Pixels,
533 scale_factor: f32,
534 ) -> taffy::prelude::LengthPercentageAuto {
535 match self {
536 Length::Definite(length) => length.to_taffy(rem_size, scale_factor),
537 Length::Auto => taffy::prelude::LengthPercentageAuto::auto(),
538 }
539 }
540}
541
542impl ToTaffy<taffy::style::Dimension> for Length {
543 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::prelude::Dimension {
544 match self {
545 Length::Definite(length) => length.to_taffy(rem_size, scale_factor),
546 Length::Auto => taffy::prelude::Dimension::auto(),
547 }
548 }
549}
550
551impl ToTaffy<taffy::style::LengthPercentage> for DefiniteLength {
552 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage {
553 match self {
554 DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor),
555 DefiniteLength::Fraction(fraction) => {
556 taffy::style::LengthPercentage::percent(*fraction)
557 }
558 }
559 }
560}
561
562impl ToTaffy<taffy::style::LengthPercentageAuto> for DefiniteLength {
563 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto {
564 match self {
565 DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor),
566 DefiniteLength::Fraction(fraction) => {
567 taffy::style::LengthPercentageAuto::percent(*fraction)
568 }
569 }
570 }
571}
572
573impl ToTaffy<taffy::style::Dimension> for DefiniteLength {
574 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension {
575 match self {
576 DefiniteLength::Absolute(length) => length.to_taffy(rem_size, scale_factor),
577 DefiniteLength::Fraction(fraction) => taffy::style::Dimension::percent(*fraction),
578 }
579 }
580}
581
582impl ToTaffy<taffy::style::LengthPercentage> for AbsoluteLength {
583 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage {
584 taffy::style::LengthPercentage::length(self.to_taffy(rem_size, scale_factor))
585 }
586}
587
588impl ToTaffy<taffy::style::LengthPercentageAuto> for AbsoluteLength {
589 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto {
590 taffy::style::LengthPercentageAuto::length(self.to_taffy(rem_size, scale_factor))
591 }
592}
593
594impl ToTaffy<taffy::style::Dimension> for AbsoluteLength {
595 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension {
596 taffy::style::Dimension::length(self.to_taffy(rem_size, scale_factor))
597 }
598}
599
600impl<T, T2> From<TaffyPoint<T>> for Point<T2>
601where
602 T: Into<T2>,
603 T2: Clone + Debug + Default + PartialEq,
604{
605 fn from(point: TaffyPoint<T>) -> Point<T2> {
606 Point {
607 x: point.x.into(),
608 y: point.y.into(),
609 }
610 }
611}
612
613impl<T, T2> From<Point<T>> for TaffyPoint<T2>
614where
615 T: Into<T2> + Clone + Debug + Default + PartialEq,
616{
617 fn from(val: Point<T>) -> Self {
618 TaffyPoint {
619 x: val.x.into(),
620 y: val.y.into(),
621 }
622 }
623}
624
625impl<T, U> ToTaffy<TaffySize<U>> for Size<T>
626where
627 T: ToTaffy<U> + Clone + Debug + Default + PartialEq,
628{
629 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffySize<U> {
630 TaffySize {
631 width: self.width.to_taffy(rem_size, scale_factor),
632 height: self.height.to_taffy(rem_size, scale_factor),
633 }
634 }
635}
636
637impl<T, U> ToTaffy<TaffyRect<U>> for Edges<T>
638where
639 T: ToTaffy<U> + Clone + Debug + Default + PartialEq,
640{
641 fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffyRect<U> {
642 TaffyRect {
643 top: self.top.to_taffy(rem_size, scale_factor),
644 right: self.right.to_taffy(rem_size, scale_factor),
645 bottom: self.bottom.to_taffy(rem_size, scale_factor),
646 left: self.left.to_taffy(rem_size, scale_factor),
647 }
648 }
649}
650
651impl<T, U> From<TaffySize<T>> for Size<U>
652where
653 T: Into<U>,
654 U: Clone + Debug + Default + PartialEq,
655{
656 fn from(taffy_size: TaffySize<T>) -> Self {
657 Size {
658 width: taffy_size.width.into(),
659 height: taffy_size.height.into(),
660 }
661 }
662}
663
664impl<T, U> From<Size<T>> for TaffySize<U>
665where
666 T: Into<U> + Clone + Debug + Default + PartialEq,
667{
668 fn from(size: Size<T>) -> Self {
669 TaffySize {
670 width: size.width.into(),
671 height: size.height.into(),
672 }
673 }
674}
675
676#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
678pub enum AvailableSpace {
679 Definite(Pixels),
681 #[default]
683 MinContent,
684 MaxContent,
686}
687
688impl AvailableSpace {
689 pub const fn min_size() -> Size<Self> {
703 Size {
704 width: Self::MinContent,
705 height: Self::MinContent,
706 }
707 }
708}
709
710impl From<AvailableSpace> for TaffyAvailableSpace {
711 fn from(space: AvailableSpace) -> TaffyAvailableSpace {
712 match space {
713 AvailableSpace::Definite(Pixels(value)) => TaffyAvailableSpace::Definite(value),
714 AvailableSpace::MinContent => TaffyAvailableSpace::MinContent,
715 AvailableSpace::MaxContent => TaffyAvailableSpace::MaxContent,
716 }
717 }
718}
719
720impl From<TaffyAvailableSpace> for AvailableSpace {
721 fn from(space: TaffyAvailableSpace) -> AvailableSpace {
722 match space {
723 TaffyAvailableSpace::Definite(value) => AvailableSpace::Definite(Pixels(value)),
724 TaffyAvailableSpace::MinContent => AvailableSpace::MinContent,
725 TaffyAvailableSpace::MaxContent => AvailableSpace::MaxContent,
726 }
727 }
728}
729
730impl From<Pixels> for AvailableSpace {
731 fn from(pixels: Pixels) -> Self {
732 AvailableSpace::Definite(pixels)
733 }
734}
735
736impl From<Size<Pixels>> for Size<AvailableSpace> {
737 fn from(size: Size<Pixels>) -> Self {
738 Size {
739 width: AvailableSpace::Definite(size.width),
740 height: AvailableSpace::Definite(size.height),
741 }
742 }
743}
744
745#[cfg(test)]
746mod tests {
747 use super::*;
748
749 #[test]
750 fn border_widths_to_taffy_use_stroke_snapping() {
751 let border_widths = Edges {
752 top: Pixels(0.0).into(),
753 right: Pixels(0.4).into(),
754 bottom: Pixels(0.5).into(),
755 left: Pixels(1.6).into(),
756 };
757 let taffy_border = border_widths_to_taffy(&border_widths, Pixels(16.0), 1.0);
758
759 assert_eq!(
760 taffy_border.top,
761 taffy::style::LengthPercentage::length(0.0)
762 );
763 assert_eq!(
764 taffy_border.right,
765 taffy::style::LengthPercentage::length(1.0)
766 );
767 assert_eq!(
768 taffy_border.bottom,
769 taffy::style::LengthPercentage::length(1.0)
770 );
771 assert_eq!(
772 taffy_border.left,
773 taffy::style::LengthPercentage::length(2.0)
774 );
775 }
776}