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}