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            }
83            Length::Bounded { bounds, sizing } => {
84                match bounds {
85                    length::Bounds::Min(min) => {
86                        self.min.width = min.min(self.max.width).max(self.min.width);
87                    }
88                    length::Bounds::Max(max) => {
89                        self.max.width = if self.infinite.width {
90                            max
91                        } else {
92                            max.min(self.max.width)
93                        }
94                        .max(self.min.width);
95
96                        self.infinite.width = false;
97                    }
98                    length::Bounds::Both { min, max } => {
99                        self.min.width = min.min(self.max.width).max(self.min.width);
100                        self.max.width = max.min(self.max.width).max(self.min.width);
101                    }
102                }
103
104                match sizing {
105                    length::Sizing::Shrink => {
106                        self.compression.width = true;
107                    }
108                    length::Sizing::Fit => {
109                        self.compression.width = false;
110                    }
111                    length::Sizing::Fill(_) => {}
112                }
113            }
114            Length::Fill | Length::FillPortion(_) => {}
115        }
116
117        self
118    }
119
120    /// Applies a height constraint to the current [`Limits`].
121    pub fn height(mut self, height: impl Into<Length>) -> Limits {
122        match height.into() {
123            Length::Shrink => {
124                self.compression.height = true;
125            }
126            Length::Fit | Length::Fluid(_) => {
127                self.compression.height = false;
128            }
129            Length::Fixed(amount) => {
130                let new_height = if self.infinite.height {
131                    amount
132                } else {
133                    amount.min(self.max.height)
134                }
135                .max(self.min.height);
136
137                self.min.height = new_height;
138                self.max.height = new_height;
139                self.compression.height = false;
140            }
141            Length::Bounded { bounds, sizing } => {
142                match bounds {
143                    length::Bounds::Min(min) => {
144                        self.min.height = min.min(self.max.height).max(self.min.height);
145                    }
146                    length::Bounds::Max(max) => {
147                        self.max.height = if self.infinite.height {
148                            max
149                        } else {
150                            max.min(self.max.height)
151                        }
152                        .max(self.min.height);
153
154                        self.infinite.height = false;
155                    }
156                    length::Bounds::Both { min, max } => {
157                        self.min.height = min.min(self.max.height).max(self.min.height);
158                        self.max.height = max.min(self.max.height).max(self.min.height);
159                    }
160                }
161
162                match sizing {
163                    length::Sizing::Shrink => {
164                        self.compression.height = true;
165                    }
166                    length::Sizing::Fit => {
167                        self.compression.height = false;
168                    }
169                    length::Sizing::Fill(_) => {}
170                }
171            }
172            Length::Fill | Length::FillPortion(_) => {}
173        }
174
175        self
176    }
177
178    /// Shrinks the current [`Limits`] by the given [`Size`].
179    pub fn shrink(&self, size: impl Into<Size>) -> Limits {
180        let size = size.into();
181
182        let min = Size::new(
183            (self.min.width - size.width).max(0.0),
184            (self.min.height - size.height).max(0.0),
185        );
186
187        let max = Size::new(
188            (self.max.width - size.width).max(0.0),
189            (self.max.height - size.height).max(0.0),
190        );
191
192        Limits {
193            min,
194            max,
195            compression: self.compression,
196            infinite: self.infinite,
197        }
198    }
199
200    /// Removes the minimum [`Size`] constraint for the current [`Limits`].
201    pub fn loose(&self) -> Limits {
202        Limits {
203            min: Size::ZERO,
204            max: self.max,
205            compression: self.compression,
206            infinite: self.infinite,
207        }
208    }
209
210    /// Computes the resulting [`Size`] that fits the [`Limits`] given
211    /// some width and height requirements and the intrinsic size of
212    /// some content.
213    pub fn resolve(
214        &self,
215        width: impl Into<Length>,
216        height: impl Into<Length>,
217        intrinsic_size: Size,
218    ) -> Size {
219        Size::new(
220            self.resolve_width(width, intrinsic_size.width),
221            self.resolve_height(height, intrinsic_size.height),
222        )
223    }
224
225    /// [Resolves](Self::resolve) only the width of the [`Limits`].
226    pub fn resolve_width(&self, width: impl Into<Length>, intrinsic_width: f32) -> f32 {
227        resolve(
228            self.min.width,
229            self.max.width,
230            self.compression.width,
231            self.infinite.width,
232            width.into(),
233            intrinsic_width,
234        )
235    }
236
237    /// [Resolves](Self::resolve) only the height of the [`Limits`].
238    pub fn resolve_height(&self, height: impl Into<Length>, intrinsic_height: f32) -> f32 {
239        resolve(
240            self.min.height,
241            self.max.height,
242            self.compression.height,
243            self.infinite.height,
244            height.into(),
245            intrinsic_height,
246        )
247    }
248}
249
250fn resolve(
251    min: f32,
252    max: f32,
253    compression: bool,
254    infinite: bool,
255    length: Length,
256    intrinsic: f32,
257) -> f32 {
258    match length {
259        Length::Fill
260        | Length::FillPortion(_)
261        | Length::Bounded {
262            sizing: length::Sizing::Fill(_),
263            ..
264        } if !compression => if infinite { max.max(intrinsic) } else { max }.max(min),
265        Length::Fixed(amount) => if infinite { amount } else { amount.min(max) }.max(min),
266        _ => if infinite {
267            intrinsic
268        } else {
269            intrinsic.min(max)
270        }
271        .max(min),
272    }
273}