blob: 3b2cfb3da96211e0df4528334a6ca8bb25f5b7ee [file] [log] [blame]
//! 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<T>`].
#[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<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::Length(value.into())
}
}
impl FromPercent for LengthPercentage {
fn from_percent<Input: Into<f32> + 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<T>`].
#[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<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::Length(value.into())
}
}
impl FromPercent for LengthPercentageAuto {
fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
Self::Percent(percent.into())
}
}
impl From<LengthPercentage> 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<f32> {
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<T>`].
#[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<Input: Into<f32> + Copy>(value: Input) -> Self {
Self::Length(value.into())
}
}
impl FromPercent for Dimension {
fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
Self::Percent(percent.into())
}
}
impl From<LengthPercentage> for Dimension {
fn from(input: LengthPercentage) -> Self {
match input {
LengthPercentage::Length(value) => Self::Length(value),
LengthPercentage::Percent(value) => Self::Percent(value),
}
}
}
impl From<LengthPercentageAuto> 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<f32> {
match self {
Dimension::Length(value) => Some(value),
_ => None,
}
}
}
impl Rect<Dimension> {
/// 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
/// <https://www.w3.org/TR/css-sizing-3/#available>
#[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<Input: Into<f32> + 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<f32> {
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<f32>) -> 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<f32> for AvailableSpace {
fn from(value: f32) -> Self {
Self::Definite(value)
}
}
impl From<Option<f32>> for AvailableSpace {
fn from(option: Option<f32>) -> Self {
match option {
Some(value) => Self::Definite(value),
None => Self::MaxContent,
}
}
}
impl Size<AvailableSpace> {
/// Convert `Size<AvailableSpace>` into `Size<Option<f32>>`
pub fn into_options(self) -> Size<Option<f32>> {
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<Option<f32>>) -> Size<AvailableSpace> {
Size { width: self.width.maybe_set(value.width), height: self.height.maybe_set(value.height) }
}
}