Skip to main content

kas_widgets/
grid.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! A grid widget
7
8use kas::layout::{DynGridStorage, GridCellInfo, GridDimensions};
9use kas::layout::{GridSetter, GridSolver, RulesSetter, RulesSolver};
10use kas::{CellCollection, layout, prelude::*};
11use std::ops::{Index, IndexMut};
12
13/// Make a [`Grid`] widget
14///
15/// Constructs a table with auto-determined number of rows and columns.
16///
17/// Cells are allowed to overlap but are not guaranteed to draw correctly in
18/// this case. The first declared widget of the overlap is "on top".
19///
20/// # Syntax
21///
22/// > _Collection_ :\
23/// > &nbsp;&nbsp; `grid!` `{` _ItemArms_<sup>\?</sup> `}`
24/// >
25/// > _ItemArms_ :\
26/// > &nbsp;&nbsp; (_ItemArm_ `,`)<sup>\*</sup> _ItemArm_ `,`<sup>\?</sup>
27/// >
28/// > _ItemArm_ :\
29/// > &nbsp;&nbsp; _Cell_ | _RowMacro_ | _ColumnMacro_
30/// >
31/// > _Cell_ :\
32/// > &nbsp;&nbsp; `(` _Column_ `,` _Row_ `)` `=>` _Item_
33/// >
34/// > _Column_, _Row_ :\
35/// > &nbsp;&nbsp; _LitInt_ | ( _LitInt_ `..=` _LitInt_ )
36/// >
37/// > _RowMacro_ :\
38/// > &npsb;&nbsp; `row!` `[` (_Item_ `,` | `_` `,`)* _Item_ `]`
39/// >
40/// > _ColumnMacro_ :\
41/// > &npsb;&nbsp; `column!` `[` (_Item_ `,` | `_` `,`)* _Item_ `]`
42///
43/// Here, _Column_ and _Row_ are selected via an index (from 0) or an inclusive
44/// range, for example `2` or `2..=3`.
45///
46/// A `row!` macro resolves to a list of cells whose column index is one larger
47/// than maximum prior column index and whose row indices count from 0.
48/// As a special case, `_` may be used to skip a cell (infer an empty cell).
49/// A `column!` macro functions similarly.
50///
51/// ## Stand-alone usage
52///
53/// When used as a stand-alone macro, `grid! { /* ... */ }` is just syntactic
54/// sugar for `Grid::new(kas::cell_collection! { /* ... */ })`.
55///
56/// In this case, _Item_ may be:
57///
58/// -   A string literal (interpreted as a label widget), optionally followed by
59///     any of the following method calls: [`align`], [`pack`], [`with_stretch`]
60/// -   An expression yielding an object implementing `Widget<Data = _A>`
61///
62/// In case all _Item_ instances are a string literal, the data type of the
63/// `grid!` widget will be `()`; otherwise the data type of the widget is `_A`
64/// where `_A` is a generic type parameter of the widget.
65///
66/// ## Usage within widget layout syntax
67///
68/// In this case, _Item_ uses [widget layout syntax]. This is broadly similar to
69/// the above with a couple of exceptions:
70///
71/// -   Supported layout macros do not need to be imported to the module scope
72/// -   An _Item_ may be a `#[widget]` field of the widget
73///
74/// # Example
75///
76/// ```
77/// let my_widget = kas_widgets::grid! {
78///     (0, 0) => "one",
79///     (1, 0) => "two",
80///     (0..=1, 1) => "three",
81/// };
82/// ```
83///
84/// [widget layout syntax]: macro@kas::layout
85/// [`align`]: crate::AdaptWidget::align
86/// [`pack`]: crate::AdaptWidget::pack
87/// [`with_stretch`]: crate::AdaptWidget::with_stretch
88#[macro_export]
89macro_rules! grid {
90    ( $($tt:tt)* ) => {
91        $crate::Grid::new( ::kas::cell_collection! { $($tt)* } )
92    };
93}
94
95#[impl_self]
96mod Grid {
97    /// A generic grid widget
98    ///
99    /// Child widgets are displayed in a grid, according to each child's
100    /// [`GridCellInfo`]. This allows spans and overlapping widgets. The numbers
101    /// of rows and columns is determined automatically while the sizes of rows and
102    /// columns are determined based on their contents (including special handling
103    /// for spans, *mostly* with good results).
104    ///
105    /// Note that all child widgets are stored in a list internally. The order of
106    /// widgets in that list does not affect display position, but does have a few
107    /// effects: (a) widgets may be accessed in this order via indexing, (b) widgets
108    /// are configured and drawn in this order, (c) navigating
109    /// through widgets with the Tab key currently uses the list order (though it
110    /// may be changed in the future to use display order).
111    ///
112    /// Cells are allowed to overlap but are not guaranteed to draw correctly in
113    /// this case. The first declared widget of the overlap is "on top".
114    ///
115    /// ## Alternatives
116    ///
117    /// Where the entries are fixed, also consider custom [`Widget`] implementations.
118    ///
119    /// ## Performance
120    ///
121    /// Most operations are `O(n)` in the number of children.
122    ///
123    /// ## Example
124    /// ```
125    /// use kas::cell_collection;
126    /// # use kas_widgets::Grid;
127    /// let _grid = Grid::new(cell_collection! {
128    ///     (0, 0) => "one",
129    ///     (1, 0) => "two",
130    ///     (0..=1, 1) => "three",
131    /// });
132    /// ```
133    #[widget]
134    pub struct Grid<C: CellCollection> {
135        core: widget_core!(),
136        layout: DynGridStorage,
137        dim: GridDimensions,
138        #[collection]
139        widgets: C,
140    }
141
142    impl Layout for Self {
143        fn size_rules(&mut self, cx: &mut SizeCx, axis: AxisInfo) -> SizeRules {
144            let mut solver = GridSolver::<Vec<_>, Vec<_>, _>::new(axis, self.dim, &mut self.layout);
145            for n in 0..self.widgets.len() {
146                if let Some((info, child)) =
147                    self.widgets.cell_info(n).zip(self.widgets.get_mut_tile(n))
148                {
149                    solver.for_child(&mut self.layout, info, |axis| child.size_rules(cx, axis));
150                }
151            }
152            solver.finish(&mut self.layout)
153        }
154
155        fn set_rect(&mut self, cx: &mut SizeCx, rect: Rect, hints: AlignHints) {
156            self.core.set_rect(rect);
157            let mut setter = GridSetter::<Vec<_>, Vec<_>, _>::new(rect, self.dim, &mut self.layout);
158            for n in 0..self.widgets.len() {
159                if let Some((info, child)) =
160                    self.widgets.cell_info(n).zip(self.widgets.get_mut_tile(n))
161                {
162                    child.set_rect(cx, setter.child_rect(&mut self.layout, info), hints);
163                }
164            }
165        }
166
167        fn draw(&self, mut draw: DrawCx) {
168            // Draw in reverse to show first of overlap on top
169            for n in (0..self.widgets.len()).rev() {
170                if let Some(child) = self.widgets.get_tile(n) {
171                    child.draw(draw.re());
172                }
173            }
174        }
175    }
176
177    impl Tile for Self {
178        fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
179            Role::None
180        }
181    }
182
183    impl Events for Self {
184        type Data = C::Data;
185
186        fn probe(&self, coord: Coord) -> Id {
187            for n in 0..self.widgets.len() {
188                if let Some(child) = self.widgets.get_tile(n) {
189                    if let Some(id) = child.try_probe(coord) {
190                        return id;
191                    }
192                }
193            }
194            self.id()
195        }
196    }
197}
198
199impl<C: CellCollection> Grid<C> {
200    /// Construct a new instance
201    #[inline]
202    pub fn new(widgets: C) -> Self {
203        Grid {
204            core: Default::default(),
205            layout: Default::default(),
206            dim: widgets.grid_dimensions(),
207            widgets,
208        }
209    }
210
211    /// Get grid dimensions
212    ///
213    /// The numbers of rows, columns and spans is determined automatically.
214    #[inline]
215    pub fn dimensions(&self) -> GridDimensions {
216        self.dim
217    }
218
219    /// Access layout storage
220    ///
221    /// Use [`Self::dimensions`] to get expected dimensions.
222    #[inline]
223    pub fn layout_storage(&mut self) -> &mut (impl layout::GridStorage + use<C>) {
224        &mut self.layout
225    }
226}
227
228impl<W: Widget> Grid<Vec<(GridCellInfo, W)>> {
229    /// Construct via a builder
230    pub fn build<F: FnOnce(GridBuilder<W>)>(f: F) -> Self {
231        let mut grid = Grid::new(vec![]);
232        f(GridBuilder(&mut grid.widgets));
233        grid.dim = grid.widgets.grid_dimensions();
234        grid
235    }
236
237    /// True if there are no child widgets
238    pub fn is_empty(&self) -> bool {
239        self.widgets.is_empty()
240    }
241
242    /// Returns the number of child widgets
243    pub fn len(&self) -> usize {
244        self.widgets.len()
245    }
246
247    /// Returns a reference to the child, if any
248    pub fn get(&self, index: usize) -> Option<&W> {
249        self.widgets.get(index).map(|t| &t.1)
250    }
251
252    /// Returns a mutable reference to the child, if any
253    pub fn get_mut(&mut self, index: usize) -> Option<&mut W> {
254        self.widgets.get_mut(index).map(|t| &mut t.1)
255    }
256
257    /// Iterate over childern
258    pub fn iter(&self) -> impl Iterator<Item = &(GridCellInfo, W)> {
259        ListIter {
260            list: &self.widgets,
261        }
262    }
263
264    /// Mutably iterate over childern
265    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (GridCellInfo, W)> {
266        ListIterMut {
267            list: &mut self.widgets,
268        }
269    }
270}
271
272pub struct GridBuilder<'a, W: Widget>(&'a mut Vec<(GridCellInfo, W)>);
273impl<'a, W: Widget> GridBuilder<'a, W> {
274    /// True if there are no child widgets
275    pub fn is_empty(&self) -> bool {
276        self.0.is_empty()
277    }
278
279    /// Returns the number of child widgets
280    pub fn len(&self) -> usize {
281        self.0.len()
282    }
283
284    /// Returns the number of elements the vector can hold without reallocating.
285    pub fn capacity(&self) -> usize {
286        self.0.capacity()
287    }
288
289    /// Reserves capacity for at least `additional` more elements to be inserted
290    /// into the list. See documentation of [`Vec::reserve`].
291    pub fn reserve(&mut self, additional: usize) {
292        self.0.reserve(additional);
293    }
294
295    /// Remove all child widgets
296    pub fn clear(&mut self) {
297        self.0.clear();
298    }
299
300    /// Add a child widget
301    ///
302    /// The child is added to the end of the "list", thus appears last in
303    /// navigation order.
304    pub fn push(&mut self, info: GridCellInfo, widget: W) {
305        self.0.push((info, widget));
306    }
307
308    /// Add a child widget to the given cell
309    ///
310    /// The child is added to the end of the "list", thus appears last in
311    /// navigation order.
312    pub fn push_cell(&mut self, col: u32, row: u32, widget: W) {
313        let info = GridCellInfo::new(col, row);
314        self.push(info, widget);
315    }
316
317    /// Add a child widget to the given cell, builder style
318    ///
319    /// The child is added to the end of the "list", thus appears last in
320    /// navigation order.
321    #[must_use]
322    pub fn with_cell(self, col: u32, row: u32, widget: W) -> Self {
323        self.with_cell_span(col, row, 1, 1, widget)
324    }
325
326    /// Add a child widget to the given cell, with spans
327    ///
328    /// Parameters `col_span` and `row_span` are the number of columns/rows
329    /// spanned and should each be at least 1.
330    ///
331    /// The child is added to the end of the "list", thus appears last in
332    /// navigation order.
333    pub fn push_cell_span(&mut self, col: u32, row: u32, col_span: u32, row_span: u32, widget: W) {
334        let info = GridCellInfo {
335            col,
336            last_col: col + col_span - 1,
337            row,
338            last_row: row + row_span - 1,
339        };
340        self.push(info, widget);
341    }
342
343    /// Add a child widget to the given cell, with spans, builder style
344    ///
345    /// Parameters `col_span` and `row_span` are the number of columns/rows
346    /// spanned and should each be at least 1.
347    ///
348    /// The child is added to the end of the "list", thus appears last in
349    /// navigation order.
350    #[must_use]
351    pub fn with_cell_span(
352        mut self,
353        col: u32,
354        row: u32,
355        col_span: u32,
356        row_span: u32,
357        widget: W,
358    ) -> Self {
359        self.push_cell_span(col, row, col_span, row_span, widget);
360        self
361    }
362
363    /// Remove the last child widget
364    ///
365    /// Returns `None` if there are no children. Otherwise, this
366    /// triggers a reconfigure before the next draw operation.
367    pub fn pop(&mut self) -> Option<(GridCellInfo, W)> {
368        self.0.pop()
369    }
370
371    /// Inserts a child widget position `index`
372    ///
373    /// Panics if `index > len`.
374    pub fn insert(&mut self, index: usize, info: GridCellInfo, widget: W) {
375        self.0.insert(index, (info, widget));
376    }
377
378    /// Removes the child widget at position `index`
379    ///
380    /// Panics if `index` is out of bounds.
381    pub fn remove(&mut self, index: usize) -> (GridCellInfo, W) {
382        self.0.remove(index)
383    }
384
385    /// Replace the child at `index`
386    ///
387    /// Panics if `index` is out of bounds.
388    pub fn replace(&mut self, index: usize, info: GridCellInfo, widget: W) -> (GridCellInfo, W) {
389        let mut item = (info, widget);
390        std::mem::swap(&mut item, &mut self.0[index]);
391        item
392    }
393
394    /// Append child widgets from an iterator
395    pub fn extend<T: IntoIterator<Item = (GridCellInfo, W)>>(&mut self, iter: T) {
396        self.0.extend(iter);
397    }
398
399    /// Resize, using the given closure to construct new widgets
400    pub fn resize_with<F: Fn(usize) -> (GridCellInfo, W)>(&mut self, len: usize, f: F) {
401        let l0 = self.0.len();
402        if l0 > len {
403            self.0.truncate(len);
404        } else if l0 < len {
405            self.0.reserve(len);
406            for i in l0..len {
407                self.0.push(f(i));
408            }
409        }
410    }
411
412    /// Retain only widgets satisfying predicate `f`
413    ///
414    /// See documentation of [`Vec::retain`].
415    pub fn retain<F: FnMut(&(GridCellInfo, W)) -> bool>(&mut self, f: F) {
416        self.0.retain(f);
417    }
418
419    /// Get the first index of a child occupying the given cell, if any
420    pub fn find_child_cell(&self, col: u32, row: u32) -> Option<usize> {
421        for (i, (info, _)) in self.0.iter().enumerate() {
422            if info.col <= col && col <= info.last_col && info.row <= row && row <= info.last_row {
423                return Some(i);
424            }
425        }
426        None
427    }
428
429    /// Iterate over childern
430    pub fn iter(&self) -> impl Iterator<Item = &(GridCellInfo, W)> {
431        ListIter { list: self.0 }
432    }
433
434    /// Mutably iterate over childern
435    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (GridCellInfo, W)> + use<'_, W> {
436        ListIterMut { list: self.0 }
437    }
438}
439
440impl<W: Widget> FromIterator<(GridCellInfo, W)> for Grid<Vec<(GridCellInfo, W)>> {
441    #[inline]
442    fn from_iter<T>(iter: T) -> Self
443    where
444        T: IntoIterator<Item = (GridCellInfo, W)>,
445    {
446        Self::new(iter.into_iter().collect())
447    }
448}
449
450impl<W: Widget> Index<usize> for Grid<Vec<(GridCellInfo, W)>> {
451    type Output = (GridCellInfo, W);
452
453    fn index(&self, index: usize) -> &Self::Output {
454        &self.widgets[index]
455    }
456}
457
458impl<W: Widget> IndexMut<usize> for Grid<Vec<(GridCellInfo, W)>> {
459    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
460        &mut self.widgets[index]
461    }
462}
463
464struct ListIter<'a, W: Widget> {
465    list: &'a [(GridCellInfo, W)],
466}
467impl<'a, W: Widget> Iterator for ListIter<'a, W> {
468    type Item = &'a (GridCellInfo, W);
469    fn next(&mut self) -> Option<Self::Item> {
470        if let Some((first, rest)) = self.list.split_first() {
471            self.list = rest;
472            Some(first)
473        } else {
474            None
475        }
476    }
477    fn size_hint(&self) -> (usize, Option<usize>) {
478        let len = self.len();
479        (len, Some(len))
480    }
481}
482impl<'a, W: Widget> ExactSizeIterator for ListIter<'a, W> {
483    fn len(&self) -> usize {
484        self.list.len()
485    }
486}
487
488struct ListIterMut<'a, W: Widget> {
489    list: &'a mut [(GridCellInfo, W)],
490}
491impl<'a, W: Widget> Iterator for ListIterMut<'a, W> {
492    type Item = &'a mut (GridCellInfo, W);
493    fn next(&mut self) -> Option<Self::Item> {
494        let list = std::mem::take(&mut self.list);
495        if let Some((first, rest)) = list.split_first_mut() {
496            self.list = rest;
497            Some(first)
498        } else {
499            None
500        }
501    }
502    fn size_hint(&self) -> (usize, Option<usize>) {
503        let len = self.len();
504        (len, Some(len))
505    }
506}
507impl<'a, W: Widget> ExactSizeIterator for ListIterMut<'a, W> {
508    fn len(&self) -> usize {
509        self.list.len()
510    }
511}