tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 231Rust 10TOML 3CSS 1348 Sutichaykuna

⎇ morphorm src/style/dimension.rs Rust · 323 siq'ikuna · 10.8 KB Ñawpa willay Pi imata llamkarqan ch'uya

WIP 0dcb67463 · Nico Burns ·

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