Skip to main content

anathema_widgets/layout/
constraints.rs

1use anathema_geometry::Region;
2
3use crate::layout::Size;
4
5/// `Constraints` are used to ensure that a widget doesn't size it self outside of a set of given bounds.
6/// A constraint can be tight, meaning then minimum and maximum width / height are the same.
7#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8pub struct Constraints {
9    /// Minimum width.
10    pub min_width: u16,
11    /// Minimum height.
12    pub min_height: u16,
13    /// Max width.
14    max_width: u16,
15    /// Max height.
16    max_height: u16,
17}
18
19impl Constraints {
20    pub const MAX: Self = Self {
21        min_width: u16::MAX,
22        min_height: u16::MAX,
23        max_width: u16::MAX,
24        max_height: u16::MAX,
25    };
26    pub const ZERO: Self = Self {
27        min_width: 0,
28        min_height: 0,
29        max_width: 0,
30        max_height: 0,
31    };
32
33    pub fn max_height(&self) -> u16 {
34        self.max_height
35    }
36
37    pub fn max_width(&self) -> u16 {
38        self.max_width
39    }
40
41    /// Subtract `width` from the max width, as long
42    /// as the width isn't unbounded.
43    pub fn sub_max_width(&mut self, width: u16) {
44        if self.max_width < u16::MAX {
45            self.max_width = self.max_width.saturating_sub(width);
46            self.min_width = self.min_width.min(self.max_width);
47        }
48    }
49
50    /// Subtract `height` from the max height, as long
51    /// as the height isn't unbounded.
52    pub fn sub_max_height(&mut self, height: u16) {
53        if self.max_height < u16::MAX {
54            self.max_height = self.max_height.saturating_sub(height);
55            self.min_height = self.min_height.min(self.max_height);
56        }
57    }
58
59    /// Create a set of constraints with a given max width / height.
60    /// If `None` is passed for either `max_width` and / or `max_height` then this is qualified as
61    /// "unbounded" constraints.
62    ///
63    /// The `min_width` and `min_height` are zero by default.
64    ///
65    /// If the `min_width` and the `max_width` are the same the constraints are considered "tight".
66    pub fn new(max_width: impl Into<Option<u16>>, max_height: impl Into<Option<u16>>) -> Self {
67        let max_width = max_width.into().unwrap_or(u16::MAX);
68        let max_height = max_height.into().unwrap_or(u16::MAX);
69        Self {
70            min_width: 0,
71            min_height: 0,
72            max_width,
73            max_height,
74        }
75    }
76
77    /// Create unbounded constraints.
78    pub fn unbounded() -> Self {
79        Self {
80            min_width: 0,
81            min_height: 0,
82            max_width: u16::MAX,
83            max_height: u16::MAX,
84        }
85    }
86
87    /// Create unbounded height
88    pub fn unbound_height(&mut self) {
89        self.max_height = u16::MAX;
90    }
91
92    /// Create unbounded width
93    pub fn unbound_width(&mut self) {
94        self.max_width = u16::MAX;
95    }
96
97    /// Returns true if the width and height is unbounded.
98    pub fn is_unbounded(&self) -> bool {
99        self.is_width_unbounded() && self.is_height_unbounded()
100    }
101
102    /// Returns true if the width is unbounded.
103    pub fn is_width_unbounded(&self) -> bool {
104        self.max_width == u16::MAX
105    }
106
107    /// Returns true if the height is unbounded.
108    pub fn is_height_unbounded(&self) -> bool {
109        self.max_height == u16::MAX
110    }
111
112    /// Returns true if the `min_width` and `max_width` are the same.
113    pub fn is_width_tight(&self) -> bool {
114        self.max_width == self.min_width
115    }
116
117    /// Returns true if the `min_height` and `max_height` are the same.
118    pub fn is_height_tight(&self) -> bool {
119        self.max_height == self.min_height
120    }
121
122    /// Make the width constraint tight.
123    /// ```
124    /// # use anathema_widgets::layout::Constraints;
125    /// let mut constraints = Constraints::new(10, 10);
126    /// constraints.make_width_tight(constraints.max_width());
127    /// # assert_eq!(constraints.min_width, constraints.max_width());
128    /// ```
129    pub fn make_width_tight(&mut self, width: u16) {
130        self.max_width = self.max_width.min(width);
131        self.min_width = self.max_width;
132    }
133
134    /// Make the height constraint tight.
135    /// ```
136    /// # use anathema_widgets::layout::Constraints;
137    /// let mut constraints = Constraints::new(10, 10);
138    /// constraints.make_height_tight(constraints.max_height());
139    /// # assert_eq!(constraints.min_height, constraints.max_height());
140    /// ```
141    pub fn make_height_tight(&mut self, height: u16) {
142        self.max_height = self.max_height.min(height);
143        self.min_height = self.max_height;
144    }
145
146    pub fn expand_horz(&mut self, mut size: Size) -> Size {
147        size.width = self.max_width;
148        size
149    }
150
151    pub fn expand_vert(&mut self, mut size: Size) -> Size {
152        size.height = self.max_height;
153        size
154    }
155
156    pub fn expand_all(&mut self, mut size: Size) -> Size {
157        size = self.expand_horz(size);
158        self.expand_vert(size)
159    }
160
161    /// This function does not verify anything, but simply
162    /// sets the max width.
163    /// There is no check to see if the max width is smaller than the min width here.
164    pub fn set_max_width(&mut self, width: u16) {
165        self.max_width = width;
166    }
167
168    /// This function does not verify anything, but simply
169    /// sets the max height.
170    /// There is no check to see if the max height is smaller than the min height here.
171    pub fn set_max_height(&mut self, height: u16) {
172        self.max_height = height;
173    }
174
175    pub fn div_assign_max_width(mut self, count: u16, overflow: u16) -> Self {
176        let width = self.max_width / count + overflow;
177        self.make_width_tight(width);
178        self
179    }
180
181    pub fn div_assign_max_height(mut self, count: u16, overflow: u16) -> Self {
182        let height = self.max_height / count + overflow;
183        self.make_height_tight(height);
184        self
185    }
186
187    /// If either the max width or max height are
188    /// zero then nothing can be laid out within
189    /// the given constraint.
190    pub fn has_zero_dimension(&self) -> bool {
191        self.max_width == 0 || self.max_height == 0
192    }
193
194    /// Get a size from the max width / height
195    pub fn max_size(&self) -> Size {
196        (self.max_width, self.max_height).into()
197    }
198}
199
200impl From<Size> for Constraints {
201    fn from(value: Size) -> Self {
202        Self::new(value.width, value.height)
203    }
204}
205
206impl From<Region> for Constraints {
207    fn from(value: Region) -> Self {
208        let width = value.to.x - value.from.x;
209        let height = value.to.y - value.from.y;
210        Self::new(width as u16, height as u16)
211    }
212}
213
214impl Default for Constraints {
215    fn default() -> Self {
216        Self::ZERO
217    }
218}