kas_widgets/
list.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 row or column with run-time adjustable contents
7
8use kas::Collection;
9use kas::dir::{Down, Right};
10use kas::layout::{
11    DynRowStorage, RowPositionSolver, RowSetter, RowSolver, RowStorage, RulesSetter, RulesSolver,
12};
13use kas::prelude::*;
14use std::collections::hash_map::{Entry, HashMap};
15use std::ops::{Index, IndexMut};
16
17/// Make a [`Row`] widget
18///
19/// # Syntax
20///
21/// > _Collection_ :\
22/// > &nbsp;&nbsp; `row!` `[` _Items_<sup>\?</sup> `]`
23/// >
24/// > _Items_ :\
25/// > &nbsp;&nbsp; (_Item_ `,`)<sup>\*</sup> _Item_ `,`<sup>\?</sup>
26///
27/// ## Stand-alone usage
28///
29/// When used as a stand-alone macro, `row! [/* ... */]` is just syntactic sugar
30/// for `Row::new(kas::collection! [/* ... */])`.
31///
32/// In this case, _Item_ may be:
33///
34/// -   A string literal (interpreted as a label widget), optionally followed by
35///     an [`align`] or [`pack`] method call
36/// -   An expression yielding an object implementing `Widget<Data = _A>`
37///
38/// In case all _Item_ instances are a string literal, the data type of the
39/// `row!` widget will be `()`; otherwise the data type of the widget is `_A`
40/// where `_A` is a generic type parameter of the widget.
41///
42/// ## Usage within widget layout syntax
43///
44/// When called within [widget layout syntax], `row!` may be evaluated as a
45/// recursive macro and the result does not have a specified type, except that
46/// methods [`map_any`], [`align`] and [`pack`] are supported via emulation.
47///
48/// In this case, _Item_ is evaluated using [widget layout syntax]. This is
49/// broadly similar to the above with a couple of exceptions:
50///
51/// -   Supported layout macros do not need to be imported to the module scope
52/// -   An _Item_ may be a `#[widget]` field of the widget
53///
54/// # Example
55///
56/// ```
57/// let my_widget = kas_widgets::row!["one", "two"];
58/// ```
59///
60/// [widget layout syntax]: macro@kas::layout
61/// [`map_any`]: crate::AdaptWidgetAny::map_any
62/// [`align`]: crate::AdaptWidget::align
63/// [`pack`]: crate::AdaptWidget::pack
64#[macro_export]
65macro_rules! row {
66    ( $( $ee:expr ),* ) => {
67        $crate::Row::new( ::kas::collection! [ $( $ee ),* ] )
68    };
69    ( $( $ee:expr ),+ , ) => {
70        $crate::Row::new( ::kas::collection! [ $( $ee ),+ ] )
71    };
72}
73
74/// Make a [`Column`] widget
75///
76/// # Syntax
77///
78/// > _Collection_ :\
79/// > &nbsp;&nbsp; `column!` `[` _Items_<sup>\?</sup> `]`
80/// >
81/// > _Items_ :\
82/// > &nbsp;&nbsp; (_Item_ `,`)<sup>\*</sup> _Item_ `,`<sup>\?</sup>
83///
84/// ## Stand-alone usage
85///
86/// When used as a stand-alone macro, `column! [/* ... */]` is just syntactic sugar
87/// for `Column::new(kas::collection! [/* ... */])`.
88///
89/// In this case, _Item_ may be:
90///
91/// -   A string literal (interpreted as a label widget), optionally followed by
92///     an [`align`] or [`pack`] method call
93/// -   An expression yielding an object implementing `Widget<Data = _A>`
94///
95/// In case all _Item_ instances are a string literal, the data type of the
96/// `column!` widget will be `()`; otherwise the data type of the widget is `_A`
97/// where `_A` is a generic type parameter of the widget.
98///
99/// ## Usage within widget layout syntax
100///
101/// When called within [widget layout syntax], `column!` may be evaluated as a
102/// recursive macro and the result does not have a specified type, except that
103/// methods [`map_any`], [`align`] and [`pack`] are supported via emulation.
104///
105/// In this case, _Item_ is evaluated using [widget layout syntax]. This is
106/// broadly similar to the above with a couple of exceptions:
107///
108/// -   Supported layout macros do not need to be imported to the module scope
109/// -   An _Item_ may be a `#[widget]` field of the widget
110///
111/// # Example
112///
113/// ```
114/// let my_widget = kas_widgets::column! [
115///     "one",
116///     "two",
117/// ];
118/// ```
119///
120/// [widget layout syntax]: macro@kas::layout
121/// [`map_any`]: crate::AdaptWidgetAny::map_any
122/// [`align`]: crate::AdaptWidget::align
123/// [`pack`]: crate::AdaptWidget::pack
124#[macro_export]
125macro_rules! column {
126    ( $( $ee:expr ),* ) => {
127        $crate::Column::new( ::kas::collection! [ $( $ee ),* ] )
128    };
129    ( $( $ee:expr ),+ , ) => {
130        $crate::Column::new( ::kas::collection! [ $( $ee ),+ ] )
131    };
132}
133
134/// Make a [`List`] widget
135///
136/// # Syntax
137///
138/// > _Collection_ :\
139/// > &nbsp;&nbsp; `list!` `[` _Items_<sup>\?</sup> `]`
140/// >
141/// > _Items_ :\
142/// > &nbsp;&nbsp; (_Item_ `,`)<sup>\*</sup> _Item_ `,`<sup>\?</sup>
143///
144/// ## Stand-alone usage
145///
146/// When used as a stand-alone macro, `list! [/* ... */]` is just syntactic sugar
147/// for `List::new(kas::collection! [/* ... */])`.
148///
149/// In this case, _Item_ may be:
150///
151/// -   A string literal (interpreted as a label widget), optionally followed by
152///     an [`align`] or [`pack`] method call
153/// -   An expression yielding an object implementing `Widget<Data = _A>`
154///
155/// In case all _Item_ instances are a string literal, the data type of the
156/// `list!` widget will be `()`; otherwise the data type of the widget is `_A`
157/// where `_A` is a generic type parameter of the widget.
158///
159/// ## Usage within widget layout syntax
160///
161/// When called within [widget layout syntax], `list!` may be evaluated as a
162/// recursive macro and the result does not have a specified type, except that
163/// methods [`map_any`], [`align`], [`pack`] and [`with_direction`] are
164/// supported via emulation. In this case, calling [`with_direction`] is
165/// required. Note that the argument passed to [`with_direction`] is expanded
166/// at the use site, so for example `.with_direction(self.dir)` will read
167/// `self.dir` whenever layout is computed.
168///
169/// In this case, _Item_ is evaluated using [widget layout syntax]. This is
170/// broadly similar to the above with a couple of exceptions:
171///
172/// -   Supported layout macros do not need to be imported to the module scope
173/// -   An _Item_ may be a `#[widget]` field of the widget
174///
175/// # Example
176///
177/// ```
178/// let my_widget = kas_widgets::list! ["one", "two"]
179///     .with_direction(kas::dir::Left);
180/// ```
181///
182/// [widget layout syntax]: macro@kas::layout
183/// [`map_any`]: crate::AdaptWidgetAny::map_any
184/// [`align`]: crate::AdaptWidget::align
185/// [`pack`]: crate::AdaptWidget::pack
186/// [`with_direction`]: List::with_direction
187#[macro_export]
188macro_rules! list {
189    ( $( $ee:expr ),* ) => {
190        $crate::List::new( ::kas::collection! [ $( $ee ),* ] )
191    };
192    ( $( $ee:expr ),+ , ) => {
193        $crate::List::new( ::kas::collection! [ $( $ee ),+ ] )
194    };
195}
196
197/// A generic row widget
198///
199/// See documentation of [`List`] type.
200pub type Row<C> = List<C, Right>;
201
202/// A generic column widget
203///
204/// See documentation of [`List`] type.
205pub type Column<C> = List<C, Down>;
206
207#[impl_self]
208mod List {
209    /// A generic row/column widget
210    ///
211    /// A linear widget over a [`Collection`] of widgets.
212    ///
213    /// When the collection uses [`Vec`], various methods to insert/remove
214    /// elements are available.
215    ///
216    /// The layout direction `D` may be compile-time fixed (e.g. [`Right`]) or
217    /// run-time mutable ([`Direction`]); in the latter case
218    /// [`set_direction`] is available.
219    ///
220    /// ## See also
221    ///
222    /// [`Row`] and [`Column`] are type-defs to `List` which fix the direction `D`.
223    ///
224    /// The macros [`row!`] and [`column!`] also create row/column
225    /// layouts, but are not fully equivalent:
226    ///
227    /// -   `row!` and `column!` generate anonymous layout widgets (or objects).
228    ///     These do not have a [`set_direction`] method or support adding or
229    ///     removing elements.
230    /// -   `row!` and `column!` generate layout objects which, when used within
231    ///     a custom widget, may refer to that widget's fields.
232    ///
233    /// ## Performance
234    ///
235    /// Configuring and resizing elements is O(n) in the number of children.
236    /// Drawing and event handling is O(log n) in the number of children (assuming
237    /// only a small number are visible at any one time).
238    ///
239    /// ## Example
240    ///
241    /// ```
242    /// use kas::collection;
243    /// # use kas_widgets::{CheckBox, List};
244    ///
245    /// let list = List::right(collection![
246    ///     "A checkbox",
247    ///     CheckBox::new(|_, state: &bool| *state),
248    /// ]);
249    /// ```
250    ///
251    /// [`row!`]: crate::row
252    /// [`column!`]: crate::column
253    /// [`set_direction`]: List::set_direction
254    #[autoimpl(Default where C: Default, D: Default)]
255    #[widget]
256    pub struct List<C: Collection, D: Directional> {
257        core: widget_core!(),
258        layout: DynRowStorage,
259        #[collection]
260        widgets: C,
261        direction: D,
262        next: usize,
263        id_map: HashMap<usize, usize>, // map key of Id to index
264    }
265
266    impl Layout for Self {
267        fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
268            let dim = (self.direction, self.widgets.len());
269            let mut solver = RowSolver::new(axis, dim, &mut self.layout);
270            for n in 0..self.widgets.len() {
271                if let Some(child) = self.widgets.get_mut_tile(n) {
272                    solver.for_child(&mut self.layout, n, |axis| {
273                        child.size_rules(sizer.re(), axis)
274                    });
275                }
276            }
277            solver.finish(&mut self.layout)
278        }
279
280        fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
281            widget_set_rect!(rect);
282            let dim = (self.direction, self.widgets.len());
283            let mut setter = RowSetter::<D, Vec<i32>, _>::new(rect, dim, &mut self.layout);
284
285            for n in 0..self.widgets.len() {
286                if let Some(child) = self.widgets.get_mut_tile(n) {
287                    child.set_rect(cx, setter.child_rect(&mut self.layout, n), hints);
288                }
289            }
290        }
291
292        fn draw(&self, mut draw: DrawCx) {
293            let solver = RowPositionSolver::new(self.direction);
294            solver.for_children(&self.widgets, draw.get_clip_rect(), |w| w.draw(draw.re()));
295        }
296    }
297
298    impl Tile for Self {
299        fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
300            Role::None
301        }
302
303        fn find_child_index(&self, id: &Id) -> Option<usize> {
304            id.next_key_after(self.id_ref())
305                .and_then(|k| self.id_map.get(&k).cloned())
306        }
307
308        fn probe(&self, coord: Coord) -> Id {
309            let solver = RowPositionSolver::new(self.direction);
310            solver
311                .find_child(&self.widgets, coord)
312                .and_then(|child| child.try_probe(coord))
313                .unwrap_or_else(|| self.id())
314        }
315    }
316
317    impl Events for Self {
318        type Data = C::Data;
319
320        /// Make a fresh id based on `self.next` then insert into `self.id_map`
321        fn make_child_id(&mut self, index: usize) -> Id {
322            if let Some(child) = self.widgets.get_tile(index) {
323                // Use the widget's existing identifier, if valid
324                if child.id_ref().is_valid() && self.id_ref().is_ancestor_of(child.id_ref()) {
325                    if let Some(key) = child.id_ref().next_key_after(self.id_ref()) {
326                        if let Entry::Vacant(entry) = self.id_map.entry(key) {
327                            entry.insert(index);
328                            return child.id();
329                        }
330                    }
331                }
332            }
333
334            loop {
335                let key = self.next;
336                self.next += 1;
337                if let Entry::Vacant(entry) = self.id_map.entry(key) {
338                    entry.insert(index);
339                    return self.id_ref().make_child(key);
340                }
341            }
342        }
343
344        fn configure(&mut self, _: &mut ConfigCx) {
345            // All children will be re-configured which will rebuild id_map
346            self.id_map.clear();
347        }
348    }
349
350    impl Self
351    where
352        D: Default,
353    {
354        /// Construct a new instance with default-constructed direction
355        ///
356        /// This constructor is available where the direction is determined by the
357        /// type: for `D: Directional + Default`. In other cases, use
358        /// [`Self::new_dir`].
359        ///
360        /// # Examples
361        ///
362        /// Where widgets have the same type and the length is fixed, an array
363        /// may be used:
364        /// ```
365        /// use kas_widgets::{Label, Row};
366        /// let _ = Row::new([Label::new("left"), Label::new("right")]);
367        /// ```
368        ///
369        /// To support run-time insertion/deletion, use [`Vec`]:
370        /// ```
371        /// use kas_widgets::{AdaptWidget, Button, Row};
372        ///
373        /// #[derive(Clone, Debug)]
374        /// enum Msg {
375        ///     Add,
376        ///     Remove,
377        /// }
378        ///
379        /// let _ = Row::new(vec![Button::label_msg("Add", Msg::Add)])
380        ///     .on_messages(|cx, row, data| {
381        ///         if let Some(msg) = cx.try_pop() {
382        ///             match msg {
383        ///                 Msg::Add => {
384        ///                     let button = if row.len() % 2 == 0 {
385        ///                         Button::label_msg("Add", Msg::Add)
386        ///                     } else {
387        ///                         Button::label_msg("Remove", Msg::Remove)
388        ///                     };
389        ///                     row.push(&mut cx.config_cx(), data, button);
390        ///                 }
391        ///                 Msg::Remove => {
392        ///                     let _ = row.pop(&mut cx.config_cx());
393        ///                 }
394        ///             }
395        ///         }
396        ///     });
397        /// ```
398        #[inline]
399        pub fn new(widgets: C) -> Self {
400            Self::new_dir(widgets, D::default())
401        }
402    }
403
404    impl<C: Collection> List<C, kas::dir::Left> {
405        /// Construct a new instance with fixed direction
406        #[inline]
407        pub fn left(widgets: C) -> Self {
408            Self::new(widgets)
409        }
410    }
411    impl<C: Collection> List<C, kas::dir::Right> {
412        /// Construct a new instance with fixed direction
413        #[inline]
414        pub fn right(widgets: C) -> Self {
415            Self::new(widgets)
416        }
417    }
418    impl<C: Collection> List<C, kas::dir::Up> {
419        /// Construct a new instance with fixed direction
420        #[inline]
421        pub fn up(widgets: C) -> Self {
422            Self::new(widgets)
423        }
424    }
425    impl<C: Collection> List<C, kas::dir::Down> {
426        /// Construct a new instance with fixed direction
427        #[inline]
428        pub fn down(widgets: C) -> Self {
429            Self::new(widgets)
430        }
431    }
432
433    impl<C: Collection, D: Directional + Eq> List<C, D> {
434        /// Set the direction of contents
435        pub fn set_direction(&mut self, cx: &mut EventState, direction: D) {
436            if direction == self.direction {
437                return;
438            }
439
440            self.direction = direction;
441            // Note: most of the time Action::SET_RECT would be enough, but margins can be different
442            cx.resize(self);
443        }
444    }
445
446    impl Self {
447        /// Construct a new instance with explicit direction
448        #[inline]
449        pub fn new_dir(widgets: C, direction: D) -> Self {
450            List {
451                core: Default::default(),
452                layout: Default::default(),
453                widgets,
454                direction,
455                next: 0,
456                id_map: Default::default(),
457            }
458        }
459
460        /// Get the direction of contents
461        pub fn direction(&self) -> Direction {
462            self.direction.as_direction()
463        }
464
465        /// Set the direction of contents (inline)
466        #[inline]
467        pub fn with_direction(mut self, direction: D) -> Self {
468            self.direction = direction;
469            self
470        }
471
472        /// Access layout storage
473        ///
474        /// The number of columns/rows is [`Self::len`].
475        #[inline]
476        pub fn layout_storage(&mut self) -> &mut (impl RowStorage + use<C, D>) {
477            &mut self.layout
478        }
479
480        /// True if there are no child widgets
481        pub fn is_empty(&self) -> bool {
482            self.widgets.is_empty()
483        }
484
485        /// Returns the number of child widgets
486        pub fn len(&self) -> usize {
487            self.widgets.len()
488        }
489    }
490
491    impl<W: Widget, D: Directional> List<Vec<W>, D> {
492        /// Returns a reference to the child, if any
493        pub fn get(&self, index: usize) -> Option<&W> {
494            self.widgets.get(index)
495        }
496
497        /// Returns a mutable reference to the child, if any
498        pub fn get_mut(&mut self, index: usize) -> Option<&mut W> {
499            self.widgets.get_mut(index)
500        }
501
502        /// Remove all child widgets
503        pub fn clear(&mut self) {
504            self.widgets.clear();
505        }
506
507        /// Append a child widget
508        ///
509        /// The new child is configured immediately. [`Action::RESIZE`] is
510        /// triggered.
511        ///
512        /// Returns the new element's index.
513        pub fn push(&mut self, cx: &mut ConfigCx, data: &W::Data, mut widget: W) -> usize {
514            let index = self.widgets.len();
515            let id = self.make_child_id(index);
516            cx.configure(widget.as_node(data), id);
517            self.widgets.push(widget);
518
519            cx.resize(self);
520            index
521        }
522
523        /// Remove the last child widget (if any) and return
524        ///
525        /// Triggers [`Action::RESIZE`].
526        pub fn pop(&mut self, cx: &mut EventState) -> Option<W> {
527            let result = self.widgets.pop();
528            if let Some(w) = result.as_ref() {
529                cx.resize(&self);
530
531                if w.id_ref().is_valid() {
532                    if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
533                        self.id_map.remove(&key);
534                    }
535                }
536            }
537            result
538        }
539
540        /// Inserts a child widget position `index`
541        ///
542        /// Panics if `index > len`.
543        ///
544        /// The new child is configured immediately. Triggers [`Action::RESIZE`].
545        pub fn insert(&mut self, cx: &mut ConfigCx, data: &W::Data, index: usize, mut widget: W) {
546            for v in self.id_map.values_mut() {
547                if *v >= index {
548                    *v += 1;
549                }
550            }
551
552            let id = self.make_child_id(index);
553            cx.configure(widget.as_node(data), id);
554            self.widgets.insert(index, widget);
555            cx.resize(self);
556        }
557
558        /// Removes the child widget at position `index`
559        ///
560        /// Panics if `index` is out of bounds.
561        ///
562        /// Triggers [`Action::RESIZE`].
563        pub fn remove(&mut self, cx: &mut EventState, index: usize) -> W {
564            let w = self.widgets.remove(index);
565            if w.id_ref().is_valid() {
566                if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
567                    self.id_map.remove(&key);
568                }
569            }
570
571            cx.resize(&self);
572
573            for v in self.id_map.values_mut() {
574                if *v > index {
575                    *v -= 1;
576                }
577            }
578            w
579        }
580
581        /// Replace the child at `index`
582        ///
583        /// Panics if `index` is out of bounds.
584        ///
585        /// The new child is configured immediately. Triggers [`Action::RESIZE`].
586        pub fn replace(&mut self, cx: &mut ConfigCx, data: &W::Data, index: usize, mut w: W) -> W {
587            let id = self.make_child_id(index);
588            cx.configure(w.as_node(data), id);
589            std::mem::swap(&mut w, &mut self.widgets[index]);
590
591            if w.id_ref().is_valid() {
592                if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
593                    self.id_map.remove(&key);
594                }
595            }
596
597            cx.resize(self);
598
599            w
600        }
601
602        /// Append child widgets from an iterator
603        ///
604        /// New children are configured immediately. Triggers [`Action::RESIZE`].
605        pub fn extend<T>(&mut self, cx: &mut ConfigCx, data: &W::Data, iter: T)
606        where
607            T: IntoIterator<Item = W>,
608        {
609            let iter = iter.into_iter();
610            if let Some(ub) = iter.size_hint().1 {
611                self.widgets.reserve(ub);
612            }
613            for mut w in iter {
614                let id = self.make_child_id(self.widgets.len());
615                cx.configure(w.as_node(data), id);
616                self.widgets.push(w);
617            }
618
619            cx.resize(self);
620        }
621
622        /// Resize, using the given closure to construct new widgets
623        ///
624        /// New children are configured immediately. Triggers [`Action::RESIZE`].
625        pub fn resize_with<F>(&mut self, cx: &mut ConfigCx, data: &W::Data, len: usize, f: F)
626        where
627            F: Fn(usize) -> W,
628        {
629            let old_len = self.widgets.len();
630
631            if len < old_len {
632                cx.resize(&self);
633                loop {
634                    let w = self.widgets.pop().unwrap();
635                    if w.id_ref().is_valid() {
636                        if let Some(key) = w.id_ref().next_key_after(self.id_ref()) {
637                            self.id_map.remove(&key);
638                        }
639                    }
640                    if len == self.widgets.len() {
641                        return;
642                    }
643                }
644            }
645
646            if len > old_len {
647                self.widgets.reserve(len - old_len);
648                for index in old_len..len {
649                    let id = self.make_child_id(index);
650                    let mut w = f(index);
651                    cx.configure(w.as_node(data), id);
652                    self.widgets.push(w);
653                }
654                cx.resize(self);
655            }
656        }
657
658        /// Iterate over childern
659        pub fn iter(&self) -> impl Iterator<Item = &W> {
660            ListIter {
661                list: &self.widgets,
662            }
663        }
664
665        /// Mutably iterate over childern
666        pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut W> {
667            ListIterMut {
668                list: &mut self.widgets,
669            }
670        }
671    }
672
673    impl<W: Widget, D: Directional> Index<usize> for List<Vec<W>, D> {
674        type Output = W;
675
676        fn index(&self, index: usize) -> &Self::Output {
677            &self.widgets[index]
678        }
679    }
680
681    impl<W: Widget, D: Directional> IndexMut<usize> for List<Vec<W>, D> {
682        fn index_mut(&mut self, index: usize) -> &mut Self::Output {
683            &mut self.widgets[index]
684        }
685    }
686}
687
688struct ListIter<'a, W: Widget> {
689    list: &'a [W],
690}
691impl<'a, W: Widget> Iterator for ListIter<'a, W> {
692    type Item = &'a W;
693    fn next(&mut self) -> Option<Self::Item> {
694        if let Some((first, rest)) = self.list.split_first() {
695            self.list = rest;
696            Some(first)
697        } else {
698            None
699        }
700    }
701    fn size_hint(&self) -> (usize, Option<usize>) {
702        let len = self.len();
703        (len, Some(len))
704    }
705}
706impl<'a, W: Widget> ExactSizeIterator for ListIter<'a, W> {
707    fn len(&self) -> usize {
708        self.list.len()
709    }
710}
711
712struct ListIterMut<'a, W: Widget> {
713    list: &'a mut [W],
714}
715impl<'a, W: Widget> Iterator for ListIterMut<'a, W> {
716    type Item = &'a mut W;
717    fn next(&mut self) -> Option<Self::Item> {
718        let list = std::mem::take(&mut self.list);
719        if let Some((first, rest)) = list.split_first_mut() {
720            self.list = rest;
721            Some(first)
722        } else {
723            None
724        }
725    }
726    fn size_hint(&self) -> (usize, Option<usize>) {
727        let len = self.len();
728        (len, Some(len))
729    }
730}
731impl<'a, W: Widget> ExactSizeIterator for ListIterMut<'a, W> {
732    fn len(&self) -> usize {
733        self.list.len()
734    }
735}