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
19use kas::TextOrSource;
20use kas::prelude::*;
21use kas_widgets::{CheckBox, Text};
22use std::default::Default;
23
24/// View widget driver
25///
26/// Implementations of this trait [make](Self::make) new view widgets and
27/// optionally [reassign](Self::set_key) existing view widgets.
28///
29/// View widgets may also need to update themselves using [`Events::update`].
30///
31/// Each view widget has an [`Id`] corresponding to its data item, and
32/// handles events like any other widget. In order to associate a returned
33/// message with a `Key`, either embed that key while constructing
34/// the widget with [`Driver::make`] or handle the message in
35/// [`crate::DataClerk::handle_messages`].
36///
37/// # Example implementations
38///
39/// It is expected that a custom implementation is created for each usage. A
40/// simple example might just map input data to a [`Text`] widget:
41/// ```
42/// use kas_view::Driver;
43/// use kas_widgets::Text;
44///
45/// struct MyDriver;
46/// impl<Key> Driver<Key, f32> for MyDriver {
47/// type Widget = Text<f32, String>;
48/// fn make(&mut self, _: &Key) -> Self::Widget {
49/// Text::new(|_, data: &f32| data.to_string())
50/// }
51/// fn set_key(&mut self, _: &mut Self::Widget, _: &Key) {
52/// // Text has no metadata that needs to be reset
53/// }
54/// }
55/// ```
56#[autoimpl(for<T: trait + ?Sized> &mut T, Box<T>)]
57pub trait Driver<Key, Item> {
58 /// Type of the widget used to view data
59 type Widget: kas::Widget<Data = Item>;
60
61 /// Construct a new view widget
62 fn make(&mut self, key: &Key) -> Self::Widget;
63
64 /// Called to bind an existing view widget to a new key
65 ///
66 /// This should reset widget metadata, for example so that when a view
67 /// widget with a text selection is assigned to a new key it does not
68 /// attempt to apply the old selection to the new text.
69 ///
70 /// This does not need to set data; [`Events::update`] does that.
71 ///
72 /// The default implementation simply replaces widget with `self.make(key)`,
73 /// which is sufficient, if not always optimal.
74 fn set_key(&mut self, widget: &mut Self::Widget, key: &Key) {
75 *widget = self.make(key);
76 }
77
78 /// Whether the `Widget` wrapper should be keyboard navigable
79 fn navigable(widget: &Self::Widget) -> bool;
80
81 /// Get optional label for widgets
82 ///
83 /// This allows accessibility tools to read an item's label on focus. For complex
84 /// widgets supporting focus this may not be wanted. Defaults to `None`.
85 fn label(widget: &Self::Widget) -> Option<TextOrSource<'_>> {
86 let _ = widget;
87 None
88 }
89}
90
91/// Default view widget constructor
92///
93/// This struct implements [`Driver`], using a default widget for the data type:
94///
95/// - [`kas_widgets::Text`] for `String`, `&str`, integer and float types
96/// - [`kas_widgets::CheckBox`] (read-only) for the bool type TODO
97#[derive(Clone, Copy, Debug, Default)]
98pub struct View;
99
100macro_rules! impl_via_to_string {
101 ($t:ty) => {
102 impl<Key> Driver<Key, $t> for View {
103 type Widget = Text<$t, String>;
104 fn make(&mut self, _: &Key) -> Self::Widget {
105 Text::new(|_, data: &$t| data.to_string())
106 }
107 fn set_key(&mut self, _: &mut Self::Widget, _: &Key) {
108 // Text has no metadata that needs to be reset
109 }
110 fn navigable(_: &Self::Widget) -> bool {
111 true
112 }
113 fn label(widget: &Self::Widget) -> Option<TextOrSource<'_>> {
114 Some(widget.id().into())
115 }
116 }
117 };
118 ($t:ty, $($tt:ty),+) => {
119 impl_via_to_string!($t);
120 impl_via_to_string!($($tt),+);
121 };
122}
123impl_via_to_string!(String, &'static str);
124impl_via_to_string!(i8, i16, i32, i64, i128, isize);
125impl_via_to_string!(u8, u16, u32, u64, u128, usize);
126impl_via_to_string!(f32, f64);
127
128impl<Key> Driver<Key, bool> for View {
129 type Widget = CheckBox<bool>;
130 fn make(&mut self, _: &Key) -> Self::Widget {
131 CheckBox::new(|_, data: &bool| *data).with_editable(false)
132 }
133 fn set_key(&mut self, _: &mut Self::Widget, _: &Key) {
134 // CheckBox has no metadata that needs to be reset
135 }
136 fn navigable(_: &Self::Widget) -> bool {
137 false
138 }
139}