use taffy::style::{
AlignContent, AlignItems, Dimension, Display, FlexDirection, FlexWrap, LengthPercentage,
LengthPercentageAuto, Overflow as TaffyOverflow, Position as TaffyPosition,
};
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum Length {
#[default]
Auto,
Px(f32),
Fraction(f32),
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Edges<T> {
pub left: T,
pub top: T,
pub right: T,
pub bottom: T,
}
impl<T: Copy> Edges<T> {
pub const fn all(value: T) -> Self {
Self {
left: value,
top: value,
right: value,
bottom: value,
}
}
pub const fn symmetric(horizontal: T, vertical: T) -> Self {
Self {
left: horizontal,
top: vertical,
right: horizontal,
bottom: vertical,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Direction {
#[default]
Row,
Column,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Align {
#[default]
Stretch,
Start,
Center,
End,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Justify {
#[default]
Start,
Center,
End,
SpaceBetween,
SpaceAround,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Overflow {
#[default]
Visible,
Clip,
Scroll,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Position {
#[default]
InFlow,
Absolute,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Style {
pub hidden: bool,
pub direction: Direction,
pub wrap: bool,
pub width: Length,
pub height: Length,
pub min_width: Length,
pub min_height: Length,
pub max_width: Length,
pub max_height: Length,
pub grow: f32,
pub shrink: f32,
pub basis: Length,
pub padding: Edges<f32>,
pub margin: Edges<Length>,
pub gap: f32,
pub align_items: Align,
pub align_self: Option<Align>,
pub justify: Justify,
pub position: Position,
pub inset: Edges<Length>,
pub overflow: Overflow,
pub aspect_ratio: Option<f32>,
}
impl Default for Style {
fn default() -> Self {
Self {
hidden: false,
direction: Direction::Row,
wrap: false,
width: Length::Auto,
height: Length::Auto,
min_width: Length::Auto,
min_height: Length::Auto,
max_width: Length::Auto,
max_height: Length::Auto,
grow: 0.0,
shrink: 1.0,
basis: Length::Auto,
padding: Edges::all(0.0),
margin: Edges::all(Length::Px(0.0)),
gap: 0.0,
align_items: Align::Stretch,
align_self: None,
justify: Justify::Start,
position: Position::InFlow,
inset: Edges::all(Length::Auto),
overflow: Overflow::Visible,
aspect_ratio: None,
}
}
}
impl Style {
pub fn row() -> Self {
Self::default()
}
pub fn column() -> Self {
Self {
direction: Direction::Column,
..Self::row()
}
}
#[must_use]
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
#[must_use]
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
#[must_use]
pub fn size(self, width: f32, height: f32) -> Self {
self.width(Length::Px(width)).height(Length::Px(height))
}
#[must_use]
pub fn min_size(mut self, width: Length, height: Length) -> Self {
self.min_width = width;
self.min_height = height;
self
}
#[must_use]
pub fn max_size(mut self, width: Length, height: Length) -> Self {
self.max_width = width;
self.max_height = height;
self
}
#[must_use]
pub fn grow(mut self, grow: f32) -> Self {
self.grow = grow;
self
}
#[must_use]
pub fn shrink(mut self, shrink: f32) -> Self {
self.shrink = shrink;
self
}
#[must_use]
pub fn basis(mut self, basis: Length) -> Self {
self.basis = basis;
self
}
#[must_use]
pub fn padding(mut self, padding: f32) -> Self {
self.padding = Edges::all(padding);
self
}
#[must_use]
pub fn padding_edges(mut self, padding: Edges<f32>) -> Self {
self.padding = padding;
self
}
#[must_use]
pub fn margin(mut self, margin: Edges<Length>) -> Self {
self.margin = margin;
self
}
#[must_use]
pub fn gap(mut self, gap: f32) -> Self {
self.gap = gap;
self
}
#[must_use]
pub fn align_items(mut self, align: Align) -> Self {
self.align_items = align;
self
}
#[must_use]
pub fn align_self(mut self, align: Align) -> Self {
self.align_self = Some(align);
self
}
#[must_use]
pub fn justify(mut self, justify: Justify) -> Self {
self.justify = justify;
self
}
#[must_use]
pub fn absolute(mut self, inset: Edges<Length>) -> Self {
self.position = Position::Absolute;
self.inset = inset;
self
}
#[must_use]
pub fn overflow(mut self, overflow: Overflow) -> Self {
self.overflow = overflow;
self
}
#[must_use]
pub fn wrap(mut self, wrap: bool) -> Self {
self.wrap = wrap;
self
}
#[must_use]
pub fn hidden(mut self, hidden: bool) -> Self {
self.hidden = hidden;
self
}
#[must_use]
pub fn aspect_ratio(mut self, ratio: f32) -> Self {
self.aspect_ratio = Some(ratio);
self
}
pub(crate) fn to_taffy(&self, scale: f32) -> taffy::Style {
let px = |v: f32| v * scale;
let dimension = |l: Length| match l {
Length::Auto => Dimension::auto(),
Length::Px(v) => Dimension::length(px(v)),
Length::Fraction(f) => Dimension::percent(f),
};
let or_auto = |l: Length| match l {
Length::Auto => LengthPercentageAuto::auto(),
Length::Px(v) => LengthPercentageAuto::length(px(v)),
Length::Fraction(f) => LengthPercentageAuto::percent(f),
};
let fixed = |v: f32| LengthPercentage::length(px(v));
let overflow = match self.overflow {
Overflow::Visible => TaffyOverflow::Visible,
Overflow::Clip => TaffyOverflow::Clip,
Overflow::Scroll => TaffyOverflow::Scroll,
};
taffy::Style {
display: if self.hidden {
Display::None
} else {
Display::Flex
},
flex_direction: match self.direction {
Direction::Row => FlexDirection::Row,
Direction::Column => FlexDirection::Column,
},
flex_wrap: if self.wrap {
FlexWrap::Wrap
} else {
FlexWrap::NoWrap
},
size: taffy::Size {
width: dimension(self.width),
height: dimension(self.height),
},
min_size: taffy::Size {
width: or_auto(self.min_width),
height: or_auto(self.min_height),
},
max_size: taffy::Size {
width: or_auto(self.max_width),
height: or_auto(self.max_height),
},
flex_grow: self.grow,
flex_shrink: self.shrink,
flex_basis: dimension(self.basis),
padding: taffy::Rect {
left: fixed(self.padding.left),
right: fixed(self.padding.right),
top: fixed(self.padding.top),
bottom: fixed(self.padding.bottom),
},
margin: taffy::Rect {
left: or_auto(self.margin.left),
right: or_auto(self.margin.right),
top: or_auto(self.margin.top),
bottom: or_auto(self.margin.bottom),
},
gap: taffy::Size {
width: fixed(self.gap),
height: fixed(self.gap),
},
align_items: align(self.align_items),
align_self: self.align_self.map(align),
justify_content: justify(self.justify),
align_content: AlignContent::START,
position: match self.position {
Position::InFlow => TaffyPosition::Relative,
Position::Absolute => TaffyPosition::Absolute,
},
inset: taffy::Rect {
left: or_auto(self.inset.left),
right: or_auto(self.inset.right),
top: or_auto(self.inset.top),
bottom: or_auto(self.inset.bottom),
},
overflow: taffy::Point {
x: overflow,
y: overflow,
},
scrollbar_width: 0.0,
aspect_ratio: self.aspect_ratio,
..taffy::Style::default()
}
}
}
fn align(align: Align) -> AlignItems {
match align {
Align::Stretch => AlignItems::STRETCH,
Align::Start => AlignItems::FLEX_START,
Align::Center => AlignItems::CENTER,
Align::End => AlignItems::FLEX_END,
}
}
fn justify(justify: Justify) -> AlignContent {
match justify {
Justify::Start => AlignContent::FLEX_START,
Justify::Center => AlignContent::CENTER,
Justify::End => AlignContent::FLEX_END,
Justify::SpaceBetween => AlignContent::SPACE_BETWEEN,
Justify::SpaceAround => AlignContent::SPACE_AROUND,
}
}