kas_view/data_clerk.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//! Traits for shared data objects
7
8#[allow(unused)] use crate::SelectionMsg;
9use kas::Id;
10use kas::cast::Cast;
11use kas::event::{ConfigCx, EventCx};
12#[allow(unused)] use kas::{Action, Events, Widget};
13use std::borrow::Borrow;
14use std::fmt::Debug;
15use std::ops::Range;
16
17/// Bounds on the key type
18///
19/// This type should be small, easy to copy, and without internal mutability.
20pub trait DataKey: Clone + Debug + Default + PartialEq + Eq + 'static {
21 /// Make an [`Id`] for a key
22 ///
23 /// The result must be distinct from `parent` and a descendant of `parent`
24 /// (use [`Id::make_child`] for this, optionally more than once).
25 fn make_id(&self, parent: &Id) -> Id;
26
27 /// Reconstruct a key from an [`Id`]
28 ///
29 /// Where `child` is the output of [`Self::make_id`] for the same `parent`
30 /// *or any [`Id`] descended from that*, this should return a copy of
31 /// the `key` passed to `make_id`.
32 ///
33 /// See: [`Id::next_key_after`], [`Id::iter_keys_after`]
34 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self>;
35}
36
37impl DataKey for () {
38 fn make_id(&self, parent: &Id) -> Id {
39 // We need a distinct child, so use index 0
40 parent.make_child(0)
41 }
42
43 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
44 if child.next_key_after(parent) == Some(0) {
45 Some(())
46 } else {
47 None
48 }
49 }
50}
51
52// NOTE: we cannot use this blanket impl without specialisation / negative impls
53// impl<Key: Cast<usize> + Clone + Debug + PartialEq + Eq + 'static> DataKey for Key
54macro_rules! impl_1D {
55 ($t:ty) => {
56 impl DataKey for $t {
57 fn make_id(&self, parent: &Id) -> Id {
58 parent.make_child((*self).cast())
59 }
60
61 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
62 child.next_key_after(parent).map(|i| i.cast())
63 }
64 }
65 };
66}
67impl_1D!(usize);
68impl_1D!(u32);
69#[cfg(target_pointer_width = "64")]
70impl_1D!(u64);
71
72macro_rules! impl_2D {
73 ($t:ty) => {
74 impl DataKey for ($t, $t) {
75 fn make_id(&self, parent: &Id) -> Id {
76 parent.make_child(self.0.cast()).make_child(self.1.cast())
77 }
78
79 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
80 let mut iter = child.iter_keys_after(parent);
81 let col = iter.next().map(|i| i.cast());
82 let row = iter.next().map(|i| i.cast());
83 col.zip(row)
84 }
85 }
86 };
87}
88impl_2D!(usize);
89impl_2D!(u32);
90#[cfg(target_pointer_width = "64")]
91impl_2D!(u64);
92
93/// Indicates whether an update to a [`DataClerk`] changes any keys or values
94#[derive(Clone, Debug, PartialEq, Eq)]
95#[must_use]
96pub enum DataChanges<Index> {
97 /// Indicates that no changes to the data set occurred.
98 None,
99 /// Indicates that changes to the data set may have occurred, but that
100 /// [`DataClerk::update_token`] and [`DataClerk::item`] results are
101 /// unchanged for the `view_range`.
102 NoPreparedItems,
103 /// Indicates that tokens for the given range may require an update
104 /// and/or that items for the given range have changed.
105 /// [`DataClerk::update_token`] will be called for each index in the
106 /// intersection of the given range with the `view_range`.
107 Range(Range<Index>),
108 /// `Any` indicates that changes to the data set may have occurred.
109 Any,
110}
111
112/// Return value of [`DataClerk::update_token`]
113#[derive(Clone, Copy, Debug, PartialEq, Eq)]
114#[must_use]
115pub enum TokenChanges {
116 /// `None` indicates that no changes to the token occurred.
117 None,
118 /// `SameKey` indicates that while the token still represents the same key,
119 /// the associated data item may have changed.
120 SameKey,
121 /// `Any` indicates that the data key (and item) may have changed.
122 Any,
123}
124
125impl TokenChanges {
126 pub(crate) fn key(self) -> bool {
127 self == TokenChanges::Any
128 }
129
130 pub(crate) fn item(self) -> bool {
131 self != TokenChanges::None
132 }
133}
134
135/// Result of [`Self::len`]
136#[derive(Clone, Copy, Debug, PartialEq, Eq)]
137pub enum DataLen<Index> {
138 /// Length is known and specified exactly
139 Known(Index),
140 /// A lower bound on length is specified
141 LBound(Index),
142}
143
144impl<Index: Copy> DataLen<Index> {
145 /// Returns the length payload (known or lower bound)
146 #[inline]
147 pub fn len(&self) -> Index {
148 match self {
149 DataLen::Known(len) => *len,
150 DataLen::LBound(len) => *len,
151 }
152 }
153
154 /// Returns true if a known length given
155 #[inline]
156 pub fn is_known(&self) -> bool {
157 matches!(self, DataLen::Known(_))
158 }
159}
160
161/// Data access manager
162///
163/// A `DataClerk` manages access to a data set, using an `Index` type specified by
164/// the [view controller](crate#view-controller).
165///
166/// In simpler cases it is sufficient to implement only required methods.
167pub trait DataClerk<Index> {
168 /// Input data type (of parent widget)
169 ///
170 /// Data of this type is passed through the parent widget; see
171 /// [`Widget::Data`] and the [`Events`] trait. This input data might be used
172 /// to access a data set stored in another widget or to pass a query or
173 /// filter into the `DataClerk`.
174 ///
175 /// Note that it is not currently possible to pass in references to multiple
176 /// data items (such as an external data set and a filter) via `Data`. This
177 /// would require use of Generic Associated Types (GATs), not only here but
178 /// also in the [`Widget`](kas::Widget) trait; alas, GATs are not (yet)
179 /// compatible with dyn traits and Kas requires use of `dyn Widget`. Instead
180 /// one can share the data set (e.g. `Rc<RefCell<DataSet>>`) or store within
181 /// the `DataClerk` using the `clerk` / `clerk_mut` methods to access; in
182 /// both cases it may be necessary to update the view controller explicitly
183 /// (e.g. `cx.update(list.as_node(&input))`) after the data set changes.
184 type Data;
185
186 /// Key type
187 ///
188 /// All data items should have a stable key so that data items may be
189 /// tracked through changing queries. This allows focus and selection to
190 /// correctly track items when the data query or filter changes.
191 ///
192 /// Where the query is fixed, this can just be the `Index` type.
193 type Key: DataKey;
194
195 /// Item type
196 ///
197 /// `&Item` is passed to child view widgets as input data.
198 type Item;
199
200 /// Token type
201 ///
202 /// Each view widget is stored with a corresponding token set by
203 /// [`Self::update_token`].
204 ///
205 /// Often this will either be [`Self::Key`] or
206 /// <code>[Token](crate::Token)<[Self::Key], [Self::Item]></code>.
207 type Token: Borrow<Self::Key>;
208
209 /// Update the clerk
210 ///
211 /// This is called by [`kas::Events::update`]. It should update `self` as
212 /// required reflecting possible data-changes and indicate through the
213 /// returned [`DataChanges`] value the updates required to tokens and views.
214 ///
215 /// Data items within `view_range` may be visible.
216 ///
217 /// Note: this method is called automatically when input data changes. When
218 /// data owned or referenced by the `DataClerk` implementation is changed it
219 /// may be necessary to explicitly update the view controller, e.g. using
220 /// [`ConfigCx::update`] or [`Action::UPDATE`].
221 ///
222 /// This method may be called frequently and without changes to `data`.
223 /// It is expected to be fast and non-blocking. Asynchronous updates to
224 /// `self` are possible using [`Self::handle_messages`].
225 fn update(
226 &mut self,
227 cx: &mut ConfigCx,
228 id: Id,
229 view_range: Range<Index>,
230 data: &Self::Data,
231 ) -> DataChanges<Index>;
232
233 /// Get the number of indexable items
234 ///
235 /// Scroll bars and the `view_range` are
236 /// limited by the result of this method.
237 ///
238 /// Where the data set size is a known fixed `len` (or unfixed but with
239 /// maximum `len <= lbound`), this method should return
240 /// <code>[DataLen::Known][](len)</code>.
241 ///
242 /// Where the data set size is unknown (or unfixed and greater than
243 /// `lbound`), this method should return
244 /// <code>[DataLen::LBound][](lbound)</code>.
245 ///
246 /// `lbound` is set to allow scrolling a little beyond the current view
247 /// position (i.e. a little larger than the last prepared `range.end`).
248 fn len(&self, data: &Self::Data, lbound: Index) -> DataLen<Index>;
249
250 /// Prepare a range
251 ///
252 /// This method is called prior to [`Self::update_token`] over the indices
253 /// in `range`. If data is to be loaded from a remote source or computed in
254 /// a worker thread, it should be done so from here using `async` worker(s)
255 /// (see [`Self::handle_messages`]).
256 ///
257 /// Data items within `view_range` may be visible.
258 ///
259 /// The passed `range` may be a subset of the `view_range` but does
260 /// not exceed it; pre-emptive loading is left to the implementation.
261 /// This method may be called frequently and without changes to `range`, and
262 /// is expected to be fast and non-blocking.
263 ///
264 /// The default implementation does nothing.
265 fn prepare_range(
266 &mut self,
267 cx: &mut ConfigCx,
268 id: Id,
269 view_range: Range<Index>,
270 data: &Self::Data,
271 range: Range<Index>,
272 ) {
273 let _ = (cx, id, view_range, data, range);
274 }
275
276 /// Handle an async message
277 ///
278 /// This method is called when a message is available. Such messages may be
279 /// taken using [`EventCx::try_pop`]. Messages may be received from:
280 ///
281 /// - The view widget for `key` when `opt_key = Some(key)`.
282 /// - [`SelectionMsg`] may be received from the view controller.
283 /// - [`Self::update`], [`Self::prepare_range`] and this method may send
284 /// `async` messages using `cx.send_async(controller.id(), SomeMessage { .. })`.
285 ///
286 /// Data items within `view_range` may be visible.
287 ///
288 /// The default implementation does nothing.
289 fn handle_messages(
290 &mut self,
291 cx: &mut EventCx,
292 id: Id,
293 view_range: Range<Index>,
294 data: &Self::Data,
295 opt_key: Option<Self::Key>,
296 ) -> DataChanges<Index> {
297 let _ = (cx, id, view_range, data, opt_key);
298 DataChanges::None
299 }
300
301 /// Update a token for the given `index`
302 ///
303 /// This method is called after [`Self::prepare_range`] for each `index` in
304 /// the prepared `range` in order to prepare a to prepare a token for each
305 /// item (see [`Self::item`]).
306 ///
307 /// The input `token` (if any) may or may not correspond to the given
308 /// `index`. This method should prepare it as follows:
309 ///
310 /// - If no item is currently available for `index`, set `*token = None`
311 /// and return any value of [`TokenChanges`].
312 /// - Otherwise, if the input `token` is `None` or corresponds to a
313 /// different `index`, replace `token` and report [`TokenChanges::Any`].
314 /// - Otherwise, if then token depends on (caches) the data item and
315 /// `update_item`, the token should be updated. The method should report
316 /// [`TokenChanges::SameKey`] when the token has changed.
317 /// - Finally (if none of the above), report [`TokenChanges::None`].
318 ///
319 /// This method should be fast since it may be called repeatedly. Slow and
320 /// blocking operations should be run asynchronously from
321 /// [`Self::prepare_range`] using an internal cache.
322 fn update_token(
323 &self,
324 data: &Self::Data,
325 index: Index,
326 update_item: bool,
327 token: &mut Option<Self::Token>,
328 ) -> TokenChanges;
329
330 /// Get the data item for the given `token`
331 ///
332 /// Data cannot be generated by this method but it can be generated by
333 /// [`Self::update_token`] and cached within a [`Token`](crate::Token).
334 /// (see [`Self::Token`]).
335 ///
336 /// A token is expected to be able to resolve an item. Since [`Self::Token`]
337 /// does not support `Clone` it is known that items can be evicted from
338 /// storage when their token is replaced.
339 ///
340 /// This method should be fast since it may be called repeatedly.
341 fn item<'r>(&'r self, data: &'r Self::Data, token: &'r Self::Token) -> &'r Self::Item;
342}