Skip to main content

layuit/
grid.rs

1//! Containers that distribute equal space to children.
2//!
3//! [`HEqual`] and [`VEqual`] work very similar to [`HStack`] and [`VStack`], but give every child
4//! equal space and do not suffer from the [`Full`] alignment caveat.
5//!
6//! [`Grid`] arranges nodes in a grid, with each child getting equal width and height. Nodes fill
7//! from left to right first, and then from top to bottom.
8//!
9//! [`HEqual`]: crate::grid::HEqual
10//! [`VEqual`]: crate::grid::VEqual
11//! [`HStack`]: crate::stacks::HStack
12//! [`VStack`]: crate::stacks::VStack
13//! [`Full`]: crate::Alignment::Full
14
15use indexmap::IndexSet;
16use std::num::NonZero;
17use thunderdome::Index as NodeIndex;
18
19use crate::{Alignment, NodeCache, Rect, UiNode, UiTree};
20
21/// Arranges children from left to right, similar to [`HStack`], but gives every child equal space
22/// and does not suffer from the [`Full`] alignment caveat.
23///
24/// [`HStack`]: crate::stacks::HStack
25/// [`Full`]: crate::Alignment::Full
26pub struct HEqual {
27    align: (Alignment, Alignment),
28    children: IndexSet<NodeIndex>,
29}
30
31impl HEqual {
32    /// Creates a new `HEqual` with no children, 0 spacing, and ([`Begin`], [`Begin`]) alignment.
33    ///
34    /// [`Begin`]: Alignment::Begin
35    pub fn new() -> Self {
36        Self {
37            align: (Alignment::Begin, Alignment::Begin),
38            children: IndexSet::new(),
39        }
40    }
41
42    /// Add a new child to the list.
43    pub fn with_child(mut self, index: NodeIndex) -> Self {
44        self.children.insert(index);
45        self
46    }
47
48    /// Set the horizontal and vertical alignment.
49    pub fn with_align(mut self, align: (Alignment, Alignment)) -> Self {
50        self.align = align;
51        self
52    }
53
54    /// Add a child to the list. The child will appear at the end.
55    pub fn add_child(&mut self, index: NodeIndex) {
56        self.children.insert(index);
57    }
58
59    /// Returns the number of children in the list.
60    pub fn len(&self) -> usize {
61        self.children.len()
62    }
63
64    /// Returns `true` if the list is empty.
65    ///
66    /// Equivalent to `len() == 0`.
67    pub fn is_empty(&self) -> bool {
68        self.children.is_empty()
69    }
70
71    /// Remove a child from the list.
72    ///
73    /// Returns `true` if the child was removed.
74    pub fn remove_child(&mut self, index: usize, tree: &mut UiTree) -> bool {
75        let Some(ti) = self.children.shift_remove_index(index) else {
76            return false;
77        };
78
79        if tree.get_node(ti).is_none() {
80            return false;
81        }
82        tree.remove_node(ti);
83
84        true
85    }
86
87    /// Move a child to a new index.
88    ///
89    /// Returns `true` if the child was moved.
90    pub fn set_child_position(&mut self, index: usize, position: usize) -> bool {
91        self.children.move_index(index, position);
92        true
93    }
94
95    /// Returns the tree index associated with a child at a given list index.
96    pub fn get_child_index(&self, index: usize) -> Option<NodeIndex> {
97        self.children.get_index(index).copied()
98    }
99}
100
101impl Default for HEqual {
102    fn default() -> Self {
103        Self::new()
104    }
105}
106
107impl UiNode for HEqual {
108    fn get_align(&self) -> (Alignment, Alignment) {
109        self.align
110    }
111
112    fn get_align_mut(&mut self) -> (&mut Alignment, &mut Alignment) {
113        (&mut self.align.0, &mut self.align.1)
114    }
115
116    fn calculate_min_size(&self, tree: &UiTree) -> (f32, f32) {
117        if self.children.is_empty() {
118            return (0.0, 0.0);
119        }
120
121        let mut w = 0.0f32;
122        let mut h = 0.0f32;
123        for child in &self.children {
124            let child = tree.get_cache(*child).expect("Child not in cache");
125            let (cw, ch) = child.min_size;
126            w = w.max(cw);
127            h = h.max(ch);
128        }
129
130        (w * (self.len() as f32), h)
131    }
132
133    fn calculate_rects(&self, cache: &NodeCache, tree: &UiTree) -> Vec<Rect> {
134        if self.is_empty() {
135            return vec![];
136        }
137
138        let mut child_rects = Vec::with_capacity(self.children.len());
139
140        let w = cache.rect.w / (self.len() as f32);
141
142        let mut x = cache.rect.x;
143        for child in &self.children {
144            let child_min = tree.get_cache(*child).expect("Child not in cache").min_size;
145            let child = tree.get_node(*child).expect("Child not in arena");
146
147            let space =
148                Rect::new(x, cache.rect.y, w, cache.rect.h).align(child.get_align(), child_min);
149            child_rects.push(space);
150            x += w;
151        }
152
153        child_rects
154    }
155
156    fn get_children(&self) -> Vec<NodeIndex> {
157        self.children.iter().copied().collect()
158    }
159}
160
161/// Arranges children from top to bottom, similar to [`VStack`], but gives every child equal space
162/// and does not suffer from the [`Full`] alignment caveat.
163///
164/// [`VStack`]: crate::stacks::VStack
165/// [`Full`]: crate::Alignment::Full
166pub struct VEqual {
167    align: (Alignment, Alignment),
168    children: IndexSet<NodeIndex>,
169}
170
171impl VEqual {
172    /// Creates a new `VEqual` with no children, 0 spacing, and ([`Begin`], [`Begin`]) alignment.
173    ///
174    /// [`Begin`]: Alignment::Begin
175    pub fn new() -> Self {
176        Self {
177            align: (Alignment::Begin, Alignment::Begin),
178            children: IndexSet::new(),
179        }
180    }
181
182    /// Add a new child to the list.
183    pub fn with_child(mut self, index: NodeIndex) -> Self {
184        self.children.insert(index);
185        self
186    }
187
188    /// Set the horizontal and vertical alignment.
189    pub fn with_align(mut self, align: (Alignment, Alignment)) -> Self {
190        self.align = align;
191        self
192    }
193
194    /// Add a child to the list. The child will appear at the end.
195    pub fn add_child(&mut self, index: NodeIndex) {
196        self.children.insert(index);
197    }
198
199    /// Returns the number of children in the list.
200    pub fn len(&self) -> usize {
201        self.children.len()
202    }
203
204    /// Returns `true` if the list is empty.
205    ///
206    /// Equivalent to `len() == 0`.
207    pub fn is_empty(&self) -> bool {
208        self.children.is_empty()
209    }
210
211    /// Remove a child from the list.
212    ///
213    /// Returns `true` if the child was removed.
214    pub fn remove_child(&mut self, index: usize, tree: &mut UiTree) -> bool {
215        let Some(ti) = self.children.shift_remove_index(index) else {
216            return false;
217        };
218
219        if tree.get_node(ti).is_none() {
220            return false;
221        }
222        tree.remove_node(ti);
223
224        true
225    }
226
227    /// Move a child to a new index.
228    ///
229    /// Returns `true` if the child was moved.
230    pub fn set_child_position(&mut self, index: usize, position: usize) -> bool {
231        self.children.move_index(index, position);
232        true
233    }
234
235    /// Returns the tree index associated with a child at a given list index.
236    pub fn get_child_index(&self, index: usize) -> Option<NodeIndex> {
237        self.children.get_index(index).copied()
238    }
239}
240
241impl Default for VEqual {
242    fn default() -> Self {
243        Self::new()
244    }
245}
246
247impl UiNode for VEqual {
248    fn get_align(&self) -> (Alignment, Alignment) {
249        self.align
250    }
251
252    fn get_align_mut(&mut self) -> (&mut Alignment, &mut Alignment) {
253        (&mut self.align.0, &mut self.align.1)
254    }
255
256    fn calculate_min_size(&self, tree: &UiTree) -> (f32, f32) {
257        if self.children.is_empty() {
258            return (0.0, 0.0);
259        }
260
261        let mut w = 0.0f32;
262        let mut h = 0.0f32;
263        for child in &self.children {
264            let child = tree.get_cache(*child).expect("Child not in cache");
265            let (cw, ch) = child.min_size;
266            w = w.max(cw);
267            h = h.max(ch);
268        }
269
270        (w, h * (self.len() as f32))
271    }
272
273    fn calculate_rects(&self, cache: &NodeCache, tree: &UiTree) -> Vec<Rect> {
274        if self.is_empty() {
275            return vec![];
276        }
277
278        let mut child_rects = Vec::with_capacity(self.children.len());
279
280        let h = cache.rect.h / (self.len() as f32);
281
282        let mut y = cache.rect.y;
283        for child in &self.children {
284            let child_min = tree.get_cache(*child).expect("Child not in cache").min_size;
285            let child = tree.get_node(*child).expect("Child not in arena");
286
287            let space =
288                Rect::new(cache.rect.x, y, cache.rect.w, h).align(child.get_align(), child_min);
289            child_rects.push(space);
290            y += h;
291        }
292
293        child_rects
294    }
295
296    fn get_children(&self) -> Vec<NodeIndex> {
297        self.children.iter().copied().collect()
298    }
299}
300
301/// Arranges children in a grid of equally-sized cells, from left to right and then top to bottom.
302pub struct Grid {
303    pub num_cols: NonZero<usize>,
304
305    align: (Alignment, Alignment),
306    children: IndexSet<NodeIndex>,
307}
308
309impl Grid {
310    /// Create a new grid with the specified number of columns.
311    pub fn new(num_cols: NonZero<usize>) -> Self {
312        Self {
313            num_cols,
314            align: (Alignment::Full, Alignment::Full),
315            children: IndexSet::new(),
316        }
317    }
318
319    /// Add a new child to the grid.
320    pub fn with_child(mut self, index: NodeIndex) -> Self {
321        self.children.insert(index);
322        self
323    }
324
325    /// Set the horizontal and vertical alignment.
326    pub fn with_align(mut self, align: (Alignment, Alignment)) -> Self {
327        self.align = align;
328        self
329    }
330
331    /// Add a child to the grid. The child will appear at the end.
332    pub fn add_child(&mut self, index: NodeIndex) {
333        self.children.insert(index);
334    }
335
336    /// Returns the number of children in the grid.
337    pub fn len(&self) -> usize {
338        self.children.len()
339    }
340
341    /// Returns `true` if the grid is empty.
342    ///
343    /// Equivalent to `len() == 0`.
344    pub fn is_empty(&self) -> bool {
345        self.children.is_empty()
346    }
347
348    /// Remove a child from the grid.
349    ///
350    /// Returns `true` if the child was removed.
351    pub fn remove_child(&mut self, index: usize, tree: &mut UiTree) -> bool {
352        let Some(ti) = self.children.shift_remove_index(index) else {
353            return false;
354        };
355
356        if tree.get_node(ti).is_none() {
357            return false;
358        }
359        tree.remove_node(ti);
360
361        true
362    }
363
364    /// Move a child to a new index.
365    ///
366    /// Returns `true` if the child was moved.
367    pub fn set_child_position(&mut self, index: usize, position: usize) -> bool {
368        self.children.move_index(index, position);
369        true
370    }
371
372    /// Returns the tree index associated with a child at a given list index.
373    pub fn get_child_index(&self, index: usize) -> Option<NodeIndex> {
374        self.children.get_index(index).copied()
375    }
376}
377
378impl UiNode for Grid {
379    fn get_align(&self) -> (Alignment, Alignment) {
380        self.align
381    }
382
383    fn get_align_mut(&mut self) -> (&mut Alignment, &mut Alignment) {
384        (&mut self.align.0, &mut self.align.1)
385    }
386
387    fn calculate_min_size(&self, tree: &UiTree) -> (f32, f32) {
388        let mut w = 0.0f32;
389        let mut h = 0.0f32;
390        for child in &self.children {
391            let (cw, ch) = tree.get_cache(*child).expect("Child not in cache").min_size;
392            w = w.max(cw);
393            h = h.max(ch);
394        }
395
396        let num_rows = self.len().div_ceil(self.num_cols.get());
397
398        (w * self.num_cols.get() as f32, h * num_rows as f32)
399    }
400
401    fn calculate_rects(&self, cache: &NodeCache, tree: &UiTree) -> Vec<Rect> {
402        let mut child_rects = Vec::with_capacity(self.children.len());
403
404        let num_rows = self.len().div_ceil(self.num_cols.get());
405        let dx = cache.rect.w / (self.num_cols.get() as f32);
406        let dy = cache.rect.h / (num_rows as f32);
407
408        let mut col = 0;
409        let mut x = cache.rect.x;
410        let mut y = cache.rect.y;
411        for child in &self.children {
412            let child_min = tree.get_cache(*child).expect("Child not in cache").min_size;
413            let child = tree.get_node(*child).expect("Child not in arena");
414
415            let space = Rect::new(x, y, dx, dy).align(child.get_align(), child_min);
416            child_rects.push(space);
417
418            col += 1;
419            if col >= self.num_cols.get() {
420                col = 0;
421                x = cache.rect.x;
422                y += dy;
423            } else {
424                x += dx;
425            }
426        }
427
428        child_rects
429    }
430
431    fn get_children(&self) -> Vec<NodeIndex> {
432        self.children.iter().copied().collect()
433    }
434}