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    min: Size,
9    max: Size,
10    compression: Size<bool>,
11}
12
13impl Limits {
14    /// No limits
15    pub const NONE: Limits = Limits {
16        min: Size::ZERO,
17        max: Size::INFINITE,
18        compression: Size::new(false, false),
19    };
20
21    /// Creates new [`Limits`] with the given minimum and maximum [`Size`].
22    pub const fn new(min: Size, max: Size) -> Limits {
23        Limits::with_compression(min, max, Size::new(false, false))
24    }
25
26    /// Creates new [`Limits`] with the given minimun and maximum [`Size`], and
27    /// whether fluid lengths should be compressed to intrinsic dimensions.
28    pub const fn with_compression(min: Size, max: Size, compress: Size<bool>) -> Self {
29        Limits {
30            min,
31            max,
32            compression: compress,
33        }
34    }
35
36    /// Returns the minimum [`Size`] of the [`Limits`].
37    pub fn min(&self) -> Size {
38        self.min
39    }
40
41    /// Returns the maximum [`Size`] of the [`Limits`].
42    pub fn max(&self) -> Size {
43        self.max
44    }
45
46    /// Returns the compression of the [`Limits`].
47    pub fn compression(&self) -> Size<bool> {
48        self.compression
49    }
50
51    /// Applies a width constraint to the current [`Limits`].
52    pub fn width(mut self, width: impl Into<Length>) -> Limits {
53        match width.into() {
54            Length::Shrink => {
55                self.compression.width = true;
56            }
57            Length::Fit | Length::Fluid(_) => {
58                self.compression.width = false;
59            }
60            Length::Fixed(amount) => {
61                let new_width = amount.min(self.max.width).max(self.min.width);
62
63                self.min.width = new_width;
64                self.max.width = new_width;
65                self.compression.width = false;
66            }
67            Length::Bounded { bounds, sizing } => {
68                match bounds {
69                    length::Bounds::Min(min) => {
70                        self.min.width = min.min(self.max.width).max(self.min.width);
71                    }
72                    length::Bounds::Max(max) => {
73                        self.max.width = max.min(self.max.width).max(self.min.width);
74                    }
75                    length::Bounds::Both { min, max } => {
76                        self.min.width = min.min(self.max.width).max(self.min.width);
77                        self.max.width = max.min(self.max.width).max(self.min.width);
78                    }
79                }
80
81                match sizing {
82                    length::Sizing::Shrink => {
83                        self.compression.width = true;
84                    }
85                    length::Sizing::Fit => {
86                        self.compression.width = false;
87                    }
88                    length::Sizing::Fill(_) => {}
89                }
90            }
91            Length::Fill | Length::FillPortion(_) => {}
92        }
93
94        self
95    }
96
97    /// Applies a height constraint to the current [`Limits`].
98    pub fn height(mut self, height: impl Into<Length>) -> Limits {
99        match height.into() {
100            Length::Shrink => {
101                self.compression.height = true;
102            }
103            Length::Fit | Length::Fluid(_) => {
104                self.compression.height = false;
105            }
106            Length::Fixed(amount) => {
107                let new_height = amount.min(self.max.height).max(self.min.height);
108
109                self.min.height = new_height;
110                self.max.height = new_height;
111                self.compression.height = false;
112            }
113            Length::Bounded { bounds, sizing } => {
114                match bounds {
115                    length::Bounds::Min(min) => {
116                        self.min.height = min.min(self.max.height).max(self.min.height);
117                    }
118                    length::Bounds::Max(max) => {
119                        self.max.height = max.min(self.max.height).max(self.min.height);
120                    }
121                    length::Bounds::Both { min, max } => {
122                        self.min.height = min.min(self.max.height).max(self.min.height);
123                        self.max.height = max.min(self.max.height).max(self.min.height);
124                    }
125                }
126
127                match sizing {
128                    length::Sizing::Shrink => {
129                        self.compression.height = true;
130                    }
131                    length::Sizing::Fit => {
132                        self.compression.height = false;
133                    }
134                    length::Sizing::Fill(_) => {}
135                }
136            }
137            Length::Fill | Length::FillPortion(_) => {}
138        }
139
140        self
141    }
142
143    /// Shrinks the current [`Limits`] by the given [`Size`].
144    pub fn shrink(&self, size: impl Into<Size>) -> Limits {
145        let size = size.into();
146
147        let min = Size::new(
148            (self.min().width - size.width).max(0.0),
149            (self.min().height - size.height).max(0.0),
150        );
151
152        let max = Size::new(
153            (self.max().width - size.width).max(0.0),
154            (self.max().height - size.height).max(0.0),
155        );
156
157        Limits {
158            min,
159            max,
160            compression: self.compression,
161        }
162    }
163
164    /// Removes the minimum [`Size`] constraint for the current [`Limits`].
165    pub fn loose(&self) -> Limits {
166        Limits {
167            min: Size::ZERO,
168            max: self.max,
169            compression: self.compression,
170        }
171    }
172
173    /// Computes the resulting [`Size`] that fits the [`Limits`] given
174    /// some width and height requirements and the intrinsic size of
175    /// some content.
176    pub fn resolve(
177        &self,
178        width: impl Into<Length>,
179        height: impl Into<Length>,
180        intrinsic_size: Size,
181    ) -> Size {
182        Size::new(
183            self.resolve_width(width, intrinsic_size.width),
184            self.resolve_height(height, intrinsic_size.height),
185        )
186    }
187
188    /// [Resolves](Self::resolve) only the width of the [`Limits`].
189    pub fn resolve_width(&self, width: impl Into<Length>, intrinsic_width: f32) -> f32 {
190        match width.into() {
191            Length::Fill
192            | Length::FillPortion(_)
193            | Length::Bounded {
194                sizing: length::Sizing::Fill(_),
195                ..
196            } if !self.compression.width => self.max.width,
197            Length::Fixed(amount) => amount.min(self.max.width).max(self.min.width),
198            _ => intrinsic_width.min(self.max.width).max(self.min.width),
199        }
200    }
201
202    /// [Resolves](Self::resolve) only the height of the [`Limits`].
203    pub fn resolve_height(&self, height: impl Into<Length>, intrinsic_height: f32) -> f32 {
204        match height.into() {
205            Length::Fill
206            | Length::FillPortion(_)
207            | Length::Bounded {
208                sizing: length::Sizing::Fill(_),
209                ..
210            } if !self.compression.height => self.max.height,
211            Length::Fixed(amount) => amount.min(self.max.height).max(self.min.height),
212            _ => intrinsic_height.min(self.max.height).max(self.min.height),
213        }
214    }
215}