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