Skip to main content

kas_view/
driver.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//! View drivers
7//!
8//! The [`Driver`] trait is used as a binding between data models and
9//! controllers. Implementations define the view (using widgets) and
10//! message handling (mapping widget messages to actions).
11//!
12//! A basic implementation is provided: [`View`] provides a simple read-only
13//! view over content (text labels in all cases except `bool`, which uses a
14//! read-only [`CheckBox`]).
15//!
16//! Intended usage is to import the module name rather than its contents, thus
17//! allowing referal to e.g. `driver::View`.
18
19#[allow(unused)] use crate::clerk::AsyncClerk;
20use kas::TextOrSource;
21use kas::prelude::*;
22use kas_widgets::{CheckBox, Text};
23use std::default::Default;
24
25/// View widget driver
26///
27/// Implementations of this trait [make](Self::make) new view widgets and
28/// optionally [reassign](Self::set_key) existing view widgets.
29///
30/// View widgets may also need to update themselves using [`Events::update`].
31///
32/// Each view widget has an [`Id`] corresponding to its data item, and
33/// handles events like any other widget. In order to associate a returned
34/// message with a `Key`, either embed that key while constructing
35/// the widget with [`Driver::make`] or handle the message in
36/// [`AsyncClerk::handle_messages`].
37///
38/// # Example implementations
39///
40/// It is expected that a custom implementation is created for each usage. A
41/// simple example might just map input data to a [`Text`] widget:
42/// ```
43/// use kas_view::Driver;
44/// use kas_widgets::Text;
45///
46/// struct MyDriver;
47/// impl<Key> Driver<Key, f32> for MyDriver {
48///     const TAB_NAVIGABLE: bool = false;
49///     type Widget = Text<f32>;
50///
51///     fn make(&mut self, _: &Key) -> Self::Widget {
52///         Text::new_gen(|_, data: &f32| data.to_string())
53///     }
54///     fn set_key(&mut self, _: &mut Self::Widget, _: &Key) {
55///         // Text has no metadata that needs to be reset
56///     }
57///     fn navigable(widget: &Self::Widget) -> bool {
58///         true
59///     }
60/// }
61/// ```
62#[autoimpl(for<T: trait + ?Sized> &mut T, Box<T>)]
63pub trait Driver<Key, Item> {
64    /// If `true`, then <kbd>Tab</kbd> will navigate between list items,
65    /// otherwise <kbd>Tab</kbd> will only select the last-used item.
66    ///
67    /// Arrow keys should be able to navigate between items regardless (if
68    /// [`Self::navigable`] returns true or another widget is navigable which
69    /// doesn't handle arrow keys), thus it is usually recommended to set this
70    /// to `false`.
71    const TAB_NAVIGABLE: bool;
72
73    /// Type of the widget used to view data
74    type Widget: kas::Widget<Data = Item>;
75
76    /// Construct a new view widget
77    fn make(&mut self, key: &Key) -> Self::Widget;
78
79    /// Called to bind an existing view widget to a new key
80    ///
81    /// This should reset widget metadata, for example so that when a view
82    /// widget with a text selection is assigned to a new key it does not
83    /// attempt to apply the old selection to the new text.
84    ///
85    /// This does not need to set data; [`Events::update`] does that.
86    ///
87    /// The default implementation simply replaces widget with `self.make(key)`,
88    /// which is sufficient, if not always optimal.
89    fn set_key(&mut self, widget: &mut Self::Widget, key: &Key) {
90        *widget = self.make(key);
91    }
92
93    /// Whether the `Widget` wrapper should be keyboard navigable
94    fn navigable(widget: &Self::Widget) -> bool;
95
96    /// Get optional label for widgets
97    ///
98    /// This allows accessibility tools to read an item's label on focus. For complex
99    /// widgets supporting focus this may not be wanted. Defaults to `None`.
100    fn label(widget: &Self::Widget) -> Option<TextOrSource<'_>> {
101        let _ = widget;
102        None
103    }
104}
105
106/// Default view widget constructor
107///
108/// This struct implements [`Driver`], using a default widget for the data type:
109///
110/// -   [`kas_widgets::Text`] for `String`, `&str`, integer and float types
111/// -   [`kas_widgets::CheckBox`] (read-only) for the bool type TODO
112#[derive(Clone, Copy, Debug, Default)]
113pub struct View;
114
115macro_rules! impl_via_to_string {
116    ($t:ty) => {
117        impl<Key> Driver<Key, $t> for View {
118            const TAB_NAVIGABLE: bool = false;
119            type Widget = Text<$t>;
120
121            fn make(&mut self, _: &Key) -> Self::Widget {
122                Text::new_gen(|_, data: &$t| data.to_string())
123            }
124            fn set_key(&mut self, _: &mut Self::Widget, _: &Key) {
125                // Text has no metadata that needs to be reset
126            }
127            fn navigable(_: &Self::Widget) -> bool {
128                true
129            }
130            fn label(widget: &Self::Widget) -> Option<TextOrSource<'_>> {
131                Some(widget.id().into())
132            }
133        }
134    };
135    ($t:ty, $($tt:ty),+) => {
136        impl_via_to_string!($t);
137        impl_via_to_string!($($tt),+);
138    };
139}
140impl_via_to_string!(String, &'static str);
141impl_via_to_string!(i8, i16, i32, i64, i128, isize);
142impl_via_to_string!(u8, u16, u32, u64, u128, usize);
143impl_via_to_string!(f32, f64);
144
145impl<Key> Driver<Key, bool> for View {
146    const TAB_NAVIGABLE: bool = false;
147    type Widget = CheckBox<bool>;
148
149    fn make(&mut self, _: &Key) -> Self::Widget {
150        CheckBox::new(|_, data: &bool| *data).with_editable(false)
151    }
152    fn set_key(&mut self, _: &mut Self::Widget, _: &Key) {
153        // CheckBox has no metadata that needs to be reset
154    }
155    fn navigable(_: &Self::Widget) -> bool {
156        false
157    }
158}