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