kas_view/
generator.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 generation (high-level) traits
7
8use crate::{DataChanges, DataClerk, DataKey, DataLen, Token, TokenChanges};
9use kas::Id;
10use kas::event::ConfigCx;
11#[allow(unused)] use kas::{Action, Events, Widget};
12use std::fmt::Debug;
13use std::marker::PhantomData;
14use std::ops::Range;
15
16/// Indicates whether an update to a [`DataGenerator`] has changed
17#[derive(Clone, Debug, PartialEq, Eq)]
18#[must_use]
19pub enum GeneratorChanges<Index> {
20    /// `None` indicates that no changes to the data has occurred.
21    None,
22    /// Indicates that [`DataGenerator::len`] may have changed but generated
23    /// values have not changed.
24    LenOnly,
25    /// Indicates that items in the given range may require an update
26    /// [`DataGenerator::generate`] will be called for each index in the
27    /// intersection of the given range with the visible data range.
28    Range(Range<Index>),
29    /// `Any` indicates that changes to the data set may have occurred.
30    Any,
31}
32
33/// A generator for use with [`GeneratorClerk`]
34///
35/// This provides a substantially simpler interface than [`DataClerk`].
36pub trait DataGenerator<Index> {
37    /// Input data type (of parent widget)
38    ///
39    /// Data of this type is passed through the parent widget; see
40    /// [`Widget::Data`] and the [`Events`] trait. This input data might be used
41    /// to access a data set stored in another widget or to pass a query or
42    /// filter into the `DataClerk`.
43    ///
44    /// Note that it is not currently possible to pass in references to multiple
45    /// data items (such as an external data set and a filter) via `Data`. This
46    /// would require use of Generic Associated Types (GATs), not only here but
47    /// also in the [`Widget`](kas::Widget) trait; alas, GATs are not (yet)
48    /// compatible with dyn traits and Kas requires use of `dyn Widget`. Instead
49    /// one can share the data set (e.g. `Rc<RefCell<DataSet>>`) or store within
50    /// the `DataClerk` using the `clerk` / `clerk_mut` methods to access; in
51    /// both cases it may be necessary to update the view controller explicitly
52    /// (e.g. `cx.update(list.as_node(&input))`) after the data set changes.
53    type Data;
54
55    /// Key type
56    ///
57    /// All data items should have a stable key so that data items may be
58    /// tracked through changing queries. This allows focus and selection to
59    /// correctly track items when the data query or filter changes.
60    ///
61    /// Where the query is fixed, this can just be the `Index` type.
62    type Key: DataKey;
63
64    /// Item type
65    ///
66    /// This is the generated type.
67    type Item: Clone + Default + PartialEq;
68
69    /// Update the generator
70    ///
71    /// This is called by [`kas::Events::update`]. It should update `self` as
72    /// required reflecting possible data-changes and indicate through the
73    /// returned [`GeneratorChanges`] value the updates required to tokens and
74    /// views.
75    ///
76    /// Note: this method is called automatically when input data changes. When
77    /// data owned or referenced by the `DataClerk` implementation is changed it
78    /// may be necessary to explicitly update the view controller, e.g. using
79    /// [`ConfigCx::update`] or [`Action::UPDATE`].
80    ///
81    /// This method may be called frequently and without changes to `data`.
82    fn update(&mut self, data: &Self::Data) -> GeneratorChanges<Index>;
83
84    /// Get the number of indexable items
85    ///
86    /// Scroll bars and the `index` values passed to [`Self::generate`] are
87    /// limited by the result of this method.
88    ///
89    /// Where the data set size is a known fixed `len` (or unfixed but with
90    /// maximum `len <= lbound`), this method should return
91    /// <code>[DataLen::Known][](len)</code>.
92    ///
93    /// Where the data set size is unknown (or unfixed and greater than
94    /// `lbound`), this method should return
95    /// <code>[DataLen::LBound][](lbound)</code>.
96    ///
97    /// `lbound` is set to allow scrolling a little beyond the current view
98    /// position (i.e. a little larger than the last prepared `range.end`).
99    fn len(&self, data: &Self::Data, lbound: Index) -> DataLen<Index>;
100
101    /// Get a key for a given `index`, if available
102    ///
103    /// This method should be fast since it may be called repeatedly.
104    /// This method is only called for `index` less than the result of
105    /// [`Self::len`].
106    ///
107    /// This may return `None` even when `index` is within the query's `range`
108    /// since data may be sparse; in this case the view widget at this `index`
109    /// is hidden.
110    fn key(&self, data: &Self::Data, index: Index) -> Option<Self::Key>;
111
112    /// Generate an item
113    ///
114    /// The `key` will be the result of [`Self::key`] for an `index` less than
115    /// [`Self::len`].
116    fn generate(&self, data: &Self::Data, key: &Self::Key) -> Self::Item;
117}
118
119/// An implementation of [`DataClerk`] for data generators
120pub struct GeneratorClerk<Index, G: DataGenerator<Index>> {
121    g: G,
122    _index: PhantomData<Index>,
123}
124
125impl<Index: Default, G: DataGenerator<Index>> GeneratorClerk<Index, G> {
126    /// Construct a `GeneratorClerk`
127    pub fn new(generator: G) -> Self {
128        GeneratorClerk {
129            g: generator,
130            _index: PhantomData,
131        }
132    }
133
134    /// Access the inner generator
135    pub fn generator(&self) -> &G {
136        &self.g
137    }
138}
139
140impl<Index: DataKey, G: DataGenerator<Index>> DataClerk<Index> for GeneratorClerk<Index, G> {
141    type Data = G::Data;
142    type Key = G::Key;
143    type Item = G::Item;
144    type Token = Token<Self::Key, Self::Item>;
145
146    fn update(
147        &mut self,
148        _: &mut ConfigCx,
149        _: Id,
150        _: Range<Index>,
151        data: &Self::Data,
152    ) -> DataChanges<Index> {
153        match self.g.update(data) {
154            GeneratorChanges::None => DataChanges::None,
155            GeneratorChanges::LenOnly => DataChanges::NoPreparedItems,
156            GeneratorChanges::Range(range) => DataChanges::Range(range),
157            GeneratorChanges::Any => DataChanges::Any,
158        }
159    }
160
161    fn len(&self, data: &Self::Data, lbound: Index) -> DataLen<Index> {
162        self.g.len(data, lbound)
163    }
164
165    fn update_token(
166        &self,
167        data: &Self::Data,
168        index: Index,
169        update_item: bool,
170        token: &mut Option<Self::Token>,
171    ) -> TokenChanges {
172        let Some(key) = self.g.key(data, index) else {
173            *token = None;
174            return TokenChanges::None;
175        };
176
177        if !update_item
178            && let Some(token) = token.as_mut()
179            && token.key == key
180        {
181            return TokenChanges::None;
182        }
183
184        let item = self.g.generate(data, &key);
185        let mut changes = TokenChanges::Any;
186
187        if let Some(token) = token.as_mut()
188            && token.key == key
189        {
190            if token.item == item {
191                return TokenChanges::None;
192            } else {
193                changes = TokenChanges::SameKey;
194            }
195        }
196
197        *token = Some(Token { key, item });
198        changes
199    }
200
201    fn item<'r>(&'r self, _: &'r Self::Data, token: &'r Self::Token) -> &'r Self::Item {
202        &token.item
203    }
204}