1use taffy::style::{
4 AlignContent, AlignItems, Dimension, Display, FlexDirection, FlexWrap, LengthPercentage,
5 LengthPercentageAuto, Overflow as TaffyOverflow, Position as TaffyPosition,
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Default)]
10#[non_exhaustive]
11pub enum Length {
12 #[default]
14 Auto,
15 Px(f32),
17 Fraction(f32),
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Default)]
23pub struct Edges<T> {
24 pub left: T,
26 pub top: T,
28 pub right: T,
30 pub bottom: T,
32}
33
34impl<T: Copy> Edges<T> {
35 pub const fn all(value: T) -> Self {
37 Self {
38 left: value,
39 top: value,
40 right: value,
41 bottom: value,
42 }
43 }
44
45 pub const fn symmetric(horizontal: T, vertical: T) -> Self {
47 Self {
48 left: horizontal,
49 top: vertical,
50 right: horizontal,
51 bottom: vertical,
52 }
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
58#[non_exhaustive]
59pub enum Direction {
60 #[default]
62 Row,
63 Column,
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
69#[non_exhaustive]
70pub enum Align {
71 #[default]
73 Stretch,
74 Start,
76 Center,
78 End,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
84#[non_exhaustive]
85pub enum Justify {
86 #[default]
88 Start,
89 Center,
91 End,
93 SpaceBetween,
95 SpaceAround,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
101#[non_exhaustive]
102pub enum Overflow {
103 #[default]
105 Visible,
106 Clip,
108 Scroll,
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
114#[non_exhaustive]
115pub enum Position {
116 #[default]
118 InFlow,
119 Absolute,
121}
122
123#[derive(Debug, Clone, PartialEq)]
141#[non_exhaustive]
142pub struct Style {
143 pub hidden: bool,
145 pub direction: Direction,
147 pub wrap: bool,
149 pub width: Length,
151 pub height: Length,
153 pub min_width: Length,
155 pub min_height: Length,
157 pub max_width: Length,
159 pub max_height: Length,
161 pub grow: f32,
163 pub shrink: f32,
165 pub basis: Length,
167 pub padding: Edges<f32>,
169 pub margin: Edges<Length>,
171 pub gap: f32,
173 pub align_items: Align,
175 pub align_self: Option<Align>,
178 pub justify: Justify,
180 pub position: Position,
182 pub inset: Edges<Length>,
184 pub overflow: Overflow,
186 pub aspect_ratio: Option<f32>,
188}
189
190impl Default for Style {
191 fn default() -> Self {
192 Self {
193 hidden: false,
194 direction: Direction::Row,
195 wrap: false,
196 width: Length::Auto,
197 height: Length::Auto,
198 min_width: Length::Auto,
199 min_height: Length::Auto,
200 max_width: Length::Auto,
201 max_height: Length::Auto,
202 grow: 0.0,
203 shrink: 1.0,
204 basis: Length::Auto,
205 padding: Edges::all(0.0),
206 margin: Edges::all(Length::Px(0.0)),
207 gap: 0.0,
208 align_items: Align::Stretch,
209 align_self: None,
210 justify: Justify::Start,
211 position: Position::InFlow,
212 inset: Edges::all(Length::Auto),
213 overflow: Overflow::Visible,
214 aspect_ratio: None,
215 }
216 }
217}
218
219impl Style {
220 pub fn row() -> Self {
222 Self::default()
223 }
224
225 pub fn column() -> Self {
227 Self {
228 direction: Direction::Column,
229 ..Self::row()
230 }
231 }
232
233 #[must_use]
235 pub fn width(mut self, width: Length) -> Self {
236 self.width = width;
237 self
238 }
239
240 #[must_use]
242 pub fn height(mut self, height: Length) -> Self {
243 self.height = height;
244 self
245 }
246
247 #[must_use]
249 pub fn size(self, width: f32, height: f32) -> Self {
250 self.width(Length::Px(width)).height(Length::Px(height))
251 }
252
253 #[must_use]
255 pub fn min_size(mut self, width: Length, height: Length) -> Self {
256 self.min_width = width;
257 self.min_height = height;
258 self
259 }
260
261 #[must_use]
263 pub fn max_size(mut self, width: Length, height: Length) -> Self {
264 self.max_width = width;
265 self.max_height = height;
266 self
267 }
268
269 #[must_use]
271 pub fn grow(mut self, grow: f32) -> Self {
272 self.grow = grow;
273 self
274 }
275
276 #[must_use]
278 pub fn shrink(mut self, shrink: f32) -> Self {
279 self.shrink = shrink;
280 self
281 }
282
283 #[must_use]
285 pub fn basis(mut self, basis: Length) -> Self {
286 self.basis = basis;
287 self
288 }
289
290 #[must_use]
292 pub fn padding(mut self, padding: f32) -> Self {
293 self.padding = Edges::all(padding);
294 self
295 }
296
297 #[must_use]
299 pub fn padding_edges(mut self, padding: Edges<f32>) -> Self {
300 self.padding = padding;
301 self
302 }
303
304 #[must_use]
306 pub fn margin(mut self, margin: Edges<Length>) -> Self {
307 self.margin = margin;
308 self
309 }
310
311 #[must_use]
313 pub fn gap(mut self, gap: f32) -> Self {
314 self.gap = gap;
315 self
316 }
317
318 #[must_use]
320 pub fn align_items(mut self, align: Align) -> Self {
321 self.align_items = align;
322 self
323 }
324
325 #[must_use]
327 pub fn align_self(mut self, align: Align) -> Self {
328 self.align_self = Some(align);
329 self
330 }
331
332 #[must_use]
334 pub fn justify(mut self, justify: Justify) -> Self {
335 self.justify = justify;
336 self
337 }
338
339 #[must_use]
341 pub fn absolute(mut self, inset: Edges<Length>) -> Self {
342 self.position = Position::Absolute;
343 self.inset = inset;
344 self
345 }
346
347 #[must_use]
349 pub fn overflow(mut self, overflow: Overflow) -> Self {
350 self.overflow = overflow;
351 self
352 }
353
354 #[must_use]
356 pub fn wrap(mut self, wrap: bool) -> Self {
357 self.wrap = wrap;
358 self
359 }
360
361 #[must_use]
363 pub fn hidden(mut self, hidden: bool) -> Self {
364 self.hidden = hidden;
365 self
366 }
367
368 #[must_use]
370 pub fn aspect_ratio(mut self, ratio: f32) -> Self {
371 self.aspect_ratio = Some(ratio);
372 self
373 }
374
375 pub(crate) fn to_taffy(&self, scale: f32) -> taffy::Style {
377 let px = |v: f32| v * scale;
378 let dimension = |l: Length| match l {
379 Length::Auto => Dimension::auto(),
380 Length::Px(v) => Dimension::length(px(v)),
381 Length::Fraction(f) => Dimension::percent(f),
382 };
383 let or_auto = |l: Length| match l {
384 Length::Auto => LengthPercentageAuto::auto(),
385 Length::Px(v) => LengthPercentageAuto::length(px(v)),
386 Length::Fraction(f) => LengthPercentageAuto::percent(f),
387 };
388 let fixed = |v: f32| LengthPercentage::length(px(v));
389 let overflow = match self.overflow {
390 Overflow::Visible => TaffyOverflow::Visible,
391 Overflow::Clip => TaffyOverflow::Clip,
392 Overflow::Scroll => TaffyOverflow::Scroll,
393 };
394 taffy::Style {
395 display: if self.hidden {
396 Display::None
397 } else {
398 Display::Flex
399 },
400 flex_direction: match self.direction {
401 Direction::Row => FlexDirection::Row,
402 Direction::Column => FlexDirection::Column,
403 },
404 flex_wrap: if self.wrap {
405 FlexWrap::Wrap
406 } else {
407 FlexWrap::NoWrap
408 },
409 size: taffy::Size {
410 width: dimension(self.width),
411 height: dimension(self.height),
412 },
413 min_size: taffy::Size {
414 width: or_auto(self.min_width),
415 height: or_auto(self.min_height),
416 },
417 max_size: taffy::Size {
418 width: or_auto(self.max_width),
419 height: or_auto(self.max_height),
420 },
421 flex_grow: self.grow,
422 flex_shrink: self.shrink,
423 flex_basis: dimension(self.basis),
424 padding: taffy::Rect {
425 left: fixed(self.padding.left),
426 right: fixed(self.padding.right),
427 top: fixed(self.padding.top),
428 bottom: fixed(self.padding.bottom),
429 },
430 margin: taffy::Rect {
431 left: or_auto(self.margin.left),
432 right: or_auto(self.margin.right),
433 top: or_auto(self.margin.top),
434 bottom: or_auto(self.margin.bottom),
435 },
436 gap: taffy::Size {
437 width: fixed(self.gap),
438 height: fixed(self.gap),
439 },
440 align_items: align(self.align_items),
441 align_self: self.align_self.map(align),
442 justify_content: justify(self.justify),
443 align_content: AlignContent::START,
444 position: match self.position {
445 Position::InFlow => TaffyPosition::Relative,
446 Position::Absolute => TaffyPosition::Absolute,
447 },
448 inset: taffy::Rect {
449 left: or_auto(self.inset.left),
450 right: or_auto(self.inset.right),
451 top: or_auto(self.inset.top),
452 bottom: or_auto(self.inset.bottom),
453 },
454 overflow: taffy::Point {
455 x: overflow,
456 y: overflow,
457 },
458 scrollbar_width: 0.0,
459 aspect_ratio: self.aspect_ratio,
460 ..taffy::Style::default()
461 }
462 }
463}
464
465fn align(align: Align) -> AlignItems {
466 match align {
467 Align::Stretch => AlignItems::STRETCH,
468 Align::Start => AlignItems::FLEX_START,
469 Align::Center => AlignItems::CENTER,
470 Align::End => AlignItems::FLEX_END,
471 }
472}
473
474fn justify(justify: Justify) -> AlignContent {
475 match justify {
476 Justify::Start => AlignContent::FLEX_START,
477 Justify::Center => AlignContent::CENTER,
478 Justify::End => AlignContent::FLEX_END,
479 Justify::SpaceBetween => AlignContent::SPACE_BETWEEN,
480 Justify::SpaceAround => AlignContent::SPACE_AROUND,
481 }
482}