taffy/style/
dimension.rs

1//! Style types for representing lengths / sizes
2use super::CompactLength;
3use crate::geometry::Rect;
4use crate::style_helpers::{FromLength, FromPercent, TaffyAuto, TaffyZero};
5
6/// A unit of linear measurement
7///
8/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`](crate::geometry::Size).
9#[derive(Copy, Clone, PartialEq, Debug)]
10#[cfg_attr(feature = "serde", derive(Serialize))]
11pub struct LengthPercentage(pub(crate) CompactLength);
12impl TaffyZero for LengthPercentage {
13    const ZERO: Self = Self(CompactLength::ZERO);
14}
15impl FromLength for LengthPercentage {
16    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
17        Self::length(value.into())
18    }
19}
20impl FromPercent for LengthPercentage {
21    fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
22        Self::percent(value.into())
23    }
24}
25impl LengthPercentage {
26    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
27    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
28    #[inline(always)]
29    pub const fn length(val: f32) -> Self {
30        Self(CompactLength::length(val))
31    }
32
33    /// A percentage length relative to the size of the containing block.
34    ///
35    /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
36    #[inline(always)]
37    pub const fn percent(val: f32) -> Self {
38        Self(CompactLength::percent(val))
39    }
40
41    /// A `calc()` value. The value passed here is treated as an opaque handle to
42    /// the actual calc representation and may be a pointer, index, etc.
43    ///
44    /// The low 3 bits are used as a tag value and will be returned as 0.
45    #[inline(always)]
46    #[cfg(feature = "calc")]
47    pub fn calc(ptr: *const ()) -> Self {
48        Self(CompactLength::calc(ptr))
49    }
50
51    /// Create a LengthPercentage from a raw `CompactLength`.
52    /// # Safety
53    /// CompactLength must represent a valid variant for LengthPercentage
54    #[allow(unsafe_code)]
55    pub unsafe fn from_raw(val: CompactLength) -> Self {
56        Self(val)
57    }
58
59    /// Get the underlying `CompactLength` representation of the value
60    pub fn into_raw(self) -> CompactLength {
61        self.0
62    }
63}
64
65#[cfg(feature = "serde")]
66impl<'de> serde::Deserialize<'de> for LengthPercentage {
67    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68    where
69        D: serde::Deserializer<'de>,
70    {
71        let inner = CompactLength::deserialize(deserializer)?;
72        // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
73        if matches!(inner.tag(), CompactLength::LENGTH_TAG | CompactLength::PERCENT_TAG) {
74            Ok(Self(inner))
75        } else {
76            Err(serde::de::Error::custom("Invalid tag"))
77        }
78    }
79}
80
81/// A unit of linear measurement
82///
83/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`](crate::geometry::Size).
84#[derive(Copy, Clone, PartialEq, Debug)]
85#[cfg_attr(feature = "serde", derive(Serialize))]
86pub struct LengthPercentageAuto(pub(crate) CompactLength);
87impl TaffyZero for LengthPercentageAuto {
88    const ZERO: Self = Self(CompactLength::ZERO);
89}
90impl TaffyAuto for LengthPercentageAuto {
91    const AUTO: Self = Self(CompactLength::AUTO);
92}
93impl FromLength for LengthPercentageAuto {
94    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
95        Self::length(value.into())
96    }
97}
98impl FromPercent for LengthPercentageAuto {
99    fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
100        Self::percent(value.into())
101    }
102}
103impl From<LengthPercentage> for LengthPercentageAuto {
104    fn from(input: LengthPercentage) -> Self {
105        Self(input.0)
106    }
107}
108
109impl LengthPercentageAuto {
110    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
111    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
112    #[inline(always)]
113    pub const fn length(val: f32) -> Self {
114        Self(CompactLength::length(val))
115    }
116
117    /// A percentage length relative to the size of the containing block.
118    ///
119    /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
120    #[inline(always)]
121    pub const fn percent(val: f32) -> Self {
122        Self(CompactLength::percent(val))
123    }
124
125    /// The dimension should be automatically computed according to algorithm-specific rules
126    /// regarding the default size of boxes.
127    #[inline(always)]
128    pub const fn auto() -> Self {
129        Self(CompactLength::auto())
130    }
131
132    /// A `calc()` value. The value passed here is treated as an opaque handle to
133    /// the actual calc representation and may be a pointer, index, etc.
134    ///
135    /// The low 3 bits are used as a tag value and will be returned as 0.
136    #[inline]
137    #[cfg(feature = "calc")]
138    pub fn calc(ptr: *const ()) -> Self {
139        Self(CompactLength::calc(ptr))
140    }
141
142    /// Create a LengthPercentageAuto from a raw `CompactLength`.
143    /// # Safety
144    /// CompactLength must represent a valid variant for LengthPercentageAuto
145    #[allow(unsafe_code)]
146    pub unsafe fn from_raw(val: CompactLength) -> Self {
147        Self(val)
148    }
149
150    /// Get the underlying `CompactLength` representation of the value
151    pub fn into_raw(self) -> CompactLength {
152        self.0
153    }
154
155    /// Returns:
156    ///   - Some(length) for Length variants
157    ///   - Some(resolved) using the provided context for Percent variants
158    ///   - None for Auto variants
159    #[inline(always)]
160    pub fn resolve_to_option(self, context: f32, calc_resolver: impl Fn(*const (), f32) -> f32) -> Option<f32> {
161        match self.0.tag() {
162            CompactLength::LENGTH_TAG => Some(self.0.value()),
163            CompactLength::PERCENT_TAG => Some(context * self.0.value()),
164            CompactLength::AUTO_TAG => None,
165            #[cfg(feature = "calc")]
166            _ if self.0.is_calc() => Some(calc_resolver(self.0.calc_value(), context)),
167            _ => unreachable!("LengthPercentageAuto values cannot be constructed with other tags"),
168        }
169    }
170
171    /// Returns true if value is LengthPercentageAuto::Auto
172    #[inline(always)]
173    pub fn is_auto(self) -> bool {
174        self.0.is_auto()
175    }
176}
177
178#[cfg(feature = "serde")]
179impl<'de> serde::Deserialize<'de> for LengthPercentageAuto {
180    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
181    where
182        D: serde::Deserializer<'de>,
183    {
184        let inner = CompactLength::deserialize(deserializer)?;
185        // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
186        if matches!(inner.tag(), CompactLength::LENGTH_TAG | CompactLength::PERCENT_TAG | CompactLength::AUTO_TAG) {
187            Ok(Self(inner))
188        } else {
189            Err(serde::de::Error::custom("Invalid tag"))
190        }
191    }
192}
193
194/// A unit of linear measurement
195///
196/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`](crate::geometry::Size).
197#[derive(Copy, Clone, PartialEq, Debug)]
198#[cfg_attr(feature = "serde", derive(Serialize))]
199pub struct Dimension(pub(crate) CompactLength);
200impl TaffyZero for Dimension {
201    const ZERO: Self = Self(CompactLength::ZERO);
202}
203impl TaffyAuto for Dimension {
204    const AUTO: Self = Self(CompactLength::AUTO);
205}
206impl FromLength for Dimension {
207    fn from_length<Input: Into<f32> + Copy>(value: Input) -> Self {
208        Self::length(value.into())
209    }
210}
211impl FromPercent for Dimension {
212    fn from_percent<Input: Into<f32> + Copy>(value: Input) -> Self {
213        Self::percent(value.into())
214    }
215}
216impl From<LengthPercentage> for Dimension {
217    fn from(input: LengthPercentage) -> Self {
218        Self(input.0)
219    }
220}
221impl From<LengthPercentageAuto> for Dimension {
222    fn from(input: LengthPercentageAuto) -> Self {
223        Self(input.0)
224    }
225}
226
227impl Dimension {
228    /// An absolute length in some abstract units. Users of Taffy may define what they correspond
229    /// to in their application (pixels, logical pixels, mm, etc) as they see fit.
230    #[inline(always)]
231    pub const fn length(val: f32) -> Self {
232        Self(CompactLength::length(val))
233    }
234
235    /// A percentage length relative to the size of the containing block.
236    ///
237    /// **NOTE: percentages are represented as a f32 value in the range [0.0, 1.0] NOT the range [0.0, 100.0]**
238    #[inline(always)]
239    pub const fn percent(val: f32) -> Self {
240        Self(CompactLength::percent(val))
241    }
242
243    /// The dimension should be automatically computed according to algorithm-specific rules
244    /// regarding the default size of boxes.
245    #[inline(always)]
246    pub const fn auto() -> Self {
247        Self(CompactLength::auto())
248    }
249
250    /// A `calc()` value. The value passed here is treated as an opaque handle to
251    /// the actual calc representation and may be a pointer, index, etc.
252    ///
253    /// The low 3 bits are used as a tag value and will be returned as 0.
254    #[inline]
255    #[cfg(feature = "calc")]
256    pub fn calc(ptr: *const ()) -> Self {
257        Self(CompactLength::calc(ptr))
258    }
259
260    /// Create a LengthPercentageAuto from a raw `CompactLength`.
261    /// # Safety
262    /// CompactLength must represent a valid variant for LengthPercentageAuto
263    #[allow(unsafe_code)]
264    pub unsafe fn from_raw(val: CompactLength) -> Self {
265        Self(val)
266    }
267
268    /// Get the underlying `CompactLength` representation of the value
269    pub fn into_raw(self) -> CompactLength {
270        self.0
271    }
272
273    /// Get Length value if value is Length variant
274    #[cfg(feature = "grid")]
275    pub fn into_option(self) -> Option<f32> {
276        match self.0.tag() {
277            CompactLength::LENGTH_TAG => Some(self.0.value()),
278            _ => None,
279        }
280    }
281    /// Returns true if value is Auto
282    #[inline(always)]
283    pub fn is_auto(self) -> bool {
284        self.0.is_auto()
285    }
286
287    /// Get the raw `CompactLength` tag
288    pub fn tag(self) -> usize {
289        self.0.tag()
290    }
291
292    /// Get the raw `CompactLength` value for non-calc variants that have a numeric parameter
293    pub fn value(self) -> f32 {
294        self.0.value()
295    }
296}
297
298#[cfg(feature = "serde")]
299impl<'de> serde::Deserialize<'de> for Dimension {
300    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
301    where
302        D: serde::Deserializer<'de>,
303    {
304        let inner = CompactLength::deserialize(deserializer)?;
305        // Note: validation intentionally excludes the CALC_TAG as deserializing calc() values is not supported
306        if matches!(inner.tag(), CompactLength::LENGTH_TAG | CompactLength::PERCENT_TAG | CompactLength::AUTO_TAG) {
307            Ok(Self(inner))
308        } else {
309            Err(serde::de::Error::custom("Invalid tag"))
310        }
311    }
312}
313
314impl Rect<Dimension> {
315    /// Create a new Rect with length values
316    #[must_use]
317    pub const fn from_length(start: f32, end: f32, top: f32, bottom: f32) -> Self {
318        Rect {
319            left: Dimension(CompactLength::length(start)),
320            right: Dimension(CompactLength::length(end)),
321            top: Dimension(CompactLength::length(top)),
322            bottom: Dimension(CompactLength::length(bottom)),
323        }
324    }
325
326    /// Create a new Rect with percentage values
327    #[must_use]
328    pub const fn from_percent(start: f32, end: f32, top: f32, bottom: f32) -> Self {
329        Rect {
330            left: Dimension(CompactLength::percent(start)),
331            right: Dimension(CompactLength::percent(end)),
332            top: Dimension(CompactLength::percent(top)),
333            bottom: Dimension(CompactLength::percent(bottom)),
334        }
335    }
336}