1use crate::{
  2    AbsoluteLength, App, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style, Window,
  3    point, size,
  4};
  5use collections::{FxHashMap, FxHashSet};
  6use stacksafe::{StackSafe, stacksafe};
  7use std::{fmt::Debug, ops::Range};
  8use taffy::{
  9    TaffyTree, TraversePartialTree as _,
 10    geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
 11    style::AvailableSpace as TaffyAvailableSpace,
 12    tree::NodeId,
 13};
 14
 15type NodeMeasureFn = StackSafe<
 16    Box<
 17        dyn FnMut(
 18            Size<Option<Pixels>>,
 19            Size<AvailableSpace>,
 20            &mut Window,
 21            &mut App,
 22        ) -> Size<Pixels>,
 23    >,
 24>;
 25
 26struct NodeContext {
 27    measure: NodeMeasureFn,
 28}
 29pub struct TaffyLayoutEngine {
 30    taffy: TaffyTree<NodeContext>,
 31    absolute_layout_bounds: FxHashMap<LayoutId, Bounds<Pixels>>,
 32    computed_layouts: FxHashSet<LayoutId>,
 33    layout_bounds_scratch_space: Vec<LayoutId>,
 34}
 35
 36const EXPECT_MESSAGE: &str = "we should avoid taffy layout errors by construction if possible";
 37
 38impl TaffyLayoutEngine {
 39    pub fn new() -> Self {
 40        let mut taffy = TaffyTree::new();
 41        taffy.enable_rounding();
 42        TaffyLayoutEngine {
 43            taffy,
 44            absolute_layout_bounds: FxHashMap::default(),
 45            computed_layouts: FxHashSet::default(),
 46            layout_bounds_scratch_space: Vec::new(),
 47        }
 48    }
 49
 50    pub fn clear(&mut self) {
 51        self.taffy.clear();
 52        self.absolute_layout_bounds.clear();
 53        self.computed_layouts.clear();
 54    }
 55
 56    pub fn request_layout(
 57        &mut self,
 58        style: Style,
 59        rem_size: Pixels,
 60        scale_factor: f32,
 61        children: &[LayoutId],
 62    ) -> LayoutId {
 63        let taffy_style = style.to_taffy(rem_size, scale_factor);
 64
 65        if children.is_empty() {
 66            self.taffy
 67                .new_leaf(taffy_style)
 68                .expect(EXPECT_MESSAGE)
 69                .into()
 70        } else {
 71            self.taffy
 72                // This is safe because LayoutId is repr(transparent) to taffy::tree::NodeId.
 73                .new_with_children(taffy_style, LayoutId::to_taffy_slice(children))
 74                .expect(EXPECT_MESSAGE)
 75                .into()
 76        }
 77    }
 78
 79    pub fn request_measured_layout(
 80        &mut self,
 81        style: Style,
 82        rem_size: Pixels,
 83        scale_factor: f32,
 84        measure: impl FnMut(
 85            Size<Option<Pixels>>,
 86            Size<AvailableSpace>,
 87            &mut Window,
 88            &mut App,
 89        ) -> Size<Pixels>
 90        + 'static,
 91    ) -> LayoutId {
 92        let taffy_style = style.to_taffy(rem_size, scale_factor);
 93
 94        self.taffy
 95            .new_leaf_with_context(
 96                taffy_style,
 97                NodeContext {
 98                    measure: StackSafe::new(Box::new(measure)),
 99                },
100            )
101            .expect(EXPECT_MESSAGE)
102            .into()
103    }
104
105    // Used to understand performance
106    #[allow(dead_code)]
107    fn count_all_children(&self, parent: LayoutId) -> anyhow::Result<u32> {
108        let mut count = 0;
109
110        for child in self.taffy.children(parent.0)? {
111            // Count this child.
112            count += 1;
113
114            // Count all of this child's children.
115            count += self.count_all_children(LayoutId(child))?
116        }
117
118        Ok(count)
119    }
120
121    // Used to understand performance
122    #[allow(dead_code)]
123    fn max_depth(&self, depth: u32, parent: LayoutId) -> anyhow::Result<u32> {
124        println!(
125            "{parent:?} at depth {depth} has {} children",
126            self.taffy.child_count(parent.0)
127        );
128
129        let mut max_child_depth = 0;
130
131        for child in self.taffy.children(parent.0)? {
132            max_child_depth = std::cmp::max(max_child_depth, self.max_depth(0, LayoutId(child))?);
133        }
134
135        Ok(depth + 1 + max_child_depth)
136    }
137
138    // Used to understand performance
139    #[allow(dead_code)]
140    fn get_edges(&self, parent: LayoutId) -> anyhow::Result<Vec<(LayoutId, LayoutId)>> {
141        let mut edges = Vec::new();
142
143        for child in self.taffy.children(parent.0)? {
144            edges.push((parent, LayoutId(child)));
145
146            edges.extend(self.get_edges(LayoutId(child))?);
147        }
148
149        Ok(edges)
150    }
151
152    #[stacksafe]
153    pub fn compute_layout(
154        &mut self,
155        id: LayoutId,
156        available_space: Size<AvailableSpace>,
157        window: &mut Window,
158        cx: &mut App,
159    ) {
160        // Leaving this here until we have a better instrumentation approach.
161        // println!("Laying out {} children", self.count_all_children(id)?);
162        // println!("Max layout depth: {}", self.max_depth(0, id)?);
163
164        // Output the edges (branches) of the tree in Mermaid format for visualization.
165        // println!("Edges:");
166        // for (a, b) in self.get_edges(id)? {
167        //     println!("N{} --> N{}", u64::from(a), u64::from(b));
168        // }
169        //
170
171        if !self.computed_layouts.insert(id) {
172            let mut stack = &mut self.layout_bounds_scratch_space;
173            stack.push(id);
174            while let Some(id) = stack.pop() {
175                self.absolute_layout_bounds.remove(&id);
176                stack.extend(
177                    self.taffy
178                        .children(id.into())
179                        .expect(EXPECT_MESSAGE)
180                        .into_iter()
181                        .map(LayoutId::from),
182                );
183            }
184        }
185
186        let scale_factor = window.scale_factor();
187
188        let transform = |v: AvailableSpace| match v {
189            AvailableSpace::Definite(pixels) => {
190                AvailableSpace::Definite(Pixels(pixels.0 * scale_factor))
191            }
192            AvailableSpace::MinContent => AvailableSpace::MinContent,
193            AvailableSpace::MaxContent => AvailableSpace::MaxContent,
194        };
195        let available_space = size(
196            transform(available_space.width),
197            transform(available_space.height),
198        );
199
200        self.taffy
201            .compute_layout_with_measure(
202                id.into(),
203                available_space.into(),
204                |known_dimensions, available_space, _id, node_context, _style| {
205                    let Some(node_context) = node_context else {
206                        return taffy::geometry::Size::default();
207                    };
208
209                    let known_dimensions = Size {
210                        width: known_dimensions.width.map(|e| Pixels(e / scale_factor)),
211                        height: known_dimensions.height.map(|e| Pixels(e / scale_factor)),
212                    };
213
214                    let available_space: Size<AvailableSpace> = available_space.into();
215                    let untransform = |ev: AvailableSpace| match ev {
216                        AvailableSpace::Definite(pixels) => {
217                            AvailableSpace::Definite(Pixels(pixels.0 / scale_factor))
218                        }
219                        AvailableSpace::MinContent => AvailableSpace::MinContent,
220                        AvailableSpace::MaxContent => AvailableSpace::MaxContent,
221                    };
222                    let available_space = size(
223                        untransform(available_space.width),
224                        untransform(available_space.height),
225                    );
226
227                    let a: Size<Pixels> =
228                        (node_context.measure)(known_dimensions, available_space, window, cx);
229                    size(a.width.0 * scale_factor, a.height.0 * scale_factor).into()
230                },
231            )
232            .expect(EXPECT_MESSAGE);
233    }
234
235    pub fn layout_bounds(&mut self, id: LayoutId, scale_factor: f32) -> Bounds<Pixels> {
236        if let Some(layout) = self.absolute_layout_bounds.get(&id).cloned() {
237            return layout;
238        }
239
240        let layout = self.taffy.layout(id.into()).expect(EXPECT_MESSAGE);
241        let mut bounds = Bounds {
242            origin: point(
243                Pixels(layout.location.x / scale_factor),
244                Pixels(layout.location.y / scale_factor),
245            ),
246            size: size(
247                Pixels(layout.size.width / scale_factor),
248                Pixels(layout.size.height / scale_factor),
249            ),
250        };
251
252        if let Some(parent_id) = self.taffy.parent(id.0) {
253            let parent_bounds = self.layout_bounds(parent_id.into(), scale_factor);
254            bounds.origin += parent_bounds.origin;
255        }
256        self.absolute_layout_bounds.insert(id, bounds);
257
258        bounds
259    }
260}
261
262/// A unique identifier for a layout node, generated when requesting a layout from Taffy
263#[derive(Copy, Clone, Eq, PartialEq, Debug)]
264#[repr(transparent)]
265pub struct LayoutId(NodeId);
266
267impl LayoutId {
268    fn to_taffy_slice(node_ids: &[Self]) -> &[taffy::NodeId] {
269        // SAFETY: LayoutId is repr(transparent) to taffy::tree::NodeId.
270        unsafe { std::mem::transmute::<&[LayoutId], &[taffy::NodeId]>(node_ids) }
271    }
272}
273
274impl std::hash::Hash for LayoutId {
275    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
276        u64::from(self.0).hash(state);
277    }
278}
279
280impl From<NodeId> for LayoutId {
281    fn from(node_id: NodeId) -> Self {
282        Self(node_id)
283    }
284}
285
286impl From<LayoutId> for NodeId {
287    fn from(layout_id: LayoutId) -> NodeId {
288        layout_id.0
289    }
290}
291
292trait ToTaffy<Output> {
293    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> Output;
294}
295
296impl ToTaffy<taffy::style::Style> for Style {
297    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Style {
298        use taffy::style_helpers::{fr, length, minmax, repeat};
299
300        fn to_grid_line(
301            placement: &Range<crate::GridPlacement>,
302        ) -> taffy::Line<taffy::GridPlacement> {
303            taffy::Line {
304                start: placement.start.into(),
305                end: placement.end.into(),
306            }
307        }
308
309        fn to_grid_repeat<T: taffy::style::CheapCloneStr>(
310            unit: &Option<u16>,
311        ) -> Vec<taffy::GridTemplateComponent<T>> {
312            // grid-template-columns: repeat(<number>, minmax(0, 1fr));
313            unit.map(|count| vec![repeat(count, vec![minmax(length(0.0), fr(1.0))])])
314                .unwrap_or_default()
315        }
316
317        taffy::style::Style {
318            display: self.display.into(),
319            overflow: self.overflow.into(),
320            scrollbar_width: self.scrollbar_width.to_taffy(rem_size, scale_factor),
321            position: self.position.into(),
322            inset: self.inset.to_taffy(rem_size, scale_factor),
323            size: self.size.to_taffy(rem_size, scale_factor),
324            min_size: self.min_size.to_taffy(rem_size, scale_factor),
325            max_size: self.max_size.to_taffy(rem_size, scale_factor),
326            aspect_ratio: self.aspect_ratio,
327            margin: self.margin.to_taffy(rem_size, scale_factor),
328            padding: self.padding.to_taffy(rem_size, scale_factor),
329            border: self.border_widths.to_taffy(rem_size, scale_factor),
330            align_items: self.align_items.map(|x| x.into()),
331            align_self: self.align_self.map(|x| x.into()),
332            align_content: self.align_content.map(|x| x.into()),
333            justify_content: self.justify_content.map(|x| x.into()),
334            gap: self.gap.to_taffy(rem_size, scale_factor),
335            flex_direction: self.flex_direction.into(),
336            flex_wrap: self.flex_wrap.into(),
337            flex_basis: self.flex_basis.to_taffy(rem_size, scale_factor),
338            flex_grow: self.flex_grow,
339            flex_shrink: self.flex_shrink,
340            grid_template_rows: to_grid_repeat(&self.grid_rows),
341            grid_template_columns: to_grid_repeat(&self.grid_cols),
342            grid_row: self
343                .grid_location
344                .as_ref()
345                .map(|location| to_grid_line(&location.row))
346                .unwrap_or_default(),
347            grid_column: self
348                .grid_location
349                .as_ref()
350                .map(|location| to_grid_line(&location.column))
351                .unwrap_or_default(),
352            ..Default::default()
353        }
354    }
355}
356
357impl ToTaffy<f32> for AbsoluteLength {
358    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> f32 {
359        match self {
360            AbsoluteLength::Pixels(pixels) => {
361                let pixels: f32 = pixels.into();
362                pixels * scale_factor
363            }
364            AbsoluteLength::Rems(rems) => {
365                let pixels: f32 = (*rems * rem_size).into();
366                pixels * scale_factor
367            }
368        }
369    }
370}
371
372impl ToTaffy<taffy::style::LengthPercentageAuto> for Length {
373    fn to_taffy(
374        &self,
375        rem_size: Pixels,
376        scale_factor: f32,
377    ) -> taffy::prelude::LengthPercentageAuto {
378        match self {
379            Length::Definite(length) => length.to_taffy(rem_size, scale_factor),
380            Length::Auto => taffy::prelude::LengthPercentageAuto::auto(),
381        }
382    }
383}
384
385impl ToTaffy<taffy::style::Dimension> for Length {
386    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::prelude::Dimension {
387        match self {
388            Length::Definite(length) => length.to_taffy(rem_size, scale_factor),
389            Length::Auto => taffy::prelude::Dimension::auto(),
390        }
391    }
392}
393
394impl ToTaffy<taffy::style::LengthPercentage> for DefiniteLength {
395    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage {
396        match self {
397            DefiniteLength::Absolute(length) => match length {
398                AbsoluteLength::Pixels(pixels) => {
399                    let pixels: f32 = pixels.into();
400                    taffy::style::LengthPercentage::length(pixels * scale_factor)
401                }
402                AbsoluteLength::Rems(rems) => {
403                    let pixels: f32 = (*rems * rem_size).into();
404                    taffy::style::LengthPercentage::length(pixels * scale_factor)
405                }
406            },
407            DefiniteLength::Fraction(fraction) => {
408                taffy::style::LengthPercentage::percent(*fraction)
409            }
410        }
411    }
412}
413
414impl ToTaffy<taffy::style::LengthPercentageAuto> for DefiniteLength {
415    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentageAuto {
416        match self {
417            DefiniteLength::Absolute(length) => match length {
418                AbsoluteLength::Pixels(pixels) => {
419                    let pixels: f32 = pixels.into();
420                    taffy::style::LengthPercentageAuto::length(pixels * scale_factor)
421                }
422                AbsoluteLength::Rems(rems) => {
423                    let pixels: f32 = (*rems * rem_size).into();
424                    taffy::style::LengthPercentageAuto::length(pixels * scale_factor)
425                }
426            },
427            DefiniteLength::Fraction(fraction) => {
428                taffy::style::LengthPercentageAuto::percent(*fraction)
429            }
430        }
431    }
432}
433
434impl ToTaffy<taffy::style::Dimension> for DefiniteLength {
435    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::Dimension {
436        match self {
437            DefiniteLength::Absolute(length) => match length {
438                AbsoluteLength::Pixels(pixels) => {
439                    let pixels: f32 = pixels.into();
440                    taffy::style::Dimension::length(pixels * scale_factor)
441                }
442                AbsoluteLength::Rems(rems) => {
443                    taffy::style::Dimension::length((*rems * rem_size * scale_factor).into())
444                }
445            },
446            DefiniteLength::Fraction(fraction) => taffy::style::Dimension::percent(*fraction),
447        }
448    }
449}
450
451impl ToTaffy<taffy::style::LengthPercentage> for AbsoluteLength {
452    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> taffy::style::LengthPercentage {
453        match self {
454            AbsoluteLength::Pixels(pixels) => {
455                let pixels: f32 = pixels.into();
456                taffy::style::LengthPercentage::length(pixels * scale_factor)
457            }
458            AbsoluteLength::Rems(rems) => {
459                let pixels: f32 = (*rems * rem_size).into();
460                taffy::style::LengthPercentage::length(pixels * scale_factor)
461            }
462        }
463    }
464}
465
466impl<T, T2> From<TaffyPoint<T>> for Point<T2>
467where
468    T: Into<T2>,
469    T2: Clone + Debug + Default + PartialEq,
470{
471    fn from(point: TaffyPoint<T>) -> Point<T2> {
472        Point {
473            x: point.x.into(),
474            y: point.y.into(),
475        }
476    }
477}
478
479impl<T, T2> From<Point<T>> for TaffyPoint<T2>
480where
481    T: Into<T2> + Clone + Debug + Default + PartialEq,
482{
483    fn from(val: Point<T>) -> Self {
484        TaffyPoint {
485            x: val.x.into(),
486            y: val.y.into(),
487        }
488    }
489}
490
491impl<T, U> ToTaffy<TaffySize<U>> for Size<T>
492where
493    T: ToTaffy<U> + Clone + Debug + Default + PartialEq,
494{
495    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffySize<U> {
496        TaffySize {
497            width: self.width.to_taffy(rem_size, scale_factor),
498            height: self.height.to_taffy(rem_size, scale_factor),
499        }
500    }
501}
502
503impl<T, U> ToTaffy<TaffyRect<U>> for Edges<T>
504where
505    T: ToTaffy<U> + Clone + Debug + Default + PartialEq,
506{
507    fn to_taffy(&self, rem_size: Pixels, scale_factor: f32) -> TaffyRect<U> {
508        TaffyRect {
509            top: self.top.to_taffy(rem_size, scale_factor),
510            right: self.right.to_taffy(rem_size, scale_factor),
511            bottom: self.bottom.to_taffy(rem_size, scale_factor),
512            left: self.left.to_taffy(rem_size, scale_factor),
513        }
514    }
515}
516
517impl<T, U> From<TaffySize<T>> for Size<U>
518where
519    T: Into<U>,
520    U: Clone + Debug + Default + PartialEq,
521{
522    fn from(taffy_size: TaffySize<T>) -> Self {
523        Size {
524            width: taffy_size.width.into(),
525            height: taffy_size.height.into(),
526        }
527    }
528}
529
530impl<T, U> From<Size<T>> for TaffySize<U>
531where
532    T: Into<U> + Clone + Debug + Default + PartialEq,
533{
534    fn from(size: Size<T>) -> Self {
535        TaffySize {
536            width: size.width.into(),
537            height: size.height.into(),
538        }
539    }
540}
541
542/// The space available for an element to be laid out in
543#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
544pub enum AvailableSpace {
545    /// The amount of space available is the specified number of pixels
546    Definite(Pixels),
547    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
548    #[default]
549    MinContent,
550    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
551    MaxContent,
552}
553
554impl AvailableSpace {
555    /// Returns a `Size` with both width and height set to `AvailableSpace::MinContent`.
556    ///
557    /// This function is useful when you want to create a `Size` with the minimum content constraints
558    /// for both dimensions.
559    ///
560    /// # Examples
561    ///
562    /// ```
563    /// use gpui::AvailableSpace;
564    /// let min_content_size = AvailableSpace::min_size();
565    /// assert_eq!(min_content_size.width, AvailableSpace::MinContent);
566    /// assert_eq!(min_content_size.height, AvailableSpace::MinContent);
567    /// ```
568    pub const fn min_size() -> Size<Self> {
569        Size {
570            width: Self::MinContent,
571            height: Self::MinContent,
572        }
573    }
574}
575
576impl From<AvailableSpace> for TaffyAvailableSpace {
577    fn from(space: AvailableSpace) -> TaffyAvailableSpace {
578        match space {
579            AvailableSpace::Definite(Pixels(value)) => TaffyAvailableSpace::Definite(value),
580            AvailableSpace::MinContent => TaffyAvailableSpace::MinContent,
581            AvailableSpace::MaxContent => TaffyAvailableSpace::MaxContent,
582        }
583    }
584}
585
586impl From<TaffyAvailableSpace> for AvailableSpace {
587    fn from(space: TaffyAvailableSpace) -> AvailableSpace {
588        match space {
589            TaffyAvailableSpace::Definite(value) => AvailableSpace::Definite(Pixels(value)),
590            TaffyAvailableSpace::MinContent => AvailableSpace::MinContent,
591            TaffyAvailableSpace::MaxContent => AvailableSpace::MaxContent,
592        }
593    }
594}
595
596impl From<Pixels> for AvailableSpace {
597    fn from(pixels: Pixels) -> Self {
598        AvailableSpace::Definite(pixels)
599    }
600}
601
602impl From<Size<Pixels>> for Size<AvailableSpace> {
603    fn from(size: Size<Pixels>) -> Self {
604        Size {
605            width: AvailableSpace::Definite(size.width),
606            height: AvailableSpace::Definite(size.height),
607        }
608    }
609}