Skip to main content

iced_core/
length.rs

1//! Describe amounts of space accurately.
2use crate::Pixels;
3
4/// The strategy used to fill space in a specific dimension.
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum Length {
7    /// Fill the minimum amount of space based on the intrinsic size of the
8    /// element; normally defined by its contents.
9    ///
10    /// This is the default sizing strategy of most widgets.
11    Fit,
12
13    /// Fill all the remaining space.
14    Fill,
15
16    /// Fill a portion of the remaining space relative to other elements.
17    ///
18    /// Let's say we have two elements: one with `FillPortion(2)` and one with
19    /// `FillPortion(3)`. The first will get 2 portions of the available space,
20    /// while the second one would get 3.
21    ///
22    /// `Length::Fill` is equivalent to `Length::FillPortion(1)`.
23    FillPortion(u16),
24
25    /// Fill the least amount of space; compressing contents if possible.
26    Shrink,
27
28    /// Fill a fixed amount of space in pixels.
29    Fixed(f32),
30
31    /// Fill a certain amount of space inside the given [`Bounds`] with some [`Sizing`] strategy.
32    Bounded {
33        /// The [`Bounds`] of space that can be filled.
34        bounds: Bounds,
35        /// The [`Sizing`] strategy with which the [`Bounds`] should be filled.
36        sizing: Sizing,
37    },
38
39    /// Fill the remaining space like [`Fill`](Self::Fill), but subject to a single
40    /// open-ended [`Constraint`].
41    Fluid(Constraint),
42}
43
44#[derive(Debug, Clone, Copy, PartialEq)]
45/// The limit of a [`Fluid`](Length::Fluid) length.
46pub enum Constraint {
47    /// Fill available space, but never shrinks below the given amount.
48    Min(f32),
49    /// Fill available space, but never beyond the size the element resolves to on its own;
50    /// releasing any share it doesn't use back to its siblings.
51    Max,
52}
53
54impl Constraint {
55    fn stack(self, other: Self) -> Self {
56        match (self, other) {
57            (Constraint::Min(a), Constraint::Min(b)) => Self::Min(a + b),
58            (Constraint::Min(min), Constraint::Max) | (Constraint::Max, Constraint::Min(min)) => {
59                Constraint::Min(min)
60            }
61            (Constraint::Max, Constraint::Max) => Self::Max,
62        }
63    }
64
65    fn cross(self, other: Self) -> Self {
66        match (self, other) {
67            (Constraint::Min(a), Constraint::Min(b)) => Self::Min(a.max(b)),
68            (Constraint::Min(min), Constraint::Max) | (Constraint::Max, Constraint::Min(min)) => {
69                Constraint::Min(min)
70            }
71            (Constraint::Max, Constraint::Max) => Self::Max,
72        }
73    }
74}
75
76/// The space limits of a [`Bounded`](Length::Bounded) length.
77#[derive(Debug, Clone, Copy, PartialEq)]
78pub enum Bounds {
79    /// The length must be at least a certain amount of pixels.
80    Min(f32),
81    /// The length must not exceed a certain amount of pixels.
82    Max(f32),
83    /// The length must be inside a range of pixels.
84    Both {
85        /// The minimum boundary.
86        min: f32,
87        /// The maximum boundary.
88        max: f32,
89    },
90}
91
92impl Bounds {
93    /// Applies a new minimum boundary to the current [`Bounds`].
94    pub fn min(self, min: f32) -> Self {
95        match self {
96            Self::Min(_) => Self::Min(min),
97            Self::Max(max) | Self::Both { max, .. } => Self::Both { min, max },
98        }
99    }
100
101    /// Applies a new maximum boundary to the current [`Bounds`].
102    pub fn max(self, max: f32) -> Self {
103        match self {
104            Self::Max(_) => Self::Max(max),
105            Self::Min(min) | Self::Both { min, .. } => Self::Both { min, max },
106        }
107    }
108
109    /// Returns a [`Constraint`] that represents the current [`Bounds`].
110    pub fn constraint(self) -> Constraint {
111        match self {
112            Bounds::Min(min) | Bounds::Both { min, .. } => Constraint::Min(min),
113            Bounds::Max(_) => Constraint::Max,
114        }
115    }
116}
117
118/// The growth strategy of a [`Bounded`](Length::Bounded) length.
119#[derive(Debug, Clone, Copy, PartialEq)]
120pub enum Sizing {
121    /// Equivalent to [`Length::Fit`].
122    Fit,
123    /// Equivalent to [`Length::FillPortion`].
124    Fill(u16),
125    /// Equivalent to [`Length::Shrink`].
126    Shrink,
127}
128
129impl Length {
130    /// Returns a new [`Bounded`](Self::Bounded) length with the given minimum bounds.
131    pub fn min(self, min: impl Into<Pixels>) -> Self {
132        let min = min.into().0;
133
134        let with = match self {
135            Self::Fit | Self::Fluid(_) => Sizing::Fit,
136            Self::Fill => Sizing::Fill(1),
137            Self::FillPortion(factor) => Sizing::Fill(factor),
138            Self::Shrink => Sizing::Shrink,
139            Self::Fixed(_) => return self,
140            Self::Bounded {
141                bounds,
142                sizing: with,
143            } => {
144                return Self::Bounded {
145                    bounds: bounds.min(min),
146                    sizing: with,
147                };
148            }
149        };
150
151        Self::Bounded {
152            bounds: Bounds::Min(min),
153            sizing: with,
154        }
155    }
156
157    /// Returns a new [`Bounded`](Self::Bounded) length with the given maximum bounds.
158    pub fn max(self, max: impl Into<Pixels>) -> Self {
159        let max = max.into().0;
160
161        let with = match self {
162            Self::Fit | Self::Fluid(_) => Sizing::Fit,
163            Self::Fill => Sizing::Fill(1),
164            Self::FillPortion(factor) => Sizing::Fill(factor),
165            Self::Shrink => Sizing::Shrink,
166            Self::Fixed(_) => return self,
167            Self::Bounded {
168                bounds,
169                sizing: with,
170            } => {
171                return Self::Bounded {
172                    bounds: bounds.max(max),
173                    sizing: with,
174                };
175            }
176        };
177
178        Self::Bounded {
179            bounds: Bounds::Max(max),
180            sizing: with,
181        }
182    }
183
184    /// Returns the _fill factor_ of the [`Length`].
185    ///
186    /// The _fill factor_ is a relative unit describing how much of the
187    /// remaining space should be filled when compared to other elements. It
188    /// is only meant to be used by layout engines.
189    pub fn fill_factor(&self) -> u16 {
190        match self {
191            Length::Fill => 1,
192            Length::FillPortion(factor)
193            | Length::Bounded {
194                sizing: Sizing::Fill(factor),
195                ..
196            } => *factor,
197            Length::Fluid(_) => 1,
198            Length::Shrink | Length::Fit | Length::Fixed(_) | Length::Bounded { .. } => 0,
199        }
200    }
201
202    /// Returns `true` if the [`Length`] is either [`Length::Fill`] or
203    /// [`Length::FillPortion`].
204    pub fn is_fill(&self) -> bool {
205        self.fill_factor() != 0
206    }
207
208    /// Returns `true` if the [`Length`] is [`Fit`](Self::Fit).
209    pub fn is_fit(&self) -> bool {
210        matches!(self, Self::Fit)
211    }
212
213    /// Stacks the constraints of the current [`Length`] with the given one, if applicable.
214    ///
215    /// Specifically, minimum constraints will be _added_ together and accumulated.
216    ///
217    /// You should use this when a container lays out multiple elements along a given axis and
218    /// need to inherit their constraints in the _main_ axis.
219    pub fn stack(self, other: Length) -> Self {
220        self.merge_with(other, Constraint::stack)
221    }
222
223    /// Crosses the constraints of the current [`Length`] with the given one, if applicable.
224    ///
225    /// Specifically, minimum constraints will be compared and the _maximum_ will be returned.
226    ///
227    /// You should use this when a container lays out multiple elements along a given axis and
228    /// need to inherit their constraints in the _cross_ axis.
229    pub fn cross(self, other: Length) -> Self {
230        self.merge_with(other, Constraint::cross)
231    }
232
233    fn merge_with(self, other: Self, merge: impl Fn(Constraint, Constraint) -> Constraint) -> Self {
234        match (self, other) {
235            // Shrink, Fixed, and Fill are unmergeable
236            (Length::Shrink | Length::Fixed(_) | Length::Fill | Length::FillPortion(_), _) => self,
237
238            // Fluid elements are merged
239            (Length::Fluid(a), Length::Fluid(b)) => Length::Fluid(merge(a, b)),
240            (
241                Length::Fluid(a),
242                Length::Bounded {
243                    bounds,
244                    sizing: Sizing::Fill(_),
245                },
246            ) => Length::Fluid(merge(a, bounds.constraint())),
247            (Length::Fluid(constraint), _) => Length::Fluid(constraint),
248
249            (
250                Length::Bounded {
251                    bounds,
252                    sizing: Sizing::Fit,
253                },
254                Length::Fill
255                | Length::FillPortion(_)
256                | Length::Bounded {
257                    sizing: Sizing::Fill(_),
258                    ..
259                },
260            ) => Length::Bounded {
261                bounds,
262                sizing: Sizing::Fill(1),
263            },
264            (
265                Length::Bounded {
266                    bounds,
267                    sizing: with,
268                },
269                _,
270            ) => Length::Bounded {
271                bounds,
272                sizing: with,
273            },
274
275            // Fluid and bounded constraints must be propagated
276            (
277                _,
278                Length::Bounded {
279                    bounds,
280                    sizing: Sizing::Fill(_),
281                },
282            ) => Length::Fluid(bounds.constraint()),
283            (_, Length::Fluid(constraint)) => Length::Fluid(constraint),
284
285            // Fill wins over Fit
286            (_, Length::Fill | Length::FillPortion(_)) => Length::Fill,
287
288            // Fall back to Fit
289            _ => Length::Fit,
290        }
291    }
292}
293
294impl From<Pixels> for Length {
295    fn from(amount: Pixels) -> Self {
296        Self::Fixed(f32::from(amount))
297    }
298}
299
300impl From<f32> for Length {
301    fn from(amount: f32) -> Self {
302        Self::Fixed(amount)
303    }
304}
305
306impl From<u32> for Length {
307    fn from(units: u32) -> Self {
308        Self::Fixed(units as f32)
309    }
310}