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}