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/// > `grid!` `{` _ItemArms_<sup>\?</sup> `}`
24/// >
25/// > _ItemArms_ :\
26/// > (_ItemArm_ `,`)<sup>\*</sup> _ItemArm_ `,`<sup>\?</sup>
27/// >
28/// > _ItemArm_ :\
29/// > _Cell_ | _RowMacro_ | _ColumnMacro_
30/// >
31/// > _Cell_ :\
32/// > `(` _Column_ `,` _Row_ `)` `=>` _Item_
33/// >
34/// > _Column_, _Row_ :\
35/// > _LitInt_ | ( _LitInt_ `..=` _LitInt_ )
36/// >
37/// > _RowMacro_ :\
38/// > &npsb; `row!` `[` (_Item_ `,` | `_` `,`)* _Item_ `]`
39/// >
40/// > _ColumnMacro_ :\
41/// > &npsb; `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}