kas_view/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//! Data clerks
7//!
8//! # Interfaces
9//!
10//! A clerk manages a view or query over a data set using an `Index` type
11//! specified by the [view controller](crate#view-controller). For
12//! [`ListView`](crate::ListView), `Index = usize`.
13//!
14//! All clerks must implement [`Clerk`]:
15//!
16//! - [`Clerk`] covers the base functionality required by all clerks
17//!
18//! ## Data generators
19//!
20//! Generator clerks construct owned items. One of the following traits must be
21//! implemented:
22//!
23//! - [`IndexedGenerator`] provides a very simple interface: `update` and
24//! `generate`.
25//! - [`KeyedGenerator`] is slightly more complex, supporting a custom key
26//! type (thus allowing data items to be tracked through changing indices).
27//!
28//! ## Async data access
29//!
30//! Async clerks allow borrowed access to locally cached items. Such clerks must
31//! implement both [`AsyncClerk`] and [`TokenClerk`]:
32//!
33//! - [`AsyncClerk`] supports async data access with a local cache and batched
34//! item retrieval.
35//! - [`TokenClerk`] supports caching data within a token stored adjacent to
36//! the view widget and item retrieval using this token.
37
38#[allow(unused)] use crate::SelectionMsg;
39use kas::Id;
40use kas::cast::Cast;
41use kas::event::{ConfigCx, EventCx};
42#[allow(unused)] use kas::{Events, Widget};
43use std::borrow::Borrow;
44use std::fmt::Debug;
45use std::ops::Range;
46
47mod generator;
48pub use generator::*;
49
50/// A pair which may be borrowed over the first item
51#[derive(Debug, Default)]
52pub struct Token<K, I> {
53 pub key: K,
54 pub item: I,
55}
56
57impl<K, I> Token<K, I> {
58 /// Construct
59 #[inline]
60 pub fn new(key: K, item: I) -> Self {
61 Token { key, item }
62 }
63}
64
65impl<K, I> Borrow<K> for Token<K, I> {
66 fn borrow(&self) -> &K {
67 &self.key
68 }
69}
70
71/// Bounds on the key type
72///
73/// This type should be small, easy to copy, and without internal mutability.
74pub trait Key: Clone + Debug + Default + PartialEq + Eq + 'static {
75 /// Make an [`Id`] for a key
76 ///
77 /// The result must be distinct from `parent` and a descendant of `parent`
78 /// (use [`Id::make_child`] for this, optionally more than once).
79 fn make_id(&self, parent: &Id) -> Id;
80
81 /// Reconstruct a key from an [`Id`]
82 ///
83 /// Where `child` is the output of [`Self::make_id`] for the same `parent`
84 /// *or any [`Id`] descended from that*, this should return a copy of
85 /// the `key` passed to `make_id`.
86 ///
87 /// See: [`Id::next_key_after`], [`Id::iter_keys_after`]
88 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self>;
89}
90
91impl Key for () {
92 fn make_id(&self, parent: &Id) -> Id {
93 // We need a distinct child, so use index 0
94 parent.make_child(0)
95 }
96
97 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
98 if child.next_key_after(parent) == Some(0) {
99 Some(())
100 } else {
101 None
102 }
103 }
104}
105
106// NOTE: we cannot use this blanket impl without specialisation / negative impls
107// impl<Key: Cast<usize> + Clone + Debug + PartialEq + Eq + 'static> Key for Key
108macro_rules! impl_1D {
109 ($t:ty) => {
110 impl Key for $t {
111 fn make_id(&self, parent: &Id) -> Id {
112 parent.make_child((*self).cast())
113 }
114
115 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
116 child.next_key_after(parent).map(|i| i.cast())
117 }
118 }
119 };
120}
121impl_1D!(usize);
122impl_1D!(u32);
123#[cfg(target_pointer_width = "64")]
124impl_1D!(u64);
125
126macro_rules! impl_2D {
127 ($t:ty) => {
128 impl Key for ($t, $t) {
129 fn make_id(&self, parent: &Id) -> Id {
130 parent.make_child(self.0.cast()).make_child(self.1.cast())
131 }
132
133 fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
134 let mut iter = child.iter_keys_after(parent);
135 let col = iter.next().map(|i| i.cast());
136 let row = iter.next().map(|i| i.cast());
137 col.zip(row)
138 }
139 }
140 };
141}
142impl_2D!(usize);
143impl_2D!(u32);
144#[cfg(target_pointer_width = "64")]
145impl_2D!(u64);
146
147/// Indicates whether an update to a clerk changes any available data
148#[derive(Clone, Debug, PartialEq, Eq)]
149#[must_use]
150pub enum Changes<Index> {
151 /// Indicates that no changes to the data set occurred.
152 None,
153 /// Indicates that the data length may have changed and/or items outside the
154 /// current `view_range` may have changed.
155 ///
156 /// I.e. this variant is applicable when within the subset of `view_range`
157 /// and the new data range (`0..len)`, no index-key pairs have changed (no
158 /// re-ordering) and no item or token values have changed.
159 NoPreparedItems,
160 /// Indicates that the data length may have changed and that keys and/or
161 /// values within the given range may have changed.
162 ///
163 /// [`TokenClerk::update_token`] will be called for each index in the
164 /// intersection of the given range with the `view_range`.
165 Range(Range<Index>),
166 /// `Any` indicates that changes to the data set may have occurred.
167 ///
168 /// This is equivalent to `Changes::Range(full_data_range)`.
169 Any,
170}
171
172/// Return value of [`TokenClerk::update_token`]
173#[derive(Clone, Copy, Debug, PartialEq, Eq)]
174#[must_use]
175pub enum TokenChanges {
176 /// `None` indicates that no changes to the token occurred.
177 None,
178 /// `SameKey` indicates that while the token still represents the same key,
179 /// the associated data item may have changed.
180 SameKey,
181 /// `Any` indicates that the data key (and item) may have changed.
182 Any,
183}
184
185impl TokenChanges {
186 pub(crate) fn key(self) -> bool {
187 self == TokenChanges::Any
188 }
189
190 pub(crate) fn item(self) -> bool {
191 self != TokenChanges::None
192 }
193}
194
195/// Result of [`Self::len`]
196#[derive(Clone, Copy, Debug, PartialEq, Eq)]
197pub enum Len<Index> {
198 /// Length is known and specified exactly
199 Known(Index),
200 /// A lower bound on length is specified
201 LBound(Index),
202}
203
204impl<Index: Copy> Len<Index> {
205 /// Returns the length payload (known or lower bound)
206 #[inline]
207 pub fn len(&self) -> Index {
208 match self {
209 Len::Known(len) => *len,
210 Len::LBound(len) => *len,
211 }
212 }
213
214 /// Returns true if a known length given
215 #[inline]
216 pub fn is_known(&self) -> bool {
217 matches!(self, Len::Known(_))
218 }
219}
220
221/// Common functionality of all clerks
222pub trait Clerk<Index> {
223 /// Input data type (of parent widget)
224 ///
225 /// Data of this type is passed through the parent widget; see
226 /// [`Widget::Data`] and the [`Events`] trait. This input data might be used
227 /// to access a data set stored in another widget or to pass a query or
228 /// filter into the `Clerk`.
229 ///
230 /// Note that it is not currently possible to pass in references to multiple
231 /// data items (such as an external data set and a filter) via `Data`. This
232 /// would require use of Generic Associated Types (GATs), not only here but
233 /// also in the [`Widget`] trait; alas, GATs are not (yet)
234 /// compatible with dyn traits and Kas requires use of `dyn Widget`. Instead
235 /// one can share the data set (e.g. `Rc<RefCell<DataSet>>`) or store within
236 /// the `Clerk` using the `clerk` / `clerk_mut` methods to access; in
237 /// both cases it may be necessary to update the view controller explicitly
238 /// (e.g. `cx.update(list.as_node(&input))`) after the data set changes.
239 type Data;
240
241 /// Item type
242 ///
243 /// `&Item` is passed to child view widgets as input data.
244 type Item;
245
246 /// Get an upper bound on length, if any
247 ///
248 /// Scroll bars and the `view_range` are
249 /// limited by the result of this method.
250 ///
251 /// Where the data set size is a known fixed `len` (or unfixed but with
252 /// maximum `len <= lbound`), this method should return
253 /// <code>[Len::Known][](len)</code>.
254 ///
255 /// Where the data set size is unknown (or unfixed and greater than
256 /// `lbound`), this method should return
257 /// <code>[Len::LBound][](lbound)</code>.
258 ///
259 /// `lbound` is set to allow scrolling a little beyond the current view
260 /// position (i.e. a little larger than the last prepared `range.end`).
261 fn len(&self, data: &Self::Data, lbound: Index) -> Len<Index>;
262
263 /// Get a mock data item for sizing purposes
264 ///
265 /// This method is called if no data items are available when initially
266 /// sizing the view. If an item is returned, then a mock view widget is
267 /// created using this data in order to determine size requirements.
268 ///
269 /// The default implementation returns `None`.
270 fn mock_item(&self, data: &Self::Data) -> Option<Self::Item> {
271 let _ = data;
272 None
273 }
274}
275
276/// Functionality common to async clerks
277pub trait AsyncClerk<Index>: Clerk<Index> {
278 /// Key type
279 ///
280 /// All data items should have a stable key so that data items may be
281 /// tracked through changing queries. This allows focus and selection to
282 /// correctly track items when the data query or filter changes.
283 ///
284 /// Where the query is fixed, this can just be the `Index` type.
285 type Key: Key;
286
287 /// Update the clerk
288 ///
289 /// This is called by [`kas::Events::update`]. It should update `self` as
290 /// required reflecting possible data-changes and indicate through the
291 /// returned [`Changes`] value the updates required to tokens and views.
292 ///
293 /// Data items within `view_range` may be visible.
294 ///
295 /// Note: this method is called automatically when input data changes. When
296 /// data owned or referenced by the `TokenClerk` implementation is changed it
297 /// may be necessary to explicitly update the view controller, e.g. using
298 /// [`ConfigCx::update`].
299 ///
300 /// This method may be called frequently and without changes to `data`.
301 /// It is expected to be fast and non-blocking. Asynchronous updates to
302 /// `self` are possible using [`Self::handle_messages`].
303 fn update(
304 &mut self,
305 cx: &mut ConfigCx,
306 id: Id,
307 view_range: Range<Index>,
308 data: &Self::Data,
309 ) -> Changes<Index>;
310
311 /// Prepare a range
312 ///
313 /// This method is called prior to [`TokenClerk::update_token`] over the
314 /// indices in `range`. If data is to be loaded
315 /// from a remote source or computed in a worker thread, it should be done
316 /// so from here using `async` worker(s) (see [`Self::handle_messages`]).
317 ///
318 /// Data items within `view_range` may be visible.
319 ///
320 /// The passed `range` may be a subset of the `view_range` but does
321 /// not exceed it; pre-emptive loading is left to the implementation.
322 /// This method may be called frequently and without changes to `range`, and
323 /// is expected to be fast and non-blocking.
324 ///
325 /// The default implementation does nothing.
326 fn prepare_range(
327 &mut self,
328 cx: &mut ConfigCx,
329 id: Id,
330 view_range: Range<Index>,
331 data: &Self::Data,
332 range: Range<Index>,
333 ) {
334 let _ = (cx, id, view_range, data, range);
335 }
336
337 /// Handle an async message
338 ///
339 /// This method is called when a message is available. Such messages may be
340 /// taken using [`EventCx::try_pop`]. Messages may be received from:
341 ///
342 /// - The view widget for `key` when `opt_key = Some(key)`.
343 /// - [`SelectionMsg`] may be received from the view controller.
344 /// - [`Self::update`], [`Self::prepare_range`] and this method may send
345 /// `async` messages using `cx.send_async(controller.id(), SomeMessage { .. })`.
346 ///
347 /// Data items within `view_range` may be visible.
348 ///
349 /// The default implementation does nothing.
350 fn handle_messages(
351 &mut self,
352 cx: &mut EventCx,
353 id: Id,
354 view_range: Range<Index>,
355 data: &Self::Data,
356 opt_key: Option<Self::Key>,
357 ) -> Changes<Index> {
358 let _ = (cx, id, view_range, data, opt_key);
359 Changes::None
360 }
361}
362
363/// Data access manager for keyed data with cache tokens
364pub trait TokenClerk<Index>: AsyncClerk<Index> {
365 /// Token type
366 ///
367 /// Each view widget is stored with a corresponding token set by
368 /// [`Self::update_token`].
369 ///
370 /// Often this will either be [`Self::Key`](AsyncClerk::Key) or
371 /// <code>[Token]<[Self::Key](AsyncClerk::Key), [Self::Item](Clerk::Item)></code>.
372 type Token: Borrow<Self::Key>;
373
374 /// Update a token for the given `index`
375 ///
376 /// This method is called after [`AsyncClerk::prepare_range`] for each `index` in
377 /// the prepared `range` in order to prepare a to prepare a token for each
378 /// item (see [`Self::item`]).
379 ///
380 /// In the case that `type Token = Key` it is recommended to use the free
381 /// function [`update_token`]:
382 /// ```rust,ignore
383 /// fn update_token(
384 /// &self,
385 /// data: &Self::Data,
386 /// index: Index,
387 /// update_item: bool,
388 /// token: &mut Option<Self::Token>,
389 /// ) -> TokenChanges {
390 /// let key = /* ... */;
391 /// kas::view::clerk::update_token(key, update_item, token)
392 /// }
393 /// ```
394 ///
395 /// The input `token` (if any) may or may not correspond to the given
396 /// `index`. This method should prepare it as follows:
397 ///
398 /// - If no item is currently available for `index`, set `*token = None`
399 /// and return any value of [`TokenChanges`].
400 /// - Otherwise, if the input `token` is `None` or corresponds to a
401 /// different `index`, replace `token` and report [`TokenChanges::Any`].
402 /// - Otherwise, if then token depends on (caches) the data item and
403 /// `update_item`, the token should be updated. The method should report
404 /// [`TokenChanges::SameKey`] when the token has changed.
405 /// - Finally (if none of the above), report [`TokenChanges::None`].
406 ///
407 /// This method should be fast since it may be called repeatedly. Slow and
408 /// blocking operations should be run asynchronously from
409 /// [`AsyncClerk::prepare_range`] using an internal cache.
410 fn update_token(
411 &self,
412 data: &Self::Data,
413 index: Index,
414 update_item: bool,
415 token: &mut Option<Self::Token>,
416 ) -> TokenChanges;
417
418 /// Get the data item for the given `token`
419 ///
420 /// Data cannot be generated by this method but it can be generated by
421 /// [`Self::update_token`] and cached within a [`Token`]
422 /// (see [`Self::Token`]).
423 ///
424 /// A token is expected to be able to resolve an item. Since [`Self::Token`]
425 /// does not support `Clone` it is known that items can be evicted from
426 /// storage when their token is replaced.
427 ///
428 /// This method should be fast since it may be called repeatedly.
429 fn item<'r>(&'r self, data: &'r Self::Data, token: &'r Self::Token) -> &'r Self::Item;
430}
431
432// TODO(Rust): the following could be added if Rust supported mutually exclusive traits
433/*
434/// Data access manager for keyed data
435pub trait KeyedClerk<Index>: AsyncClerk<Index> {
436 /// Get a key for a given `index`, if available
437 ///
438 /// This method should be fast since it may be called repeatedly.
439 /// This method is only called for `index` less than the result of
440 /// [`Clerk::len`].
441 ///
442 /// This method should return `Some(key)` only when [`Self::item`] can yield
443 /// an item for this `key`.
444 fn key(&self, data: &Self::Data, index: Index) -> Option<Self::Key>;
445
446 /// Get the data item for the given `key`
447 ///
448 /// This method should be fast since it may be called repeatedly.
449 fn item<'r>(&'r self, data: &'r Self::Data, key: &'r Self::Key) -> &'r Self::Item;
450}
451
452impl<Index, C: KeyedClerk<Index>> TokenClerk<Index> for C {
453 type Token = C::Key;
454
455 fn update_token(
456 &self,
457 data: &Self::Data,
458 index: Index,
459 update_item: bool,
460 token: &mut Option<Self::Token>,
461 ) -> TokenChanges {
462 let key = self.key(data, index);
463 update_token(key, update_item, token)
464 }
465
466 fn item<'r>(&'r self, data: &'r Self::Data, token: &'r Self::Token) -> &'r Self::Item {
467 self.item(data, token)
468 }
469}
470*/
471
472/// Implementation of [`TokenClerk::update_token`] for non-caching tokens
473///
474/// This may be used where `type Token = Key`.
475pub fn update_token<Key: PartialEq>(
476 key: Option<Key>,
477 update_item: bool,
478 token: &mut Option<Key>,
479) -> TokenChanges {
480 if *token == key {
481 if update_item {
482 TokenChanges::SameKey
483 } else {
484 TokenChanges::None
485 }
486 } else {
487 *token = key;
488 TokenChanges::Any
489 }
490}