Skip to main content

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]&lt;[Self::Key](AsyncClerk::Key), [Self::Item](Clerk::Item)&gt;</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}