taffy.rs

  1use super::{
  2    AbsoluteLength, Bounds, DefiniteLength, Edges, Layout, Length, Pixels, Point, Result, Size,
  3    Style,
  4};
  5use std::fmt::Debug;
  6pub use taffy::tree::NodeId as LayoutId;
  7use taffy::{
  8    geometry::Size as TaffySize,
  9    style::AvailableSpace as TaffyAvailableSpace,
 10    tree::{Measurable, MeasureFunc},
 11    Taffy,
 12};
 13pub struct TaffyLayoutEngine(Taffy);
 14
 15#[derive(Copy, Clone, Debug)]
 16pub enum AvailableSpace {
 17    /// The amount of space available is the specified number of pixels
 18    Definite(Pixels),
 19    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
 20    MinContent,
 21    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
 22    MaxContent,
 23}
 24
 25impl TaffyLayoutEngine {
 26    pub fn new() -> Self {
 27        TaffyLayoutEngine(Taffy::new())
 28    }
 29
 30    pub fn request_layout(
 31        &mut self,
 32        style: Style,
 33        rem_size: Pixels,
 34        children: &[LayoutId],
 35    ) -> Result<LayoutId> {
 36        let style = style.to_taffy(rem_size);
 37        if children.is_empty() {
 38            Ok(self.0.new_leaf(style)?)
 39        } else {
 40            Ok(self.0.new_with_children(style, children)?)
 41        }
 42    }
 43
 44    pub fn request_measured_layout(
 45        &mut self,
 46        style: Style,
 47        rem_size: Pixels,
 48        measure: impl Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels>
 49            + Send
 50            + Sync
 51            + 'static,
 52    ) -> Result<LayoutId> {
 53        let style = style.to_taffy(rem_size);
 54
 55        let measurable = Box::new(Measureable(measure)) as Box<dyn Measurable>;
 56        Ok(self
 57            .0
 58            .new_leaf_with_measure(style, MeasureFunc::Boxed(measurable))?)
 59    }
 60
 61    pub fn layout(&mut self, id: LayoutId) -> Result<Layout> {
 62        Ok(self.0.layout(id).map(Into::into)?)
 63    }
 64}
 65
 66struct Measureable<F>(F);
 67
 68impl<F> taffy::tree::Measurable for Measureable<F>
 69where
 70    F: Send + Sync + Fn(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels>,
 71{
 72    fn measure(
 73        &self,
 74        known_dimensions: TaffySize<Option<f32>>,
 75        available_space: TaffySize<TaffyAvailableSpace>,
 76    ) -> TaffySize<f32> {
 77        let known_dimensions: Size<Option<f32>> = known_dimensions.into();
 78        let known_dimensions: Size<Option<Pixels>> = known_dimensions.map(|d| d.map(Into::into));
 79        let available_space = available_space.into();
 80        let size = (self.0)(known_dimensions, available_space);
 81        size.into()
 82    }
 83}
 84
 85trait ToTaffy<Output> {
 86    fn to_taffy(&self, rem_size: Pixels) -> Output;
 87}
 88
 89impl ToTaffy<taffy::style::Style> for Style {
 90    fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Style {
 91        taffy::style::Style {
 92            display: self.display,
 93            overflow: self.overflow.clone().into(),
 94            scrollbar_width: self.scrollbar_width,
 95            position: self.position,
 96            inset: self.inset.to_taffy(rem_size),
 97            size: self.size.to_taffy(rem_size),
 98            min_size: self.min_size.to_taffy(rem_size),
 99            max_size: self.max_size.to_taffy(rem_size),
100            aspect_ratio: self.aspect_ratio,
101            margin: self.margin.to_taffy(rem_size),
102            padding: self.padding.to_taffy(rem_size),
103            border: self.border_widths.to_taffy(rem_size),
104            align_items: self.align_items,
105            align_self: self.align_self,
106            align_content: self.align_content,
107            justify_content: self.justify_content,
108            gap: self.gap.to_taffy(rem_size),
109            flex_direction: self.flex_direction,
110            flex_wrap: self.flex_wrap,
111            flex_basis: self.flex_basis.to_taffy(rem_size),
112            flex_grow: self.flex_grow,
113            flex_shrink: self.flex_shrink,
114            ..Default::default() // Ignore grid properties for now
115        }
116    }
117}
118
119// impl ToTaffy for Bounds<Length> {
120//     type Output = taffy::prelude::Bounds<taffy::prelude::LengthPercentageAuto>;
121
122//     fn to_taffy(
123//         &self,
124//         rem_size: Pixels,
125//     ) -> taffy::prelude::Bounds<taffy::prelude::LengthPercentageAuto> {
126//         taffy::prelude::Bounds {
127//             origin: self.origin.to_taffy(rem_size),
128//             size: self.size.to_taffy(rem_size),
129//         }
130//     }
131// }
132
133impl ToTaffy<taffy::style::LengthPercentageAuto> for Length {
134    fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::LengthPercentageAuto {
135        match self {
136            Length::Definite(length) => length.to_taffy(rem_size),
137            Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
138        }
139    }
140}
141
142impl ToTaffy<taffy::style::Dimension> for Length {
143    fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::Dimension {
144        match self {
145            Length::Definite(length) => length.to_taffy(rem_size),
146            Length::Auto => taffy::prelude::Dimension::Auto,
147        }
148    }
149}
150
151impl ToTaffy<taffy::style::LengthPercentage> for DefiniteLength {
152    fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentage {
153        match self {
154            DefiniteLength::Absolute(length) => match length {
155                AbsoluteLength::Pixels(pixels) => {
156                    taffy::style::LengthPercentage::Length(pixels.into())
157                }
158                AbsoluteLength::Rems(rems) => {
159                    taffy::style::LengthPercentage::Length((*rems * rem_size).into())
160                }
161            },
162            DefiniteLength::Fraction(fraction) => {
163                taffy::style::LengthPercentage::Percent(*fraction)
164            }
165        }
166    }
167}
168
169impl ToTaffy<taffy::style::LengthPercentageAuto> for DefiniteLength {
170    fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentageAuto {
171        match self {
172            DefiniteLength::Absolute(length) => match length {
173                AbsoluteLength::Pixels(pixels) => {
174                    taffy::style::LengthPercentageAuto::Length(pixels.into())
175                }
176                AbsoluteLength::Rems(rems) => {
177                    taffy::style::LengthPercentageAuto::Length((*rems * rem_size).into())
178                }
179            },
180            DefiniteLength::Fraction(fraction) => {
181                taffy::style::LengthPercentageAuto::Percent(*fraction)
182            }
183        }
184    }
185}
186
187impl ToTaffy<taffy::style::Dimension> for DefiniteLength {
188    fn to_taffy(&self, rem_size: Pixels) -> taffy::style::Dimension {
189        match self {
190            DefiniteLength::Absolute(length) => match length {
191                AbsoluteLength::Pixels(pixels) => taffy::style::Dimension::Length(pixels.into()),
192                AbsoluteLength::Rems(rems) => {
193                    taffy::style::Dimension::Length((*rems * rem_size).into())
194                }
195            },
196            DefiniteLength::Fraction(fraction) => taffy::style::Dimension::Percent(*fraction),
197        }
198    }
199}
200
201impl ToTaffy<taffy::style::LengthPercentage> for AbsoluteLength {
202    fn to_taffy(&self, rem_size: Pixels) -> taffy::style::LengthPercentage {
203        match self {
204            AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(pixels.into()),
205            AbsoluteLength::Rems(rems) => {
206                taffy::style::LengthPercentage::Length((*rems * rem_size).into())
207            }
208        }
209    }
210}
211
212impl<T, T2: Clone + Debug> From<taffy::geometry::Point<T>> for Point<T2>
213where
214    T: Into<T2>,
215{
216    fn from(point: taffy::geometry::Point<T>) -> Point<T2> {
217        Point {
218            x: point.x.into(),
219            y: point.y.into(),
220        }
221    }
222}
223
224impl<T: Clone + Debug, T2> Into<taffy::geometry::Point<T2>> for Point<T>
225where
226    T: Into<T2>,
227{
228    fn into(self) -> taffy::geometry::Point<T2> {
229        taffy::geometry::Point {
230            x: self.x.into(),
231            y: self.y.into(),
232        }
233    }
234}
235
236impl<T: ToTaffy<U> + Clone + Debug, U> ToTaffy<taffy::geometry::Size<U>> for Size<T> {
237    fn to_taffy(&self, rem_size: Pixels) -> taffy::geometry::Size<U> {
238        taffy::geometry::Size {
239            width: self.width.to_taffy(rem_size).into(),
240            height: self.height.to_taffy(rem_size).into(),
241        }
242    }
243}
244
245impl<T, U> ToTaffy<taffy::geometry::Rect<U>> for Edges<T>
246where
247    T: ToTaffy<U> + Clone + Debug,
248{
249    fn to_taffy(&self, rem_size: Pixels) -> taffy::geometry::Rect<U> {
250        taffy::geometry::Rect {
251            top: self.top.to_taffy(rem_size).into(),
252            right: self.right.to_taffy(rem_size).into(),
253            bottom: self.bottom.to_taffy(rem_size).into(),
254            left: self.left.to_taffy(rem_size).into(),
255        }
256    }
257}
258
259impl<T: Into<U>, U: Clone + Debug> From<TaffySize<T>> for Size<U> {
260    fn from(taffy_size: taffy::geometry::Size<T>) -> Self {
261        Size {
262            width: taffy_size.width.into(),
263            height: taffy_size.height.into(),
264        }
265    }
266}
267
268impl<T: Into<U> + Clone + Debug, U> From<Size<T>> for taffy::geometry::Size<U> {
269    fn from(size: Size<T>) -> Self {
270        taffy::geometry::Size {
271            width: size.width.into(),
272            height: size.height.into(),
273        }
274    }
275}
276
277// impl From<TaffySize<Option<f32>>> for Size<Option<Pixels>> {
278//     fn from(value: TaffySize<Option<f32>>) -> Self {
279//         Self {
280//             width: value.width.map(Into::into),
281//             height: value.height.map(Into::into),
282//         }
283//     }
284// }
285
286// impl From<TaffySize<TaffyAvailableSpace>> for Size<AvailableSpace> {
287//     fn from(taffy_size: TaffySize<TaffyAvailableSpace>) -> Self {
288//         Size {
289//             width: From::from(taffy_size.width),
290//             height: From::from(taffy_size.height),
291//         }
292//     }
293// }
294
295impl From<TaffyAvailableSpace> for AvailableSpace {
296    fn from(space: TaffyAvailableSpace) -> Self {
297        match space {
298            TaffyAvailableSpace::Definite(value) => AvailableSpace::Definite(Pixels(value)),
299            TaffyAvailableSpace::MinContent => AvailableSpace::MinContent,
300            TaffyAvailableSpace::MaxContent => AvailableSpace::MaxContent,
301        }
302    }
303}
304
305impl From<&taffy::tree::Layout> for Layout {
306    fn from(layout: &taffy::tree::Layout) -> Self {
307        Layout {
308            order: layout.order,
309            bounds: Bounds {
310                origin: layout.location.into(),
311                size: layout.size.into(),
312            },
313        }
314    }
315}