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}