taffy/style/
dimension.rs

1//! Style types for representing lengths / sizes
2
3use crate::geometry::{Rect, Size};
4use crate::style_helpers::{FromLength, FromPercent, TaffyAuto, TaffyMaxContent, TaffyMinContent, TaffyZero};
5use crate::util::sys::abs;
6
7/// A unit of linear measurement
8///
9/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`].
10#[derive(Copy, Clone, PartialEq, Debug)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub enum LengthPercentage {
13    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
14    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
15    Length(f32),
16    /// The dimension is stored in percentage relative to the parent item.
17    Percent(f32),
18}
19impl TaffyZero for LengthPercentage {
20    const ZERO: Self = Self::Length(0.0);
21}
22impl FromLength for LengthPercentage {
23    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
24        Self::Length(value.into())
25    }
26}
27impl FromPercent for LengthPercentage {
28    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
29        Self::Percent(percent.into())
30    }
31}
32
33/// A unit of linear measurement
34///
35/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`].
36#[derive(Copy, Clone, PartialEq, Debug)]
37#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
38pub enum LengthPercentageAuto {
39    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
40    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
41    Length(f32),
42    /// The dimension is stored in percentage relative to the parent item.
43    Percent(f32),
44    /// The dimension should be automatically computed
45    Auto,
46}
47impl TaffyZero for LengthPercentageAuto {
48    const ZERO: Self = Self::Length(0.0);
49}
50impl TaffyAuto for LengthPercentageAuto {
51    const AUTO: Self = Self::Auto;
52}
53impl FromLength for LengthPercentageAuto {
54    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
55        Self::Length(value.into())
56    }
57}
58impl FromPercent for LengthPercentageAuto {
59    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
60        Self::Percent(percent.into())
61    }
62}
63
64impl From<LengthPercentage> for LengthPercentageAuto {
65    fn from(input: LengthPercentage) -> Self {
66        match input {
67            LengthPercentage::Length(value) => Self::Length(value),
68            LengthPercentage::Percent(value) => Self::Percent(value),
69        }
70    }
71}
72
73impl LengthPercentageAuto {
74    /// Returns:
75    ///   - Some(length) for Length variants
76    ///   - Some(resolved) using the provided context for Percent variants
77    ///   - None for Auto variants
78    #[inline(always)]
79    pub fn resolve_to_option(self, context: f32) -> Option<f32> {
80        match self {
81            Self::Length(length) => Some(length),
82            Self::Percent(percent) => Some(context * percent),
83            Self::Auto => None,
84        }
85    }
86
87    /// Returns true if value is LengthPercentageAuto::Auto
88    #[inline(always)]
89    pub fn is_auto(self) -> bool {
90        self == Self::Auto
91    }
92}
93
94/// A unit of linear measurement
95///
96/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`].
97#[derive(Copy, Clone, PartialEq, Debug)]
98#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
99pub enum Dimension {
100    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
101    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
102    Length(f32),
103    /// The dimension is stored in percentage relative to the parent item.
104    Percent(f32),
105    /// The dimension should be automatically computed
106    Auto,
107}
108impl TaffyZero for Dimension {
109    const ZERO: Self = Self::Length(0.0);
110}
111impl TaffyAuto for Dimension {
112    const AUTO: Self = Self::Auto;
113}
114impl FromLength for Dimension {
115    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
116        Self::Length(value.into())
117    }
118}
119impl FromPercent for Dimension {
120    fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
121        Self::Percent(percent.into())
122    }
123}
124
125impl From<LengthPercentage> for Dimension {
126    fn from(input: LengthPercentage) -> Self {
127        match input {
128            LengthPercentage::Length(value) => Self::Length(value),
129            LengthPercentage::Percent(value) => Self::Percent(value),
130        }
131    }
132}
133
134impl From<LengthPercentageAuto> for Dimension {
135    fn from(input: LengthPercentageAuto) -> Self {
136        match input {
137            LengthPercentageAuto::Length(value) => Self::Length(value),
138            LengthPercentageAuto::Percent(value) => Self::Percent(value),
139            LengthPercentageAuto::Auto => Self::Auto,
140        }
141    }
142}
143
144impl Dimension {
145    /// Get Length value if value is Length variant
146    #[cfg(feature = "grid")]
147    pub fn into_option(self) -> Option<f32> {
148        match self {
149            Dimension::Length(value) => Some(value),
150            _ => None,
151        }
152    }
153}
154
155impl Rect<Dimension> {
156    /// Create a new Rect with [`Dimension::Length`]
157    #[must_use]
158    pub const fn from_length(start: f32, end: f32, top: f32, bottom: f32) -> Self {
159        Rect {
160            left: Dimension::Length(start),
161            right: Dimension::Length(end),
162            top: Dimension::Length(top),
163            bottom: Dimension::Length(bottom),
164        }
165    }
166
167    /// Create a new Rect with [`Dimension::Percent`]
168    #[must_use]
169    pub const fn from_percent(start: f32, end: f32, top: f32, bottom: f32) -> Self {
170        Rect {
171            left: Dimension::Percent(start),
172            right: Dimension::Percent(end),
173            top: Dimension::Percent(top),
174            bottom: Dimension::Percent(bottom),
175        }
176    }
177}
178
179/// The amount of space available to a node in a given axis
180/// <https://www.w3.org/TR/css-sizing-3/#available>
181#[derive(Copy, Clone, Debug, PartialEq)]
182pub enum AvailableSpace {
183    /// The amount of space available is the specified number of pixels
184    Definite(f32),
185    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
186    MinContent,
187    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
188    MaxContent,
189}
190impl TaffyZero for AvailableSpace {
191    const ZERO: Self = Self::Definite(0.0);
192}
193impl TaffyMaxContent for AvailableSpace {
194    const MAX_CONTENT: Self = Self::MaxContent;
195}
196impl TaffyMinContent for AvailableSpace {
197    const MIN_CONTENT: Self = Self::MinContent;
198}
199impl FromLength for AvailableSpace {
200    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
201        Self::Definite(value.into())
202    }
203}
204
205impl AvailableSpace {
206    /// Returns true for definite values, else false
207    pub fn is_definite(self) -> bool {
208        matches!(self, AvailableSpace::Definite(_))
209    }
210
211    /// Convert to Option
212    /// Definite values become Some(value). Contraints become None.
213    pub fn into_option(self) -> Option<f32> {
214        match self {
215            AvailableSpace::Definite(value) => Some(value),
216            _ => None,
217        }
218    }
219
220    /// Return the definite value or a default value
221    pub fn unwrap_or(self, default: f32) -> f32 {
222        self.into_option().unwrap_or(default)
223    }
224
225    /// Return the definite value. Panic is the value is not definite.
226    #[track_caller]
227    pub fn unwrap(self) -> f32 {
228        self.into_option().unwrap()
229    }
230
231    /// Return self if definite or a default value
232    pub fn or(self, default: AvailableSpace) -> AvailableSpace {
233        match self {
234            AvailableSpace::Definite(_) => self,
235            _ => default,
236        }
237    }
238
239    /// Return self if definite or a the result of the default value callback
240    pub fn or_else(self, default_cb: impl FnOnce() -> AvailableSpace) -> AvailableSpace {
241        match self {
242            AvailableSpace::Definite(_) => self,
243            _ => default_cb(),
244        }
245    }
246
247    /// Return the definite value or the result of the default value callback
248    pub fn unwrap_or_else(self, default_cb: impl FnOnce() -> f32) -> f32 {
249        self.into_option().unwrap_or_else(default_cb)
250    }
251
252    /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self
253    pub fn maybe_set(self, value: Option<f32>) -> AvailableSpace {
254        match value {
255            Some(value) => AvailableSpace::Definite(value),
256            None => self,
257        }
258    }
259
260    /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self
261    pub fn map_definite_value(self, map_function: impl FnOnce(f32) -> f32) -> AvailableSpace {
262        match self {
263            AvailableSpace::Definite(value) => AvailableSpace::Definite(map_function(value)),
264            _ => self,
265        }
266    }
267
268    /// Compute free_space given the passed used_space
269    pub fn compute_free_space(&self, used_space: f32) -> f32 {
270        match self {
271            AvailableSpace::MaxContent => f32::INFINITY,
272            AvailableSpace::MinContent => 0.0,
273            AvailableSpace::Definite(available_space) => available_space - used_space,
274        }
275    }
276
277    /// Compare equality with another AvailableSpace, treating definite values
278    /// that are within f32::EPSILON of each other as equal
279    pub fn is_roughly_equal(self, other: AvailableSpace) -> bool {
280        use AvailableSpace::*;
281        match (self, other) {
282            (Definite(a), Definite(b)) => abs(a - b) < f32::EPSILON,
283            (MinContent, MinContent) => true,
284            (MaxContent, MaxContent) => true,
285            _ => false,
286        }
287    }
288}
289
290impl From<f32> for AvailableSpace {
291    fn from(value: f32) -> Self {
292        Self::Definite(value)
293    }
294}
295
296impl From<Option<f32>> for AvailableSpace {
297    fn from(option: Option<f32>) -> Self {
298        match option {
299            Some(value) => Self::Definite(value),
300            None => Self::MaxContent,
301        }
302    }
303}
304
305impl Size<AvailableSpace> {
306    /// Convert `Size<AvailableSpace>` into `Size<Option<f32>>`
307    pub fn into_options(self) -> Size<Option<f32>> {
308        Size { width: self.width.into_option(), height: self.height.into_option() }
309    }
310
311    /// If passed value is Some then return AvailableSpace::Definite containing that value, else return self
312    pub fn maybe_set(self, value: Size<Option<f32>>) -> Size<AvailableSpace> {
313        Size { width: self.width.maybe_set(value.width), height: self.height.maybe_set(value.height) }
314    }
315}