use std::convert::TryFrom;
use std::fmt::{Display, Formatter};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum FlexError {
#[error("bad direction value `{0}`")]
BadDirection(String),
#[error("bad wrap value `{0}`")]
BadWrap(String)
}
pub use FlexError as Error;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Direction {
Row,
RowReverse,
Column,
ColumnReverse,
}
impl<'a> TryFrom<&'a str> for Direction {
type Error = FlexError;
fn try_from(src: &'a str) -> Result<Self, Self::Error> {
if src.eq_ignore_ascii_case("row") {
Ok(Self::Row)
} else if src.eq_ignore_ascii_case("row-reverse") {
Ok(Self::RowReverse)
} else if src.eq_ignore_ascii_case("column") {
Ok(Self::Column)
} else if src.eq_ignore_ascii_case("column-reverse") {
Ok(Self::ColumnReverse)
} else {
Err(FlexError::BadDirection(src.to_string()))
}
}
}
impl Display for Direction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Row => write!(f, "row"),
Self::RowReverse => write!(f, "row-reverse"),
Self::Column => write!(f, "column"),
Self::ColumnReverse => write!(f, "column-reverse"),
}
}
}
impl Into<String> for Direction {
fn into(self) -> String {
self.to_string()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Align {
Auto,
Center,
FlexStart,
FlexEnd,
Stretch,
Between,
Around,
}
impl<'a> From<&'a str> for Align {
fn from(src: &'a str) -> Self {
if src.eq_ignore_ascii_case("auto") {
Self::Auto
} else if src.eq_ignore_ascii_case("center") {
Self::Center
} else if src.eq_ignore_ascii_case("flex-start") {
Self::FlexStart
} else if src.eq_ignore_ascii_case("flex-end") {
Self::FlexEnd
} else if src.eq_ignore_ascii_case("stretch") {
Self::Stretch
} else if src.eq_ignore_ascii_case("between") {
Self::Between
} else if src.eq_ignore_ascii_case("around") {
Self::Around
} else {
Self::Auto
}
}
}
impl Display for Align {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Auto => write!(f, "auto"),
Self::Center => write!(f, "center"),
Self::FlexStart => write!(f, "flex-start"),
Self::FlexEnd => write!(f, "flex-end"),
Self::Stretch => write!(f, "stretch"),
Self::Between => write!(f, "between"),
Self::Around => write!(f, "around"),
}
}
}
impl Into<String> for Align {
fn into(self) -> String {
self.to_string()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Wrap {
Wrap,
NoWrap,
WrapReverse,
}
impl<'a> TryFrom<&'a str> for Wrap {
type Error = FlexError;
fn try_from(src: &'a str) -> Result<Self, Self::Error> {
if src.eq_ignore_ascii_case("wrap") {
Ok(Self::Wrap)
} else if src.eq_ignore_ascii_case("nowrap") {
Ok(Self::NoWrap)
} else if src.eq_ignore_ascii_case("wrap-reverse") {
Ok(Self::WrapReverse)
} else {
Err(FlexError::BadWrap(src.to_string()))
}
}
}
impl Display for Wrap {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Wrap => write!(f, "wrap"),
Self::NoWrap => write!(f, "nowrap"),
Self::WrapReverse => write!(f, "wrap-reverse"),
}
}
}
impl Into<String> for Wrap {
fn into(self) -> String {
self.to_string()
}
}
#[derive(Clone, Debug)]
pub struct FlexBox {
reverse: bool,
size_i: usize,
pos_i: usize,
size2_i: usize,
pos2_i: usize,
grows: usize,
shrinks: usize,
}
#[derive(Clone, Debug)]
pub struct FlexItem {
pub width: usize,
pub height: usize,
pub direction: Direction,
pub children: Vec<FlexItem>,
pub frame: [usize; 4],
pub grow: usize,
pub shrink: usize,
pub align_self: Align,
pub align_items: Align,
pub basis: usize,
pub justify_content: Align,
pub wrap: Wrap,
}
impl FlexBox {
pub fn new() -> FlexBox {
FlexBox {
reverse: false,
grows: 0,
shrinks: 0,
pos_i: 0,
size_i: 0,
size2_i: 0,
pos2_i: 0,
}
}
pub fn layout(&mut self, item: &mut FlexItem) {
let mut flex_dim = 0;
let mut align_dim = 0;
let mut size = 0;
match &item.direction {
Direction::Row => {
flex_dim = item.width;
align_dim = item.height;
self.pos_i = 0;
self.pos2_i = 1;
self.size_i = 2;
self.size2_i = 3;
}
Direction::RowReverse => {
self.reverse = true;
}
Direction::Column => {
flex_dim = item.height;
align_dim = item.width;
self.pos_i = 1;
self.pos2_i = 0;
self.size_i = 3;
self.size2_i = 2;
}
Direction::ColumnReverse => self.reverse = false,
}
let mut pos = if self.reverse { flex_dim } else { 0 };
for child in item.children.iter_mut() {
child.frame[0] = 0;
child.frame[1] = 0;
child.frame[2] = child.width;
child.frame[3] = child.height;
flex_dim -= child.frame[self.size_i];
self.grows += child.grow;
self.shrinks += child.shrink;
if child.basis > 0 {
child.frame[self.size_i] = child.basis;
}
}
for child in item.children.iter_mut() {
let mut wrap_dim = flex_dim;
match item.wrap {
Wrap::NoWrap => {
if flex_dim > 0 {
if child.grow != 0 {
size = (flex_dim / self.grows) * child.grow;
}
} else {
if child.shrink != 0 {
size = (flex_dim / self.shrinks) * child.shrink;
}
}
}
Wrap::Wrap => {
let child_size = child.frame[self.size_i];
if wrap_dim >= child_size {
wrap_dim -= child_size;
} else {
wrap_dim = flex_dim;
pos += child.frame[self.size_i];
}
}
Wrap::WrapReverse => {
unimplemented!()
}
}
child.frame[self.size_i] += size;
let mut spacing = 0;
match child.justify_content {
Align::FlexEnd => {
pos = flex_dim;
}
Align::Center => {
pos = flex_dim / 2;
}
Align::Between => {
if child.children.len() > 0 {
spacing = flex_dim / (child.children.len() - 1);
}
}
Align::Around => {
if child.children.len() > 0 {
spacing = flex_dim / child.children.len();
pos = spacing / 2;
}
}
_ => {}
}
let a_pos = child.frame[self.size_i] + spacing;
if self.reverse {
pos -= a_pos;
child.frame[self.pos_i] = pos;
} else {
child.frame[self.pos_i] = pos;
pos += a_pos;
}
let mut align = 0;
let mut align_type = &child.align_self;
if align_type == &Align::Auto {
align_type = &child.align_items;
}
match align_type {
Align::Auto => {}
Align::FlexStart => {}
Align::Center => {
align = (align_dim / 2) - (child.frame[self.size2_i] / 2);
}
Align::FlexEnd => {
align = align_dim - child.frame[self.size2_i];
}
Align::Stretch => {
align = 0;
child.frame[self.size2_i] = align_dim;
}
_ => {}
}
child.frame[self.pos2_i] = align;
}
}
pub fn flex_layout(&mut self, root: &mut FlexItem) {
self.layout(root)
}
}
impl FlexItem {
pub fn add(&mut self, child: FlexItem) {
self.children.push(child)
}
pub fn delete(&mut self, index: usize) {
self.children.remove(index);
()
}
pub fn new(width: usize, height: usize) -> FlexItem {
FlexItem {
width,
height,
direction: Direction::Row,
children: vec![],
frame: [0; 4],
grow: 0,
shrink: 0,
basis: 0,
align_self: Align::Auto,
align_items: Align::Auto,
justify_content: Align::Auto,
wrap: Wrap::NoWrap,
}
}
pub fn default() -> FlexItem {
FlexItem {
width: 0,
height: 0,
direction: Direction::Row,
children: vec![],
frame: [0; 4],
grow: 0,
shrink: 0,
basis: 0,
align_self: Align::Auto,
align_items: Align::Auto,
justify_content: Align::Auto,
wrap: Wrap::NoWrap,
}
}
}