use taffy::prelude::*;
use crate::types::Edges;
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub enum Length {
#[default]
Auto,
Px(f32),
Percent(f32),
}
impl Length {
pub const fn px(value: f32) -> Self {
Self::Px(value)
}
pub const fn percent(value: f32) -> Self {
Self::Percent(value)
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum DisplayMode {
#[default]
Flex,
Grid,
None,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum Position {
#[default]
Relative,
Absolute,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum Overflow {
#[default]
Visible,
Hidden,
Scroll,
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct SizeConstraint {
pub width: Length,
pub height: Length,
}
impl SizeConstraint {
pub const AUTO: Self = Self {
width: Length::Auto,
height: Length::Auto,
};
pub const fn new(width: Length, height: Length) -> Self {
Self { width, height }
}
pub const fn px(width: f32, height: f32) -> Self {
Self {
width: Length::Px(width),
height: Length::Px(height),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Style {
pub display: DisplayMode,
pub flex_direction: FlexDirection,
pub align_items: AlignItems,
pub justify_content: JustifyContent,
pub flex_wrap: FlexWrap,
pub flex_grow: f32,
pub flex_shrink: f32,
pub flex_basis: Length,
pub padding: Edges<Length>,
pub margin: Edges<Length>,
pub gap: f32,
pub size: SizeConstraint,
pub min_size: SizeConstraint,
pub max_size: SizeConstraint,
pub position: Position,
pub overflow: Overflow,
}
impl Default for Style {
fn default() -> Self {
Self {
display: DisplayMode::Flex,
flex_direction: FlexDirection::Row,
align_items: AlignItems::Stretch,
justify_content: JustifyContent::FlexStart,
flex_wrap: FlexWrap::NoWrap,
flex_grow: 0.0,
flex_shrink: 1.0,
flex_basis: Length::Auto,
padding: Edges::default(),
margin: Edges::default(),
gap: 0.0,
size: SizeConstraint::AUTO,
min_size: SizeConstraint::AUTO,
max_size: SizeConstraint::AUTO,
position: Position::Relative,
overflow: Overflow::Visible,
}
}
}
impl Style {
pub fn new() -> Self {
Self::default()
}
pub fn display(mut self, display: DisplayMode) -> Self {
self.display = display;
self
}
pub fn flex_direction(mut self, direction: FlexDirection) -> Self {
self.flex_direction = direction;
self
}
pub fn column(mut self) -> Self {
self.flex_direction = FlexDirection::Column;
self
}
pub fn row(mut self) -> Self {
self.flex_direction = FlexDirection::Row;
self
}
pub fn align_items(mut self, align: AlignItems) -> Self {
self.align_items = align;
self
}
pub fn justify_content(mut self, justify: JustifyContent) -> Self {
self.justify_content = justify;
self
}
pub fn flex_grow(mut self, grow: f32) -> Self {
self.flex_grow = grow;
self
}
pub fn flex_shrink(mut self, shrink: f32) -> Self {
self.flex_shrink = shrink;
self
}
pub fn padding_all(mut self, value: f32) -> Self {
self.padding = Edges::all(Length::Px(value));
self
}
pub fn padding(mut self, edges: Edges<Length>) -> Self {
self.padding = edges;
self
}
pub fn margin_all(mut self, value: f32) -> Self {
self.margin = Edges::all(Length::Px(value));
self
}
pub fn margin(mut self, edges: Edges<Length>) -> Self {
self.margin = edges;
self
}
pub fn gap(mut self, gap: f32) -> Self {
self.gap = gap;
self
}
pub fn width(mut self, width: f32) -> Self {
self.size.width = Length::Px(width);
self
}
pub fn height(mut self, height: f32) -> Self {
self.size.height = Length::Px(height);
self
}
pub fn size(mut self, size: SizeConstraint) -> Self {
self.size = size;
self
}
pub fn min_size(mut self, size: SizeConstraint) -> Self {
self.min_size = size;
self
}
pub fn max_size(mut self, size: SizeConstraint) -> Self {
self.max_size = size;
self
}
pub fn position(mut self, position: Position) -> Self {
self.position = position;
self
}
pub fn overflow(mut self, overflow: Overflow) -> Self {
self.overflow = overflow;
self
}
}
fn length_to_dimension(len: Length) -> Dimension {
match len {
Length::Auto => Dimension::auto(),
Length::Px(v) => Dimension::length(v),
Length::Percent(v) => Dimension::percent(v / 100.0),
}
}
fn length_to_length_percentage(len: Length) -> LengthPercentage {
match len {
Length::Auto => LengthPercentage::length(0.0),
Length::Px(v) => LengthPercentage::length(v),
Length::Percent(v) => LengthPercentage::percent(v / 100.0),
}
}
fn length_to_length_percentage_auto(len: Length) -> LengthPercentageAuto {
match len {
Length::Auto => LengthPercentageAuto::auto(),
Length::Px(v) => LengthPercentageAuto::length(v),
Length::Percent(v) => LengthPercentageAuto::percent(v / 100.0),
}
}
impl From<&Style> for taffy::Style {
fn from(s: &Style) -> taffy::Style {
taffy::Style {
display: match s.display {
DisplayMode::Flex => Display::Flex,
DisplayMode::Grid => Display::Grid,
DisplayMode::None => Display::None,
},
flex_direction: s.flex_direction,
align_items: Some(s.align_items),
justify_content: Some(s.justify_content),
flex_wrap: s.flex_wrap,
flex_grow: s.flex_grow,
flex_shrink: s.flex_shrink,
flex_basis: length_to_dimension(s.flex_basis),
padding: Rect {
left: length_to_length_percentage(s.padding.left),
right: length_to_length_percentage(s.padding.right),
top: length_to_length_percentage(s.padding.top),
bottom: length_to_length_percentage(s.padding.bottom),
},
margin: Rect {
left: length_to_length_percentage_auto(s.margin.left),
right: length_to_length_percentage_auto(s.margin.right),
top: length_to_length_percentage_auto(s.margin.top),
bottom: length_to_length_percentage_auto(s.margin.bottom),
},
gap: taffy::Size {
width: LengthPercentage::length(s.gap),
height: LengthPercentage::length(s.gap),
},
size: taffy::Size {
width: length_to_dimension(s.size.width),
height: length_to_dimension(s.size.height),
},
min_size: taffy::Size {
width: length_to_dimension(s.min_size.width),
height: length_to_dimension(s.min_size.height),
},
max_size: taffy::Size {
width: length_to_dimension(s.max_size.width),
height: length_to_dimension(s.max_size.height),
},
position: match s.position {
Position::Relative => taffy::Position::Relative,
Position::Absolute => taffy::Position::Absolute,
},
overflow: taffy::Point {
x: match s.overflow {
Overflow::Visible => taffy::Overflow::Visible,
Overflow::Hidden => taffy::Overflow::Clip,
Overflow::Scroll => taffy::Overflow::Scroll,
},
y: match s.overflow {
Overflow::Visible => taffy::Overflow::Visible,
Overflow::Hidden => taffy::Overflow::Clip,
Overflow::Scroll => taffy::Overflow::Scroll,
},
},
..Default::default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn style_default() {
let s = Style::default();
assert_eq!(s.display, DisplayMode::Flex);
assert_eq!(s.flex_direction, FlexDirection::Row);
assert_eq!(s.flex_grow, 0.0);
assert_eq!(s.flex_shrink, 1.0);
}
#[test]
fn style_builder() {
let s = Style::new()
.column()
.padding_all(16.0)
.gap(8.0)
.flex_grow(1.0);
assert_eq!(s.flex_direction, FlexDirection::Column);
assert_eq!(s.padding, Edges::all(Length::Px(16.0)));
assert_eq!(s.gap, 8.0);
assert_eq!(s.flex_grow, 1.0);
}
#[test]
fn style_to_taffy() {
let s = Style::new().padding_all(10.0).width(100.0).height(50.0);
let taffy_style: taffy::Style = (&s).into();
assert_eq!(taffy_style.display, Display::Flex);
assert_eq!(taffy_style.padding.left, LengthPercentage::length(10.0));
}
}