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}