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}