use crate::core::{
AlignItems, AlignSelf, BorderStyle, Color, Dimension, Display, Edges, Element, ElementType,
FlexDirection, JustifyContent, Overflow, Position, Style,
};
macro_rules! style_setter {
($(#[doc = $doc:literal])* $fn_name:ident($value_ty:ty) => $field:ident) => {
$(#[doc = $doc])*
pub fn $fn_name(mut self, value: $value_ty) -> Self {
self.style.$field = value;
self
}
};
}
macro_rules! style_setter_into {
($(#[doc = $doc:literal])* $fn_name:ident($value_ty:ty) => $field:ident) => {
$(#[doc = $doc])*
pub fn $fn_name(mut self, value: impl Into<$value_ty>) -> Self {
self.style.$field = value.into();
self
}
};
}
macro_rules! style_setter_some {
($(#[doc = $doc:literal])* $fn_name:ident($value_ty:ty) => $field:ident) => {
$(#[doc = $doc])*
pub fn $fn_name(mut self, value: $value_ty) -> Self {
self.style.$field = Some(value);
self
}
};
}
macro_rules! style_setter_sub {
($(#[doc = $doc:literal])* $fn_name:ident($value_ty:ty) => $parent:ident . $field:ident) => {
$(#[doc = $doc])*
pub fn $fn_name(mut self, value: $value_ty) -> Self {
self.style.$parent.$field = value;
self
}
};
}
#[derive(Debug, Clone, Default)]
pub struct Box {
style: Style,
children: Vec<Element>,
key: Option<String>,
scroll_offset_x: Option<u16>,
scroll_offset_y: Option<u16>,
}
impl Box {
pub fn new() -> Self {
Self {
style: Style::new(),
children: Vec::new(),
key: None,
scroll_offset_x: None,
scroll_offset_y: None,
}
}
pub fn key(mut self, key: impl Into<String>) -> Self {
self.key = Some(key.into());
self
}
style_setter!( display(Display) => display);
pub fn hidden(mut self) -> Self {
self.style.display = Display::None;
self
}
pub fn visible(mut self) -> Self {
self.style.display = Display::Flex;
self
}
style_setter!( flex_direction(FlexDirection) => flex_direction);
style_setter!( flex_wrap(bool) => flex_wrap);
style_setter!( flex_grow(f32) => flex_grow);
style_setter!( flex_shrink(f32) => flex_shrink);
pub fn flex(mut self, value: f32) -> Self {
self.style.flex_grow = value;
self.style.flex_shrink = 1.0;
self
}
style_setter_into!( flex_basis(Dimension) => flex_basis);
style_setter!( align_items(AlignItems) => align_items);
style_setter!( align_self(AlignSelf) => align_self);
style_setter!( justify_content(JustifyContent) => justify_content);
style_setter_into!( padding(Edges) => padding);
style_setter_sub!( padding_top(f32) => padding.top);
style_setter_sub!( padding_right(f32) => padding.right);
style_setter_sub!( padding_bottom(f32) => padding.bottom);
style_setter_sub!( padding_left(f32) => padding.left);
pub fn padding_x(mut self, value: f32) -> Self {
self.style.padding.left = value;
self.style.padding.right = value;
self
}
pub fn padding_y(mut self, value: f32) -> Self {
self.style.padding.top = value;
self.style.padding.bottom = value;
self
}
style_setter_into!( margin(Edges) => margin);
style_setter_sub!( margin_top(f32) => margin.top);
style_setter_sub!( margin_right(f32) => margin.right);
style_setter_sub!( margin_bottom(f32) => margin.bottom);
style_setter_sub!( margin_left(f32) => margin.left);
pub fn margin_x(mut self, value: f32) -> Self {
self.style.margin.left = value;
self.style.margin.right = value;
self
}
pub fn margin_y(mut self, value: f32) -> Self {
self.style.margin.top = value;
self.style.margin.bottom = value;
self
}
style_setter!( gap(f32) => gap);
style_setter_some!( column_gap(f32) => column_gap);
style_setter_some!( row_gap(f32) => row_gap);
style_setter_into!( width(Dimension) => width);
style_setter_into!( height(Dimension) => height);
style_setter_into!( min_width(Dimension) => min_width);
style_setter_into!( min_height(Dimension) => min_height);
style_setter_into!( max_width(Dimension) => max_width);
style_setter_into!( max_height(Dimension) => max_height);
style_setter!( border_style(BorderStyle) => border_style);
style_setter_some!( border_color(Color) => border_color);
style_setter_some!( border_top_color(Color) => border_top_color);
style_setter_some!( border_right_color(Color) => border_right_color);
style_setter_some!( border_bottom_color(Color) => border_bottom_color);
style_setter_some!( border_left_color(Color) => border_left_color);
style_setter!( border_dim(bool) => border_dim);
pub fn border(mut self, top: bool, right: bool, bottom: bool, left: bool) -> Self {
self.style.border_top = top;
self.style.border_right = right;
self.style.border_bottom = bottom;
self.style.border_left = left;
self
}
pub fn background(mut self, color: Color) -> Self {
self.style.background_color = Some(color);
self
}
pub fn bg(self, color: Color) -> Self {
self.background(color)
}
pub fn overflow(mut self, overflow: Overflow) -> Self {
self.style.overflow_x = overflow;
self.style.overflow_y = overflow;
self
}
style_setter!( overflow_x(Overflow) => overflow_x);
style_setter!( overflow_y(Overflow) => overflow_y);
pub fn scroll_offset_x(mut self, offset: u16) -> Self {
self.scroll_offset_x = Some(offset);
self
}
pub fn scroll_offset_y(mut self, offset: u16) -> Self {
self.scroll_offset_y = Some(offset);
self
}
pub fn scroll_offset(mut self, x: u16, y: u16) -> Self {
self.scroll_offset_x = Some(x);
self.scroll_offset_y = Some(y);
self
}
style_setter!( position(Position) => position);
pub fn position_absolute(mut self) -> Self {
self.style.position = Position::Absolute;
self
}
style_setter_some!( top(f32) => top);
style_setter_some!( right(f32) => right);
style_setter_some!( bottom(f32) => bottom);
style_setter_some!( left(f32) => left);
pub fn child(mut self, element: impl Into<Element>) -> Self {
self.children.push(element.into());
self
}
pub fn children(mut self, elements: impl IntoIterator<Item = Element>) -> Self {
self.children.extend(elements);
self
}
pub fn into_element(self) -> Element {
let mut element = Element::new(ElementType::Box);
element.style = self.style;
element.key = self.key;
element.scroll_offset_x = self.scroll_offset_x;
element.scroll_offset_y = self.scroll_offset_y;
for child in self.children {
element.add_child(child);
}
element
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_box_builder() {
let element = Box::new()
.padding(1)
.flex_direction(FlexDirection::Column)
.into_element();
assert_eq!(element.style.padding.top, 1.0);
assert_eq!(element.style.flex_direction, FlexDirection::Column);
}
#[test]
fn test_box_with_children() {
let element = Box::new()
.child(Element::text("Hello"))
.child(Element::text("World"))
.into_element();
assert_eq!(element.children.len(), 2);
}
#[test]
fn test_box_border() {
let element = Box::new()
.border_style(BorderStyle::Round)
.border_color(Color::Cyan)
.into_element();
assert_eq!(element.style.border_style, BorderStyle::Round);
assert_eq!(element.style.border_color, Some(Color::Cyan));
}
}