kas-view 0.17.0

KAS GUI / view widgets
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Data clerks
//!
//! # Interfaces
//!
//! A clerk manages a view or query over a data set using an `Index` type
//! specified by the [view controller](crate#view-controller). For
//! [`ListView`](crate::ListView), `Index = usize`.
//!
//! All clerks must implement [`Clerk`]:
//!
//! -   [`Clerk`] covers the base functionality required by all clerks
//!
//! ## Data generators
//!
//! Generator clerks construct owned items. One of the following traits must be
//! implemented:
//!
//! -   [`IndexedGenerator`] provides a very simple interface: `update` and
//!     `generate`.
//! -   [`KeyedGenerator`] is slightly more complex, supporting a custom key
//!     type (thus allowing data items to be tracked through changing indices).
//!
//! ## Async data access
//!
//! Async clerks allow borrowed access to locally cached items. Such clerks must
//! implement both [`AsyncClerk`] and [`TokenClerk`]:
//!
//! -   [`AsyncClerk`] supports async data access with a local cache and batched
//!     item retrieval.
//! -   [`TokenClerk`] supports caching data within a token stored adjacent to
//!     the view widget and item retrieval using this token.

#[allow(unused)] use crate::SelectionMsg;
use kas::Id;
use kas::cast::Cast;
use kas::event::{ConfigCx, EventCx};
#[allow(unused)] use kas::{Events, Widget};
use std::borrow::Borrow;
use std::fmt::Debug;
use std::ops::Range;

mod generator;
pub use generator::*;

/// A pair which may be borrowed over the first item
#[derive(Debug, Default)]
pub struct Token<K, I> {
    pub key: K,
    pub item: I,
}

impl<K, I> Token<K, I> {
    /// Construct
    #[inline]
    pub fn new(key: K, item: I) -> Self {
        Token { key, item }
    }
}

impl<K, I> Borrow<K> for Token<K, I> {
    fn borrow(&self) -> &K {
        &self.key
    }
}

/// Bounds on the key type
///
/// This type should be small, easy to copy, and without internal mutability.
pub trait Key: Clone + Debug + Default + PartialEq + Eq + 'static {
    /// Make an [`Id`] for a key
    ///
    /// The result must be distinct from `parent` and a descendant of `parent`
    /// (use [`Id::make_child`] for this, optionally more than once).
    fn make_id(&self, parent: &Id) -> Id;

    /// Reconstruct a key from an [`Id`]
    ///
    /// Where `child` is the output of [`Self::make_id`] for the same `parent`
    /// *or any [`Id`] descended from that*, this should return a copy of
    /// the `key` passed to `make_id`.
    ///
    /// See: [`Id::next_key_after`], [`Id::iter_keys_after`]
    fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self>;
}

impl Key for () {
    fn make_id(&self, parent: &Id) -> Id {
        // We need a distinct child, so use index 0
        parent.make_child(0)
    }

    fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
        if child.next_key_after(parent) == Some(0) {
            Some(())
        } else {
            None
        }
    }
}

// NOTE: we cannot use this blanket impl without specialisation / negative impls
// impl<Key: Cast<usize> + Clone + Debug + PartialEq + Eq + 'static> Key for Key
macro_rules! impl_1D {
    ($t:ty) => {
        impl Key for $t {
            fn make_id(&self, parent: &Id) -> Id {
                parent.make_child((*self).cast())
            }

            fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
                child.next_key_after(parent).map(|i| i.cast())
            }
        }
    };
}
impl_1D!(usize);
impl_1D!(u32);
#[cfg(target_pointer_width = "64")]
impl_1D!(u64);

macro_rules! impl_2D {
    ($t:ty) => {
        impl Key for ($t, $t) {
            fn make_id(&self, parent: &Id) -> Id {
                parent.make_child(self.0.cast()).make_child(self.1.cast())
            }

            fn reconstruct_key(parent: &Id, child: &Id) -> Option<Self> {
                let mut iter = child.iter_keys_after(parent);
                let col = iter.next().map(|i| i.cast());
                let row = iter.next().map(|i| i.cast());
                col.zip(row)
            }
        }
    };
}
impl_2D!(usize);
impl_2D!(u32);
#[cfg(target_pointer_width = "64")]
impl_2D!(u64);

/// Indicates whether an update to a clerk changes any available data
#[derive(Clone, Debug, PartialEq, Eq)]
#[must_use]
pub enum Changes<Index> {
    /// Indicates that no changes to the data set occurred.
    None,
    /// Indicates that the data length may have changed and/or items outside the
    /// current `view_range` may have changed.
    ///
    /// I.e. this variant is applicable when within the subset of `view_range`
    /// and the new data range (`0..len)`, no index-key pairs have changed (no
    /// re-ordering) and no item or token values have changed.
    NoPreparedItems,
    /// Indicates that the data length may have changed and that keys and/or
    /// values within the given range may have changed.
    ///
    /// [`TokenClerk::update_token`] will be called for each index in the
    /// intersection of the given range with the `view_range`.
    Range(Range<Index>),
    /// `Any` indicates that changes to the data set may have occurred.
    ///
    /// This is equivalent to `Changes::Range(full_data_range)`.
    Any,
}

/// Return value of [`TokenClerk::update_token`]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[must_use]
pub enum TokenChanges {
    /// `None` indicates that no changes to the token occurred.
    None,
    /// `SameKey` indicates that while the token still represents the same key,
    /// the associated data item may have changed.
    SameKey,
    /// `Any` indicates that the data key (and item) may have changed.
    Any,
}

impl TokenChanges {
    pub(crate) fn key(self) -> bool {
        self == TokenChanges::Any
    }

    pub(crate) fn item(self) -> bool {
        self != TokenChanges::None
    }
}

/// Result of [`Self::len`]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Len<Index> {
    /// Length is known and specified exactly
    Known(Index),
    /// A lower bound on length is specified
    LBound(Index),
}

impl<Index: Copy> Len<Index> {
    /// Returns the length payload (known or lower bound)
    #[inline]
    pub fn len(&self) -> Index {
        match self {
            Len::Known(len) => *len,
            Len::LBound(len) => *len,
        }
    }

    /// Returns true if a known length given
    #[inline]
    pub fn is_known(&self) -> bool {
        matches!(self, Len::Known(_))
    }
}

/// Common functionality of all clerks
pub trait Clerk<Index> {
    /// Input data type (of parent widget)
    ///
    /// Data of this type is passed through the parent widget; see
    /// [`Widget::Data`] and the [`Events`] trait. This input data might be used
    /// to access a data set stored in another widget or to pass a query or
    /// filter into the `Clerk`.
    ///
    /// Note that it is not currently possible to pass in references to multiple
    /// data items (such as an external data set and a filter) via `Data`. This
    /// would require use of Generic Associated Types (GATs), not only here but
    /// also in the [`Widget`] trait; alas, GATs are not (yet)
    /// compatible with dyn traits and Kas requires use of `dyn Widget`. Instead
    /// one can share the data set (e.g. `Rc<RefCell<DataSet>>`) or store within
    /// the `Clerk` using the `clerk` / `clerk_mut` methods to access; in
    /// both cases it may be necessary to update the view controller explicitly
    /// (e.g. `cx.update(list.as_node(&input))`) after the data set changes.
    type Data;

    /// Item type
    ///
    /// `&Item` is passed to child view widgets as input data.
    type Item;

    /// Get an upper bound on length, if any
    ///
    /// Scroll bars and the `view_range` are
    /// limited by the result of this method.
    ///
    /// Where the data set size is a known fixed `len` (or unfixed but with
    /// maximum `len <= lbound`), this method should return
    /// <code>[Len::Known][](len)</code>.
    ///
    /// Where the data set size is unknown (or unfixed and greater than
    /// `lbound`), this method should return
    /// <code>[Len::LBound][](lbound)</code>.
    ///
    /// `lbound` is set to allow scrolling a little beyond the current view
    /// position (i.e. a little larger than the last prepared `range.end`).
    fn len(&self, data: &Self::Data, lbound: Index) -> Len<Index>;

    /// Get a mock data item for sizing purposes
    ///
    /// This method is called if no data items are available when initially
    /// sizing the view. If an item is returned, then a mock view widget is
    /// created using this data in order to determine size requirements.
    ///
    /// The default implementation returns `None`.
    fn mock_item(&self, data: &Self::Data) -> Option<Self::Item> {
        let _ = data;
        None
    }
}

/// Functionality common to async clerks
pub trait AsyncClerk<Index>: Clerk<Index> {
    /// Key type
    ///
    /// All data items should have a stable key so that data items may be
    /// tracked through changing queries. This allows focus and selection to
    /// correctly track items when the data query or filter changes.
    ///
    /// Where the query is fixed, this can just be the `Index` type.
    type Key: Key;

    /// Update the clerk
    ///
    /// This is called by [`kas::Events::update`]. It should update `self` as
    /// required reflecting possible data-changes and indicate through the
    /// returned [`Changes`] value the updates required to tokens and views.
    ///
    /// Data items within `view_range` may be visible.
    ///
    /// Note: this method is called automatically when input data changes. When
    /// data owned or referenced by the `TokenClerk` implementation is changed it
    /// may be necessary to explicitly update the view controller, e.g. using
    /// [`ConfigCx::update`].
    ///
    /// This method may be called frequently and without changes to `data`.
    /// It is expected to be fast and non-blocking. Asynchronous updates to
    /// `self` are possible using [`Self::handle_messages`].
    fn update(
        &mut self,
        cx: &mut ConfigCx,
        id: Id,
        view_range: Range<Index>,
        data: &Self::Data,
    ) -> Changes<Index>;

    /// Prepare a range
    ///
    /// This method is called prior to [`TokenClerk::update_token`] over the
    /// indices in `range`. If data is to be loaded
    /// from a remote source or computed in a worker thread, it should be done
    /// so from here using `async` worker(s) (see [`Self::handle_messages`]).
    ///
    /// Data items within `view_range` may be visible.
    ///
    /// The passed `range` may be a subset of the `view_range` but does
    /// not exceed it; pre-emptive loading is left to the implementation.
    /// This method may be called frequently and without changes to `range`, and
    /// is expected to be fast and non-blocking.
    ///
    /// The default implementation does nothing.
    fn prepare_range(
        &mut self,
        cx: &mut ConfigCx,
        id: Id,
        view_range: Range<Index>,
        data: &Self::Data,
        range: Range<Index>,
    ) {
        let _ = (cx, id, view_range, data, range);
    }

    /// Handle an async message
    ///
    /// This method is called when a message is available. Such messages may be
    /// taken using [`EventCx::try_pop`]. Messages may be received from:
    ///
    /// -   The view widget for `key` when `opt_key = Some(key)`.
    /// -   [`SelectionMsg`] may be received from the view controller.
    /// -   [`Self::update`], [`Self::prepare_range`] and this method may send
    ///     `async` messages using `cx.send_async(controller.id(), SomeMessage { .. })`.
    ///
    /// Data items within `view_range` may be visible.
    ///
    /// The default implementation does nothing.
    fn handle_messages(
        &mut self,
        cx: &mut EventCx,
        id: Id,
        view_range: Range<Index>,
        data: &Self::Data,
        opt_key: Option<Self::Key>,
    ) -> Changes<Index> {
        let _ = (cx, id, view_range, data, opt_key);
        Changes::None
    }
}

/// Data access manager for keyed data with cache tokens
pub trait TokenClerk<Index>: AsyncClerk<Index> {
    /// Token type
    ///
    /// Each view widget is stored with a corresponding token set by
    /// [`Self::update_token`].
    ///
    /// Often this will either be [`Self::Key`](AsyncClerk::Key) or
    /// <code>[Token]&lt;[Self::Key](AsyncClerk::Key), [Self::Item](Clerk::Item)&gt;</code>.
    type Token: Borrow<Self::Key>;

    /// Update a token for the given `index`
    ///
    /// This method is called after [`AsyncClerk::prepare_range`] for each `index` in
    /// the prepared `range` in order to prepare a to prepare a token for each
    /// item (see [`Self::item`]).
    ///
    /// In the case that `type Token = Key` it is recommended to use the free
    /// function [`update_token`]:
    /// ```rust,ignore
    /// fn update_token(
    ///     &self,
    ///     data: &Self::Data,
    ///     index: Index,
    ///     update_item: bool,
    ///     token: &mut Option<Self::Token>,
    /// ) -> TokenChanges {
    ///     let key = /* ... */;
    ///     kas::view::clerk::update_token(key, update_item, token)
    /// }
    /// ```
    ///
    /// The input `token` (if any) may or may not correspond to the given
    /// `index`. This method should prepare it as follows:
    ///
    /// -   If no item is currently available for `index`, set `*token = None`
    ///     and return any value of [`TokenChanges`].
    /// -   Otherwise, if the input `token` is `None` or corresponds to a
    ///     different `index`, replace `token` and report [`TokenChanges::Any`].
    /// -   Otherwise, if then token depends on (caches) the data item and
    ///     `update_item`, the token should be updated. The method should report
    ///     [`TokenChanges::SameKey`] when the token has changed.
    /// -   Finally (if none of the above), report [`TokenChanges::None`].
    ///
    /// This method should be fast since it may be called repeatedly. Slow and
    /// blocking operations should be run asynchronously from
    /// [`AsyncClerk::prepare_range`] using an internal cache.
    fn update_token(
        &self,
        data: &Self::Data,
        index: Index,
        update_item: bool,
        token: &mut Option<Self::Token>,
    ) -> TokenChanges;

    /// Get the data item for the given `token`
    ///
    /// Data cannot be generated by this method but it can be generated by
    /// [`Self::update_token`] and cached within a [`Token`]
    /// (see [`Self::Token`]).
    ///
    /// A token is expected to be able to resolve an item. Since [`Self::Token`]
    /// does not support `Clone` it is known that items can be evicted from
    /// storage when their token is replaced.
    ///
    /// This method should be fast since it may be called repeatedly.
    fn item<'r>(&'r self, data: &'r Self::Data, token: &'r Self::Token) -> &'r Self::Item;
}

// TODO(Rust): the following could be added if Rust supported mutually exclusive traits
/*
/// Data access manager for keyed data
pub trait KeyedClerk<Index>: AsyncClerk<Index> {
    /// Get a key for a given `index`, if available
    ///
    /// This method should be fast since it may be called repeatedly.
    /// This method is only called for `index` less than the result of
    /// [`Clerk::len`].
    ///
    /// This method should return `Some(key)` only when [`Self::item`] can yield
    /// an item for this `key`.
    fn key(&self, data: &Self::Data, index: Index) -> Option<Self::Key>;

    /// Get the data item for the given `key`
    ///
    /// This method should be fast since it may be called repeatedly.
    fn item<'r>(&'r self, data: &'r Self::Data, key: &'r Self::Key) -> &'r Self::Item;
}

impl<Index, C: KeyedClerk<Index>> TokenClerk<Index> for C {
    type Token = C::Key;

    fn update_token(
        &self,
        data: &Self::Data,
        index: Index,
        update_item: bool,
        token: &mut Option<Self::Token>,
    ) -> TokenChanges {
        let key = self.key(data, index);
        update_token(key, update_item, token)
    }

    fn item<'r>(&'r self, data: &'r Self::Data, token: &'r Self::Token) -> &'r Self::Item {
        self.item(data, token)
    }
}
*/

/// Implementation of [`TokenClerk::update_token`] for non-caching tokens
///
/// This may be used where `type Token = Key`.
pub fn update_token<Key: PartialEq>(
    key: Option<Key>,
    update_item: bool,
    token: &mut Option<Key>,
) -> TokenChanges {
    if *token == key {
        if update_item {
            TokenChanges::SameKey
        } else {
            TokenChanges::None
        }
    } else {
        *token = key;
        TokenChanges::Any
    }
}