use astrelis_core::math::Vec2;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Length {
Px(f32),
Percent(f32),
Auto,
Vw(f32),
Vh(f32),
Vmin(f32),
Vmax(f32),
}
impl Length {
pub fn px(value: f32) -> Self {
Self::Px(value)
}
pub fn percent(value: f32) -> Self {
Self::Percent(value)
}
pub fn auto() -> Self {
Self::Auto
}
pub fn vw(value: f32) -> Self {
Self::Vw(value)
}
pub fn vh(value: f32) -> Self {
Self::Vh(value)
}
pub fn vmin(value: f32) -> Self {
Self::Vmin(value)
}
pub fn vmax(value: f32) -> Self {
Self::Vmax(value)
}
pub fn resolve(self, viewport_size: Vec2) -> Self {
match self {
Length::Vw(v) => Length::Px(v * viewport_size.x / 100.0),
Length::Vh(v) => Length::Px(v * viewport_size.y / 100.0),
Length::Vmin(v) => {
let min = viewport_size.x.min(viewport_size.y);
Length::Px(v * min / 100.0)
}
Length::Vmax(v) => {
let max = viewport_size.x.max(viewport_size.y);
Length::Px(v * max / 100.0)
}
other => other, }
}
pub fn to_dimension(self) -> taffy::Dimension {
match self {
Length::Px(v) => taffy::Dimension::Length(v),
Length::Percent(v) => taffy::Dimension::Percent(v / 100.0),
Length::Auto => taffy::Dimension::Auto,
Length::Vw(_) | Length::Vh(_) | Length::Vmin(_) | Length::Vmax(_) => {
panic!(
"Viewport-relative units must be resolved to pixels before converting to Taffy dimension. \
Call .resolve(viewport_size) first."
);
}
}
}
pub fn from_dimension(dim: taffy::Dimension) -> Self {
match dim {
taffy::Dimension::Length(v) => Length::Px(v),
taffy::Dimension::Percent(v) => Length::Percent(v * 100.0),
taffy::Dimension::Auto => Length::Auto,
}
}
pub fn is_px(&self) -> bool {
matches!(self, Length::Px(_))
}
pub fn is_percent(&self) -> bool {
matches!(self, Length::Percent(_))
}
pub fn is_auto(&self) -> bool {
matches!(self, Length::Auto)
}
pub fn is_viewport(&self) -> bool {
matches!(
self,
Length::Vw(_) | Length::Vh(_) | Length::Vmin(_) | Length::Vmax(_)
)
}
}
impl From<f32> for Length {
fn from(value: f32) -> Self {
Length::Px(value)
}
}
impl From<Length> for taffy::Dimension {
fn from(length: Length) -> Self {
length.to_dimension()
}
}
impl fmt::Display for Length {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Length::Px(v) => write!(f, "{}px", v),
Length::Percent(v) => write!(f, "{}%", v),
Length::Auto => write!(f, "auto"),
Length::Vw(v) => write!(f, "{}vw", v),
Length::Vh(v) => write!(f, "{}vh", v),
Length::Vmin(v) => write!(f, "{}vmin", v),
Length::Vmax(v) => write!(f, "{}vmax", v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LengthAuto {
Px(f32),
Percent(f32),
Auto,
Vw(f32),
Vh(f32),
Vmin(f32),
Vmax(f32),
}
impl LengthAuto {
pub fn px(value: f32) -> Self {
Self::Px(value)
}
pub fn percent(value: f32) -> Self {
Self::Percent(value)
}
pub fn auto() -> Self {
Self::Auto
}
pub fn vw(value: f32) -> Self {
Self::Vw(value)
}
pub fn vh(value: f32) -> Self {
Self::Vh(value)
}
pub fn vmin(value: f32) -> Self {
Self::Vmin(value)
}
pub fn vmax(value: f32) -> Self {
Self::Vmax(value)
}
pub fn resolve(self, viewport_size: Vec2) -> Self {
match self {
LengthAuto::Vw(v) => LengthAuto::Px(v * viewport_size.x / 100.0),
LengthAuto::Vh(v) => LengthAuto::Px(v * viewport_size.y / 100.0),
LengthAuto::Vmin(v) => {
let min = viewport_size.x.min(viewport_size.y);
LengthAuto::Px(v * min / 100.0)
}
LengthAuto::Vmax(v) => {
let max = viewport_size.x.max(viewport_size.y);
LengthAuto::Px(v * max / 100.0)
}
other => other,
}
}
pub fn to_length_percentage_auto(self) -> taffy::LengthPercentageAuto {
match self {
LengthAuto::Px(v) => taffy::LengthPercentageAuto::Length(v),
LengthAuto::Percent(v) => taffy::LengthPercentageAuto::Percent(v / 100.0),
LengthAuto::Auto => taffy::LengthPercentageAuto::Auto,
LengthAuto::Vw(_) | LengthAuto::Vh(_) | LengthAuto::Vmin(_) | LengthAuto::Vmax(_) => {
panic!(
"Viewport-relative units must be resolved to pixels before converting to Taffy dimension. \
Call .resolve(viewport_size) first."
);
}
}
}
pub fn from_length_percentage_auto(lpa: taffy::LengthPercentageAuto) -> Self {
match lpa {
taffy::LengthPercentageAuto::Length(v) => LengthAuto::Px(v),
taffy::LengthPercentageAuto::Percent(v) => LengthAuto::Percent(v * 100.0),
taffy::LengthPercentageAuto::Auto => LengthAuto::Auto,
}
}
}
impl From<f32> for LengthAuto {
fn from(value: f32) -> Self {
LengthAuto::Px(value)
}
}
impl From<Length> for LengthAuto {
fn from(length: Length) -> Self {
match length {
Length::Px(v) => LengthAuto::Px(v),
Length::Percent(v) => LengthAuto::Percent(v),
Length::Auto => LengthAuto::Auto,
Length::Vw(v) => LengthAuto::Vw(v),
Length::Vh(v) => LengthAuto::Vh(v),
Length::Vmin(v) => LengthAuto::Vmin(v),
Length::Vmax(v) => LengthAuto::Vmax(v),
}
}
}
impl From<LengthAuto> for taffy::LengthPercentageAuto {
fn from(length: LengthAuto) -> Self {
length.to_length_percentage_auto()
}
}
impl fmt::Display for LengthAuto {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LengthAuto::Px(v) => write!(f, "{}px", v),
LengthAuto::Percent(v) => write!(f, "{}%", v),
LengthAuto::Auto => write!(f, "auto"),
LengthAuto::Vw(v) => write!(f, "{}vw", v),
LengthAuto::Vh(v) => write!(f, "{}vh", v),
LengthAuto::Vmin(v) => write!(f, "{}vmin", v),
LengthAuto::Vmax(v) => write!(f, "{}vmax", v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LengthPercentage {
Px(f32),
Percent(f32),
Vw(f32),
Vh(f32),
Vmin(f32),
Vmax(f32),
}
impl LengthPercentage {
pub fn px(value: f32) -> Self {
Self::Px(value)
}
pub fn percent(value: f32) -> Self {
Self::Percent(value)
}
pub fn vw(value: f32) -> Self {
Self::Vw(value)
}
pub fn vh(value: f32) -> Self {
Self::Vh(value)
}
pub fn vmin(value: f32) -> Self {
Self::Vmin(value)
}
pub fn vmax(value: f32) -> Self {
Self::Vmax(value)
}
pub fn resolve(self, viewport_size: Vec2) -> Self {
match self {
LengthPercentage::Vw(v) => LengthPercentage::Px(v * viewport_size.x / 100.0),
LengthPercentage::Vh(v) => LengthPercentage::Px(v * viewport_size.y / 100.0),
LengthPercentage::Vmin(v) => {
let min = viewport_size.x.min(viewport_size.y);
LengthPercentage::Px(v * min / 100.0)
}
LengthPercentage::Vmax(v) => {
let max = viewport_size.x.max(viewport_size.y);
LengthPercentage::Px(v * max / 100.0)
}
other => other,
}
}
pub fn to_length_percentage(self) -> taffy::LengthPercentage {
match self {
LengthPercentage::Px(v) => taffy::LengthPercentage::Length(v),
LengthPercentage::Percent(v) => taffy::LengthPercentage::Percent(v / 100.0),
LengthPercentage::Vw(_)
| LengthPercentage::Vh(_)
| LengthPercentage::Vmin(_)
| LengthPercentage::Vmax(_) => {
panic!(
"Viewport-relative units must be resolved to pixels before converting to Taffy dimension. \
Call .resolve(viewport_size) first."
);
}
}
}
pub fn from_length_percentage(lp: taffy::LengthPercentage) -> Self {
match lp {
taffy::LengthPercentage::Length(v) => LengthPercentage::Px(v),
taffy::LengthPercentage::Percent(v) => LengthPercentage::Percent(v * 100.0),
}
}
}
impl From<f32> for LengthPercentage {
fn from(value: f32) -> Self {
LengthPercentage::Px(value)
}
}
impl From<LengthPercentage> for taffy::LengthPercentage {
fn from(length: LengthPercentage) -> Self {
length.to_length_percentage()
}
}
impl fmt::Display for LengthPercentage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LengthPercentage::Px(v) => write!(f, "{}px", v),
LengthPercentage::Percent(v) => write!(f, "{}%", v),
LengthPercentage::Vw(v) => write!(f, "{}vw", v),
LengthPercentage::Vh(v) => write!(f, "{}vh", v),
LengthPercentage::Vmin(v) => write!(f, "{}vmin", v),
LengthPercentage::Vmax(v) => write!(f, "{}vmax", v),
}
}
}
pub fn length(value: f32) -> taffy::Dimension {
taffy::Dimension::Length(value)
}
pub fn percent(value: f32) -> taffy::Dimension {
taffy::Dimension::Percent(value / 100.0)
}
pub fn auto() -> taffy::Dimension {
taffy::Dimension::Auto
}
pub fn vw(value: f32) -> Length {
Length::Vw(value)
}
pub fn vh(value: f32) -> Length {
Length::Vh(value)
}
pub fn vmin(value: f32) -> Length {
Length::Vmin(value)
}
pub fn vmax(value: f32) -> Length {
Length::Vmax(value)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_length_px() {
let len = Length::Px(100.0);
assert!(len.is_px());
assert!(!len.is_percent());
assert!(!len.is_auto());
assert_eq!(len.to_string(), "100px");
}
#[test]
fn test_length_percent() {
let len = Length::Percent(50.0);
assert!(!len.is_px());
assert!(len.is_percent());
assert!(!len.is_auto());
assert_eq!(len.to_string(), "50%");
}
#[test]
fn test_length_auto() {
let len = Length::Auto;
assert!(!len.is_px());
assert!(!len.is_percent());
assert!(len.is_auto());
assert_eq!(len.to_string(), "auto");
}
#[test]
fn test_length_from_f32() {
let len: Length = 100.0.into();
assert_eq!(len, Length::Px(100.0));
}
#[test]
fn test_length_to_dimension() {
let len = Length::Px(100.0);
let dim = len.to_dimension();
assert!(matches!(dim, taffy::Dimension::Length(100.0)));
let len = Length::Percent(50.0);
let dim = len.to_dimension();
assert!(matches!(dim, taffy::Dimension::Percent(0.5)));
let len = Length::Auto;
let dim = len.to_dimension();
assert!(matches!(dim, taffy::Dimension::Auto));
}
#[test]
fn test_length_percentage() {
let lp = LengthPercentage::Px(100.0);
assert_eq!(lp.to_string(), "100px");
let lp = LengthPercentage::Percent(50.0);
assert_eq!(lp.to_string(), "50%");
}
#[test]
fn test_length_auto_conversion() {
let la = LengthAuto::Px(100.0);
assert_eq!(la.to_string(), "100px");
let len: LengthAuto = Length::Percent(50.0).into();
assert_eq!(len, LengthAuto::Percent(50.0));
}
#[test]
fn test_helper_functions() {
let dim = length(100.0);
assert!(matches!(dim, taffy::Dimension::Length(100.0)));
let dim = percent(50.0);
assert!(matches!(dim, taffy::Dimension::Percent(0.5)));
let dim = auto();
assert!(matches!(dim, taffy::Dimension::Auto));
}
}