//! Style types for representing lengths / sizes use crate::geometry::{Rect, Size}; use crate::style_helpers::{FromLength, FromPercent, TaffyAuto, TaffyMaxContent, TaffyMinContent, TaffyZero}; use crate::util::sys::abs; /// A unit of linear measurement /// /// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size`]. #[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum LengthPercentage { /// An absolute length in some abstract units. Users of Taffy may define what they correspond /// to in their application (pixels, logical pixels, mm, etc) as they see fit. Length(f32), /// A percentage length relative to the size of the containing block. /// /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]** Percent(f32), } impl TaffyZero for LengthPercentage { const ZERO: Self = Self::Length(0.0); } impl FromLength for LengthPercentage { fn from_length + Copy>(value: Input) -> Self { Self::Length(value.into()) } } impl FromPercent for LengthPercentage { fn from_percent + Copy>(percent: Input) -> Self { Self::Percent(percent.into()) } } /// A unit of linear measurement /// /// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size`]. #[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum LengthPercentageAuto { /// An absolute length in some abstract units. Users of Taffy may define what they correspond /// to in their application (pixels, logical pixels, mm, etc) as they see fit. Length(f32), /// A percentage length relative to the size of the containing block. /// /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]** Percent(f32), /// The dimension should be automatically computed Auto, } impl TaffyZero for LengthPercentageAuto { const ZERO: Self = Self::Length(0.0); } impl TaffyAuto for LengthPercentageAuto { const AUTO: Self = Self::Auto; } impl FromLength for LengthPercentageAuto { fn from_length + Copy>(value: Input) -> Self { Self::Length(value.into()) } } impl FromPercent for LengthPercentageAuto { fn from_percent + Copy>(percent: Input) -> Self { Self::Percent(percent.into()) } } impl From for LengthPercentageAuto { fn from(input: LengthPercentage) -> Self { match input { LengthPercentage::Length(value) => Self::Length(value), LengthPercentage::Percent(value) => Self::Percent(value), } } } impl LengthPercentageAuto { /// Returns: /// - Some(length) for Length variants /// - Some(resolved) using the provided context for Percent variants /// - None for Auto variants #[inline(always)] pub fn resolve_to_option(self, context: f32) -> Option { match self { Self::Length(length) => Some(length), Self::Percent(percent) => Some(context * percent), Self::Auto => None, } } /// Returns true if value is LengthPercentageAuto::Auto #[inline(always)] pub fn is_auto(self) -> bool { self == Self::Auto } } /// A unit of linear measurement /// /// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size`]. #[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Dimension { /// An absolute length in some abstract units. Users of Taffy may define what they correspond /// to in their application (pixels, logical pixels, mm, etc) as they see fit. Length(f32), /// A percentage length relative to the size of the containing block. /// /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]** Percent(f32), /// The dimension should be automatically computed Auto, } impl TaffyZero for Dimension { const ZERO: Self = Self::Length(0.0); } impl TaffyAuto for Dimension { const AUTO: Self = Self::Auto; } impl FromLength for Dimension { fn from_length + Copy>(value: Input) -> Self { Self::Length(value.into()) } } impl FromPercent for Dimension { fn from_percent + Copy>(percent: Input) -> Self { Self::Percent(percent.into()) } } impl From for Dimension { fn from(input: LengthPercentage) -> Self { match input { LengthPercentage::Length(value) => Self::Length(value), LengthPercentage::Percent(value) => Self::Percent(value), } } } impl From for Dimension { fn from(input: LengthPercentageAuto) -> Self { match input { LengthPercentageAuto::Length(value) => Self::Length(value), LengthPercentageAuto::Percent(value) => Self::Percent(value), LengthPercentageAuto::Auto => Self::Auto, } } } impl Dimension { /// Get Length value if value is Length variant #[cfg(feature = "grid")] pub fn into_option(self) -> Option { match self { Dimension::Length(value) => Some(value), _ => None, } } } impl Rect { /// Create a new Rect with [`Dimension::Length`] #[must_use] pub const fn from_length(start: f32, end: f32, top: f32, bottom: f32) -> Self { Rect { left: Dimension::Length(start), right: Dimension::Length(end), top: Dimension::Length(top), bottom: Dimension::Length(bottom), } } /// Create a new Rect with [`Dimension::Percent`] #[must_use] pub const fn from_percent(start: f32, end: f32, top: f32, bottom: f32) -> Self { Rect { left: Dimension::Percent(start), right: Dimension::Percent(end), top: Dimension::Percent(top), bottom: Dimension::Percent(bottom), } } } /// The amount of space available to a node in a given axis /// #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum AvailableSpace { /// The amount of space available is the specified number of pixels Definite(f32), /// The amount of space available is indefinite and the node should be laid out under a min-content constraint MinContent, /// The amount of space available is indefinite and the node should be laid out under a max-content constraint MaxContent, } impl TaffyZero for AvailableSpace { const ZERO: Self = Self::Definite(0.0); } impl TaffyMaxContent for AvailableSpace { const MAX_CONTENT: Self = Self::MaxContent; } impl TaffyMinContent for AvailableSpace { const MIN_CONTENT: Self = Self::MinContent; } impl FromLength for AvailableSpace { fn from_length + Copy>(value: Input) -> Self { Self::Definite(value.into()) } } impl AvailableSpace { /// Returns true for definite values, else false pub fn is_definite(self) -> bool { matches!(self, AvailableSpace::Definite(_)) } /// Convert to Option /// Definite values become Some(value). Constraints become None. pub fn into_option(self) -> Option { match self { AvailableSpace::Definite(value) => Some(value), _ => None, } } /// Return the definite value or a default value pub fn unwrap_or(self, default: f32) -> f32 { self.into_option().unwrap_or(default) } /// Return the definite value. Panic is the value is not definite. #[track_caller] pub fn unwrap(self) -> f32 { self.into_option().unwrap() } /// Return self if definite or a default value pub fn or(self, default: AvailableSpace) -> AvailableSpace { match self { AvailableSpace::Definite(_) => self, _ => default, } } /// Return self if definite or a the result of the default value callback pub fn or_else(self, default_cb: impl FnOnce() -> AvailableSpace) -> AvailableSpace { match self { AvailableSpace::Definite(_) => self, _ => default_cb(), } } /// Return the definite value or the result of the default value callback pub fn unwrap_or_else(self, default_cb: impl FnOnce() -> f32) -> f32 { self.into_option().unwrap_or_else(default_cb) } /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self pub fn maybe_set(self, value: Option) -> AvailableSpace { match value { Some(value) => AvailableSpace::Definite(value), None => self, } } /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self pub fn map_definite_value(self, map_function: impl FnOnce(f32) -> f32) -> AvailableSpace { match self { AvailableSpace::Definite(value) => AvailableSpace::Definite(map_function(value)), _ => self, } } /// Compute free_space given the passed used_space pub fn compute_free_space(&self, used_space: f32) -> f32 { match self { AvailableSpace::MaxContent => f32::INFINITY, AvailableSpace::MinContent => 0.0, AvailableSpace::Definite(available_space) => available_space - used_space, } } /// Compare equality with another AvailableSpace, treating definite values /// that are within f32::EPSILON of each other as equal pub fn is_roughly_equal(self, other: AvailableSpace) -> bool { use AvailableSpace::*; match (self, other) { (Definite(a), Definite(b)) => abs(a - b) < f32::EPSILON, (MinContent, MinContent) => true, (MaxContent, MaxContent) => true, _ => false, } } } impl From for AvailableSpace { fn from(value: f32) -> Self { Self::Definite(value) } } impl From> for AvailableSpace { fn from(option: Option) -> Self { match option { Some(value) => Self::Definite(value), None => Self::MaxContent, } } } impl Size { /// Convert `Size` into `Size>` pub fn into_options(self) -> Size> { Size { width: self.width.into_option(), height: self.height.into_option() } } /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self pub fn maybe_set(self, value: Size>) -> Size { Size { width: self.width.maybe_set(value.width), height: self.height.maybe_set(value.height) } } }