ailloli_ui_core/style/
length.rs

1//! CSS-like intrinsic, fixed, fill, and percentage axis lengths.
2
3/// CSS-like length for layout axes (width, height, min/max).
4///
5/// Possible values are intrinsic [`Length::Auto`], fixed [`Length::Px`], parent
6/// fill, and [`Length::Percent`].
7///
8/// # Examples
9///
10/// ```
11/// use ailloli_ui_core::style::Length;
12/// assert_eq!(Length::percent(50.0).resolve(200.0), Some(100.0));
13/// ```
14#[derive(Clone, Copy, Debug, Default, PartialEq)]
15pub enum Length {
16    /// Intrinsic size (children, text measure).
17    #[default]
18    Auto,
19    /// Fixed logical pixels; resolution floors negative values and NaN at zero.
20    /// Positive infinity remains infinite.
21    Px(f32),
22    /// Fill the parent on this axis when resolved via [`Length::resolve`].
23    ///
24    /// In a flex main axis, parent layout treats `Fill` as **remaining space**
25    /// (grow participant) instead of calling `resolve` with the full parent max.
26    Fill,
27    /// Fraction of parent available space.
28    ///
29    /// Direct construction stores the fraction verbatim; [`Length::percent`]
30    /// accepts either fractional or percentage-style input.
31    Percent(f32),
32}
33
34impl Length {
35    /// Creates a fixed logical-pixel length without immediate normalization.
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// use ailloli_ui_core::style::Length;
41    /// assert_eq!(Length::px(24.0), Length::Px(24.0));
42    /// ```
43    pub fn px(value: f32) -> Self {
44        Self::Px(value)
45    }
46
47    /// Fill parent on this axis (see [`Length::Fill`]).
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// use ailloli_ui_core::style::Length;
53    /// assert_eq!(Length::fill(), Length::Fill);
54    /// ```
55    pub fn fill() -> Self {
56        Self::Fill
57    }
58
59    /// Creates a percentage/fraction of parent available space.
60    ///
61    /// Values with absolute magnitude at most `1.0` are treated as fractions,
62    /// so `0.5` and `1.0` mean 50% and 100%. Larger magnitudes are divided by
63    /// 100, so `50.0` also means 50%. Negative fractions resolve to zero.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use ailloli_ui_core::style::Length;
69    /// assert_eq!(Length::percent(50.0), Length::Percent(0.5));
70    /// assert_eq!(Length::percent(0.5), Length::Percent(0.5));
71    /// ```
72    pub fn percent(value: f32) -> Self {
73        let fraction = if value.abs() <= 1.0 {
74            value
75        } else {
76            value / 100.0
77        };
78        Self::Percent(fraction)
79    }
80
81    /// `true` when this axis uses intrinsic sizing.
82    ///
83    /// # Examples
84    ///
85    /// ```
86    /// use ailloli_ui_core::style::Length;
87    /// assert!(Length::Auto.is_auto());
88    /// assert!(!Length::Fill.is_auto());
89    /// ```
90    pub fn is_auto(self) -> bool {
91        matches!(self, Self::Auto)
92    }
93
94    /// Resolves against parent `available` logical pixels.
95    ///
96    /// [`Self::Auto`] returns `None`. Fixed, fill, and percentage results are
97    /// floored at zero; they are not capped to `available`. Non-finite results
98    /// follow floating-point `max` semantics.
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use ailloli_ui_core::style::Length;
104    ///
105    /// assert_eq!(Length::Auto.resolve(200.0), None);
106    /// assert_eq!(Length::px(24.0).resolve(200.0), Some(24.0));
107    /// assert_eq!(Length::percent(50.0).resolve(200.0), Some(100.0));
108    /// assert_eq!(Length::fill().resolve(200.0), Some(200.0));
109    /// assert_eq!(Length::px(-1.0).resolve(200.0), Some(0.0));
110    /// ```
111    pub fn resolve(self, available: f32) -> Option<f32> {
112        match self {
113            Self::Auto => None,
114            Self::Px(value) => Some(value.max(0.0)),
115            Self::Fill => Some(available.max(0.0)),
116            Self::Percent(value) => Some((available * value).max(0.0)),
117        }
118    }
119}
120
121impl From<f32> for Length {
122    fn from(value: f32) -> Self {
123        Self::Px(value)
124    }
125}