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/// > `collection!` `[` _ItemArms_<sup>\?</sup> `]`
23/// >
24/// > _ItemArms_ :\
25/// > (_ItemArm_ `,`)<sup>\*</sup> _ItemArm_ `,`<sup>\?</sup>
26/// >
27/// > _ItemArm_ :\
28/// > `(` _Column_ `,` _Row_ `)` `=>` _Item_
29/// >
30/// > _Column_, _Row_ :\
31/// > _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}