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