Skip to main content

iced_core/layout/
limits.rs

1#![allow(clippy::manual_clamp)]
2use crate::length;
3use crate::{Length, Size};
4
5/// A set of size constraints for layouting.
6#[derive(Debug, Clone, Copy, PartialEq)]
7pub struct Limits {
8    /// The minimum bounds.
9    pub min: Size,
10    /// The maximum bounds.
11    pub max: Size,
12    /// Whether fluid lengths should be compressed to intrinsic dimensions.
13    pub compression: Size<bool>,
14    /// Whether intrinsic dimensions can exceed maximum bounds. The maximum
15    /// boundary becomes a hint in this case.
16    pub infinite: Size<bool>,
17}
18
19impl Limits {
20    /// Creates new [`Limits`] with the given minimum and maximum [`Size`].
21    pub const fn new(min: Size, max: Size) -> Limits {
22        Limits::with_flags(min, max, Size::new(false, false), Size::new(false, false))
23    }
24
25    /// Creates new [`Limits`] with the given minimum and maximum [`Size`],
26    /// whether fluid lengths should be compressed to intrinsic dimensions,
27    /// and whether the upper bounds may grow indefinitely.
28    pub const fn with_flags(
29        min: Size,
30        max: Size,
31        compression: Size<bool>,
32        infinite: Size<bool>,
33    ) -> Self {
34        Limits {
35            min,
36            max,
37            compression,
38            infinite,
39        }
40    }
41
42    /// Returns the maximum [`Size`] of the [`Limits`].
43    ///
44    /// On axes with [`infinite`](Self::infinite) bounds, the value is
45    /// [`f32::INFINITY`], as the maximum boundary becomes a hint in this
46    /// case.
47    pub fn bounds(&self) -> Size {
48        Size::new(
49            if self.infinite.width {
50                f32::INFINITY
51            } else {
52                self.max.width
53            },
54            if self.infinite.height {
55                f32::INFINITY
56            } else {
57                self.max.height
58            },
59        )
60    }
61
62    /// Applies a width constraint to the current [`Limits`].
63    pub fn width(mut self, width: impl Into<Length>) -> Limits {
64        match width.into() {
65            Length::Shrink => {
66                self.compression.width = true;
67            }
68            Length::Fit | Length::Fluid(_) => {
69                self.compression.width = false;
70            }
71            Length::Fixed(amount) => {
72                let new_width = if self.infinite.width {
73                    amount
74                } else {
75                    amount.min(self.max.width)
76                }
77                .max(self.min.width);
78
79                self.min.width = new_width;
80                self.max.width = new_width;
81                self.compression.width = false;
82                self.infinite.width = false;
83            }
84            Length::Bounded { bounds, sizing } => {
85                match bounds {
86                    length::Bounds::Min(min) => {
87                        self.min.width = if self.infinite.width {
88                            min
89                        } else {
90                            min.min(self.max.width)
91                        };
92                    }
93                    length::Bounds::Max(max) => {
94                        self.max.width = if self.infinite.width {
95                            max
96                        } else {
97                            max.min(self.max.width)
98                        }
99                        .max(self.min.width);
100
101                        self.infinite.width = false;
102                    }
103                    length::Bounds::Both { min, max } => {
104                        self.min.width = min.min(self.max.width).max(self.min.width);
105                        self.max.width = max.min(self.max.width).max(self.min.width);
106                    }
107                }
108
109                match sizing {
110                    length::Sizing::Shrink => {
111                        self.compression.width = true;
112                    }
113                    length::Sizing::Fit => {
114                        self.compression.width = false;
115                    }
116                    length::Sizing::Fill(_) => {}
117                }
118            }
119            Length::Fill | Length::FillPortion(_) => {}
120        }
121
122        self
123    }
124
125    /// Applies a height constraint to the current [`Limits`].
126    pub fn height(mut self, height: impl Into<Length>) -> Limits {
127        match height.into() {
128            Length::Shrink => {
129                self.compression.height = true;
130            }
131            Length::Fit | Length::Fluid(_) => {
132                self.compression.height = false;
133            }
134            Length::Fixed(amount) => {
135                let new_height = if self.infinite.height {
136                    amount
137                } else {
138                    amount.min(self.max.height)
139                }
140                .max(self.min.height);
141
142                self.min.height = new_height;
143                self.max.height = new_height;
144                self.compression.height = false;
145                self.infinite.height = false;
146            }
147            Length::Bounded { bounds, sizing } => {
148                match bounds {
149                    length::Bounds::Min(min) => {
150                        self.min.height = if self.infinite.height {
151                            min
152                        } else {
153                            min.min(self.max.height)
154                        };
155                    }
156                    length::Bounds::Max(max) => {
157                        self.max.height = if self.infinite.height {
158                            max
159                        } else {
160                            max.min(self.max.height)
161                        }
162                        .max(self.min.height);
163
164                        self.infinite.height = false;
165                    }
166                    length::Bounds::Both { min, max } => {
167                        self.min.height = min.min(self.max.height).max(self.min.height);
168                        self.max.height = max.min(self.max.height).max(self.min.height);
169                    }
170                }
171
172                match sizing {
173                    length::Sizing::Shrink => {
174                        self.compression.height = true;
175                    }
176                    length::Sizing::Fit => {
177                        self.compression.height = false;
178                    }
179                    length::Sizing::Fill(_) => {}
180                }
181            }
182            Length::Fill | Length::FillPortion(_) => {}
183        }
184
185        self
186    }
187
188    /// Shrinks the current [`Limits`] by the given [`Size`].
189    pub fn shrink(&self, size: impl Into<Size>) -> Limits {
190        let size = size.into();
191
192        let min = Size::new(
193            (self.min.width - size.width).max(0.0),
194            (self.min.height - size.height).max(0.0),
195        );
196
197        let max = Size::new(
198            (self.max.width - size.width).max(0.0),
199            (self.max.height - size.height).max(0.0),
200        );
201
202        Limits {
203            min,
204            max,
205            compression: self.compression,
206            infinite: self.infinite,
207        }
208    }
209
210    /// Removes the minimum [`Size`] constraint for the current [`Limits`].
211    pub fn loose(&self) -> Limits {
212        Limits {
213            min: Size::ZERO,
214            max: self.max,
215            compression: self.compression,
216            infinite: self.infinite,
217        }
218    }
219
220    /// Computes the resulting [`Size`] that fits the [`Limits`] given
221    /// some width and height requirements and the intrinsic size of
222    /// some content.
223    pub fn resolve(
224        &self,
225        width: impl Into<Length>,
226        height: impl Into<Length>,
227        intrinsic_size: Size,
228    ) -> Size {
229        Size::new(
230            self.resolve_width(width, intrinsic_size.width),
231            self.resolve_height(height, intrinsic_size.height),
232        )
233    }
234
235    /// [Resolves](Self::resolve) only the width of the [`Limits`].
236    pub fn resolve_width(&self, width: impl Into<Length>, intrinsic_width: f32) -> f32 {
237        resolve(
238            self.min.width,
239            self.max.width,
240            self.compression.width,
241            self.infinite.width,
242            width.into(),
243            intrinsic_width,
244        )
245    }
246
247    /// [Resolves](Self::resolve) only the height of the [`Limits`].
248    pub fn resolve_height(&self, height: impl Into<Length>, intrinsic_height: f32) -> f32 {
249        resolve(
250            self.min.height,
251            self.max.height,
252            self.compression.height,
253            self.infinite.height,
254            height.into(),
255            intrinsic_height,
256        )
257    }
258}
259
260fn resolve(
261    min: f32,
262    max: f32,
263    compression: bool,
264    infinite: bool,
265    length: Length,
266    intrinsic: f32,
267) -> f32 {
268    match length {
269        Length::Fill
270        | Length::FillPortion(_)
271        | Length::Bounded {
272            sizing: length::Sizing::Fill(_),
273            ..
274        } if !compression => if infinite { max.max(intrinsic) } else { max }.max(min),
275        Length::Fixed(amount) => if infinite { amount } else { amount.min(max) }.max(min),
276        _ => if infinite {
277            intrinsic
278        } else {
279            intrinsic.min(max)
280        }
281        .max(min),
282    }
283}