ratatui/layout/rect/
iter.rs

1use crate::layout::{Position, Rect};
2
3/// An iterator over rows within a `Rect`.
4pub struct Rows {
5    /// The `Rect` associated with the rows.
6    rect: Rect,
7    /// The y coordinate of the row within the `Rect` when iterating forwards.
8    current_row_fwd: u16,
9    /// The y coordinate of the row within the `Rect` when iterating backwards.
10    current_row_back: u16,
11}
12
13impl Rows {
14    /// Creates a new `Rows` iterator.
15    pub const fn new(rect: Rect) -> Self {
16        Self {
17            rect,
18            current_row_fwd: rect.y,
19            current_row_back: rect.bottom(),
20        }
21    }
22}
23
24impl Iterator for Rows {
25    type Item = Rect;
26
27    /// Retrieves the next row within the `Rect`.
28    ///
29    /// Returns `None` when there are no more rows to iterate through.
30    fn next(&mut self) -> Option<Self::Item> {
31        if self.current_row_fwd >= self.current_row_back {
32            return None;
33        }
34        let row = Rect::new(self.rect.x, self.current_row_fwd, self.rect.width, 1);
35        self.current_row_fwd += 1;
36        Some(row)
37    }
38
39    fn size_hint(&self) -> (usize, Option<usize>) {
40        let start_count = self.current_row_fwd.saturating_sub(self.rect.top());
41        let end_count = self.rect.bottom().saturating_sub(self.current_row_back);
42        let count = self
43            .rect
44            .height
45            .saturating_sub(start_count)
46            .saturating_sub(end_count) as usize;
47        (count, Some(count))
48    }
49}
50
51impl DoubleEndedIterator for Rows {
52    /// Retrieves the previous row within the `Rect`.
53    ///
54    /// Returns `None` when there are no more rows to iterate through.
55    fn next_back(&mut self) -> Option<Self::Item> {
56        if self.current_row_back <= self.current_row_fwd {
57            return None;
58        }
59        self.current_row_back -= 1;
60        let row = Rect::new(self.rect.x, self.current_row_back, self.rect.width, 1);
61        Some(row)
62    }
63}
64
65/// An iterator over columns within a `Rect`.
66pub struct Columns {
67    /// The `Rect` associated with the columns.
68    rect: Rect,
69    /// The x coordinate of the column within the `Rect` when iterating forwards.
70    current_column_fwd: u16,
71    /// The x coordinate of the column within the `Rect` when iterating backwards.
72    current_column_back: u16,
73}
74
75impl Columns {
76    /// Creates a new `Columns` iterator.
77    pub const fn new(rect: Rect) -> Self {
78        Self {
79            rect,
80            current_column_fwd: rect.x,
81            current_column_back: rect.right(),
82        }
83    }
84}
85
86impl Iterator for Columns {
87    type Item = Rect;
88
89    /// Retrieves the next column within the `Rect`.
90    ///
91    /// Returns `None` when there are no more columns to iterate through.
92    fn next(&mut self) -> Option<Self::Item> {
93        if self.current_column_fwd >= self.current_column_back {
94            return None;
95        }
96        let column = Rect::new(self.current_column_fwd, self.rect.y, 1, self.rect.height);
97        self.current_column_fwd += 1;
98        Some(column)
99    }
100
101    fn size_hint(&self) -> (usize, Option<usize>) {
102        let start_count = self.current_column_fwd.saturating_sub(self.rect.left());
103        let end_count = self.rect.right().saturating_sub(self.current_column_back);
104        let count = self
105            .rect
106            .width
107            .saturating_sub(start_count)
108            .saturating_sub(end_count) as usize;
109        (count, Some(count))
110    }
111}
112
113impl DoubleEndedIterator for Columns {
114    /// Retrieves the previous column within the `Rect`.
115    ///
116    /// Returns `None` when there are no more columns to iterate through.
117    fn next_back(&mut self) -> Option<Self::Item> {
118        if self.current_column_back <= self.current_column_fwd {
119            return None;
120        }
121        self.current_column_back -= 1;
122        let column = Rect::new(self.current_column_back, self.rect.y, 1, self.rect.height);
123        Some(column)
124    }
125}
126
127/// An iterator over positions within a `Rect`.
128///
129/// The iterator will yield all positions within the `Rect` in a row-major order.
130pub struct Positions {
131    /// The `Rect` associated with the positions.
132    rect: Rect,
133    /// The current position within the `Rect`.
134    current_position: Position,
135}
136
137impl Positions {
138    /// Creates a new `Positions` iterator.
139    pub const fn new(rect: Rect) -> Self {
140        Self {
141            rect,
142            current_position: Position::new(rect.x, rect.y),
143        }
144    }
145}
146
147impl Iterator for Positions {
148    type Item = Position;
149
150    /// Retrieves the next position within the `Rect`.
151    ///
152    /// Returns `None` when there are no more positions to iterate through.
153    fn next(&mut self) -> Option<Self::Item> {
154        if self.current_position.y >= self.rect.bottom() {
155            return None;
156        }
157        let position = self.current_position;
158        self.current_position.x += 1;
159        if self.current_position.x >= self.rect.right() {
160            self.current_position.x = self.rect.x;
161            self.current_position.y += 1;
162        }
163        Some(position)
164    }
165
166    fn size_hint(&self) -> (usize, Option<usize>) {
167        let row_count = self.rect.bottom().saturating_sub(self.current_position.y);
168        if row_count == 0 {
169            return (0, Some(0));
170        }
171        let column_count = self.rect.right().saturating_sub(self.current_position.x);
172        // subtract 1 from the row count to account for the current row
173        let count = (row_count - 1)
174            .saturating_mul(self.rect.width)
175            .saturating_add(column_count) as usize;
176        (count, Some(count))
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn rows() {
186        let rect = Rect::new(0, 0, 2, 3);
187        let mut rows = Rows::new(rect);
188        assert_eq!(rows.size_hint(), (3, Some(3)));
189        assert_eq!(rows.next(), Some(Rect::new(0, 0, 2, 1)));
190        assert_eq!(rows.size_hint(), (2, Some(2)));
191        assert_eq!(rows.next(), Some(Rect::new(0, 1, 2, 1)));
192        assert_eq!(rows.size_hint(), (1, Some(1)));
193        assert_eq!(rows.next(), Some(Rect::new(0, 2, 2, 1)));
194        assert_eq!(rows.size_hint(), (0, Some(0)));
195        assert_eq!(rows.next(), None);
196        assert_eq!(rows.size_hint(), (0, Some(0)));
197        assert_eq!(rows.next_back(), None);
198        assert_eq!(rows.size_hint(), (0, Some(0)));
199    }
200
201    #[test]
202    fn rows_back() {
203        let rect = Rect::new(0, 0, 2, 3);
204        let mut rows = Rows::new(rect);
205        assert_eq!(rows.size_hint(), (3, Some(3)));
206        assert_eq!(rows.next_back(), Some(Rect::new(0, 2, 2, 1)));
207        assert_eq!(rows.size_hint(), (2, Some(2)));
208        assert_eq!(rows.next_back(), Some(Rect::new(0, 1, 2, 1)));
209        assert_eq!(rows.size_hint(), (1, Some(1)));
210        assert_eq!(rows.next_back(), Some(Rect::new(0, 0, 2, 1)));
211        assert_eq!(rows.size_hint(), (0, Some(0)));
212        assert_eq!(rows.next_back(), None);
213        assert_eq!(rows.size_hint(), (0, Some(0)));
214        assert_eq!(rows.next(), None);
215        assert_eq!(rows.size_hint(), (0, Some(0)));
216    }
217
218    #[test]
219    fn rows_meet_in_the_middle() {
220        let rect = Rect::new(0, 0, 2, 4);
221        let mut rows = Rows::new(rect);
222        assert_eq!(rows.size_hint(), (4, Some(4)));
223        assert_eq!(rows.next(), Some(Rect::new(0, 0, 2, 1)));
224        assert_eq!(rows.size_hint(), (3, Some(3)));
225        assert_eq!(rows.next_back(), Some(Rect::new(0, 3, 2, 1)));
226        assert_eq!(rows.size_hint(), (2, Some(2)));
227        assert_eq!(rows.next(), Some(Rect::new(0, 1, 2, 1)));
228        assert_eq!(rows.size_hint(), (1, Some(1)));
229        assert_eq!(rows.next_back(), Some(Rect::new(0, 2, 2, 1)));
230        assert_eq!(rows.size_hint(), (0, Some(0)));
231        assert_eq!(rows.next(), None);
232        assert_eq!(rows.size_hint(), (0, Some(0)));
233        assert_eq!(rows.next_back(), None);
234        assert_eq!(rows.size_hint(), (0, Some(0)));
235    }
236
237    #[test]
238    fn columns() {
239        let rect = Rect::new(0, 0, 3, 2);
240        let mut columns = Columns::new(rect);
241        assert_eq!(columns.size_hint(), (3, Some(3)));
242        assert_eq!(columns.next(), Some(Rect::new(0, 0, 1, 2)));
243        assert_eq!(columns.size_hint(), (2, Some(2)));
244        assert_eq!(columns.next(), Some(Rect::new(1, 0, 1, 2)));
245        assert_eq!(columns.size_hint(), (1, Some(1)));
246        assert_eq!(columns.next(), Some(Rect::new(2, 0, 1, 2)));
247        assert_eq!(columns.size_hint(), (0, Some(0)));
248        assert_eq!(columns.next(), None);
249        assert_eq!(columns.size_hint(), (0, Some(0)));
250        assert_eq!(columns.next_back(), None);
251        assert_eq!(columns.size_hint(), (0, Some(0)));
252    }
253
254    #[test]
255    fn columns_back() {
256        let rect = Rect::new(0, 0, 3, 2);
257        let mut columns = Columns::new(rect);
258        assert_eq!(columns.size_hint(), (3, Some(3)));
259        assert_eq!(columns.next_back(), Some(Rect::new(2, 0, 1, 2)));
260        assert_eq!(columns.size_hint(), (2, Some(2)));
261        assert_eq!(columns.next_back(), Some(Rect::new(1, 0, 1, 2)));
262        assert_eq!(columns.size_hint(), (1, Some(1)));
263        assert_eq!(columns.next_back(), Some(Rect::new(0, 0, 1, 2)));
264        assert_eq!(columns.size_hint(), (0, Some(0)));
265        assert_eq!(columns.next_back(), None);
266        assert_eq!(columns.size_hint(), (0, Some(0)));
267        assert_eq!(columns.next(), None);
268        assert_eq!(columns.size_hint(), (0, Some(0)));
269    }
270
271    #[test]
272    fn columns_meet_in_the_middle() {
273        let rect = Rect::new(0, 0, 4, 2);
274        let mut columns = Columns::new(rect);
275        assert_eq!(columns.size_hint(), (4, Some(4)));
276        assert_eq!(columns.next(), Some(Rect::new(0, 0, 1, 2)));
277        assert_eq!(columns.size_hint(), (3, Some(3)));
278        assert_eq!(columns.next_back(), Some(Rect::new(3, 0, 1, 2)));
279        assert_eq!(columns.size_hint(), (2, Some(2)));
280        assert_eq!(columns.next(), Some(Rect::new(1, 0, 1, 2)));
281        assert_eq!(columns.size_hint(), (1, Some(1)));
282        assert_eq!(columns.next_back(), Some(Rect::new(2, 0, 1, 2)));
283        assert_eq!(columns.size_hint(), (0, Some(0)));
284        assert_eq!(columns.next(), None);
285        assert_eq!(columns.size_hint(), (0, Some(0)));
286        assert_eq!(columns.next_back(), None);
287        assert_eq!(columns.size_hint(), (0, Some(0)));
288    }
289
290    /// We allow a total of `65536` columns in the range `(0..=65535)`.  In this test we iterate
291    /// forward and skip the first `65534` columns, and expect the next column to be `65535` and
292    /// the subsequent columns to be `None`.
293    #[test]
294    fn columns_max() {
295        let rect = Rect::new(0, 0, u16::MAX, 1);
296        let mut columns = Columns::new(rect).skip(usize::from(u16::MAX - 1));
297        assert_eq!(columns.next(), Some(Rect::new(u16::MAX - 1, 0, 1, 1)));
298        assert_eq!(columns.next(), None);
299    }
300
301    /// We allow a total of `65536` columns in the range `(0..=65535)`.  In this test we iterate
302    /// backward and skip the last `65534` columns, and expect the next column to be `0` and the
303    /// subsequent columns to be `None`.
304    #[test]
305    fn columns_min() {
306        let rect = Rect::new(0, 0, u16::MAX, 1);
307        let mut columns = Columns::new(rect).rev().skip(usize::from(u16::MAX - 1));
308        assert_eq!(columns.next(), Some(Rect::new(0, 0, 1, 1)));
309        assert_eq!(columns.next(), None);
310        assert_eq!(columns.next(), None);
311    }
312
313    #[test]
314    fn positions() {
315        let rect = Rect::new(0, 0, 2, 2);
316        let mut positions = Positions::new(rect);
317        assert_eq!(positions.size_hint(), (4, Some(4)));
318        assert_eq!(positions.next(), Some(Position::new(0, 0)));
319        assert_eq!(positions.size_hint(), (3, Some(3)));
320        assert_eq!(positions.next(), Some(Position::new(1, 0)));
321        assert_eq!(positions.size_hint(), (2, Some(2)));
322        assert_eq!(positions.next(), Some(Position::new(0, 1)));
323        assert_eq!(positions.size_hint(), (1, Some(1)));
324        assert_eq!(positions.next(), Some(Position::new(1, 1)));
325        assert_eq!(positions.size_hint(), (0, Some(0)));
326        assert_eq!(positions.next(), None);
327        assert_eq!(positions.size_hint(), (0, Some(0)));
328    }
329}