Skip to main content

kas_view/clerk/
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 super::{AsyncClerk, Changes, Clerk, Key, Token, TokenChanges, TokenClerk};
9use kas::Id;
10use kas::event::ConfigCx;
11#[allow(unused)] use kas::{Events, Widget};
12use std::fmt::Debug;
13use std::ops::Range;
14
15/// Indicates whether an update to a generator changes any available data
16#[derive(Clone, Debug, PartialEq, Eq)]
17#[must_use]
18pub enum GeneratorChanges<Index> {
19    /// `None` indicates that no changes to the data has occurred.
20    None,
21    /// Indicates that [`Clerk::len`] may have changed but generated
22    /// values have not changed.
23    LenOnly,
24    /// Indicates that items in the given range may require an update.
25    /// The `generate` method will be called for each index in the
26    /// intersection of the given range with the visible data range.
27    Range(Range<Index>),
28    /// `Any` indicates that changes to the data set may have occurred.
29    Any,
30}
31
32/// Interface for generators over indexed data
33pub trait IndexedGenerator<Index>: Clerk<Index, Item: Clone + Default + PartialEq> {
34    /// Update the generator
35    ///
36    /// This is called by [`kas::Events::update`]. It should update `self` as
37    /// required reflecting possible data-changes and indicate through the
38    /// returned [`GeneratorChanges`] value the updates required to tokens and
39    /// views.
40    ///
41    /// Note: this method is called automatically when input data changes. When
42    /// data owned or referenced by the `IndexedGenerator` implementation is
43    /// changed it may be necessary to explicitly update the view controller,
44    /// e.g. using [`ConfigCx::update`].
45    ///
46    /// This method may be called frequently and without changes to `data`.
47    fn update(&mut self, data: &Self::Data) -> GeneratorChanges<Index>;
48
49    /// Generate an item
50    fn generate(&self, data: &Self::Data, index: Index) -> Self::Item;
51}
52
53/// Interface for generators over keyed data
54pub trait KeyedGenerator<Index>: Clerk<Index, Item: Clone + Default + PartialEq> {
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: Key;
63
64    /// Update the generator
65    ///
66    /// This is called by [`kas::Events::update`]. It should update `self` as
67    /// required reflecting possible data-changes and indicate through the
68    /// returned [`GeneratorChanges`] value the updates required to tokens and
69    /// views.
70    ///
71    /// Note: this method is called automatically when input data changes. When
72    /// data owned or referenced by the `KeyedGenerator` implementation is
73    /// changed it may be necessary to explicitly update the view controller,
74    /// e.g. using [`ConfigCx::update`].
75    ///
76    /// This method may be called frequently and without changes to `data`.
77    fn update(&mut self, data: &Self::Data) -> GeneratorChanges<Index>;
78
79    /// Get a key for a given `index`, if available
80    ///
81    /// This method should be fast since it may be called repeatedly.
82    /// This method is only called for `index` less than the result of
83    /// [`Clerk::len`].
84    ///
85    /// This may return `None` even when `index` is within the query's `range`
86    /// since data may be sparse; in this case the view widget at this `index`
87    /// is hidden.
88    fn key(&self, data: &Self::Data, index: Index) -> Option<Self::Key>;
89
90    /// Generate an item
91    ///
92    /// The `key` will be the result of [`Self::key`] for an `index` less than
93    /// [`Clerk::len`].
94    fn generate(&self, data: &Self::Data, key: &Self::Key) -> Self::Item;
95}
96
97impl<Index: Key, G: IndexedGenerator<Index>> KeyedGenerator<Index> for G {
98    type Key = Index;
99
100    fn update(&mut self, data: &Self::Data) -> GeneratorChanges<Index> {
101        self.update(data)
102    }
103
104    fn key(&self, _: &Self::Data, index: Index) -> Option<Self::Key> {
105        Some(index)
106    }
107
108    fn generate(&self, data: &Self::Data, key: &Self::Key) -> Self::Item {
109        self.generate(data, key.clone())
110    }
111}
112
113impl<Index, G: KeyedGenerator<Index>> AsyncClerk<Index> for G {
114    type Key = G::Key;
115
116    fn update(
117        &mut self,
118        _: &mut ConfigCx,
119        _: Id,
120        _: Range<Index>,
121        data: &Self::Data,
122    ) -> Changes<Index> {
123        match self.update(data) {
124            GeneratorChanges::None => Changes::None,
125            GeneratorChanges::LenOnly => Changes::NoPreparedItems,
126            GeneratorChanges::Range(range) => Changes::Range(range),
127            GeneratorChanges::Any => Changes::Any,
128        }
129    }
130}
131
132impl<Index, G: KeyedGenerator<Index>> TokenClerk<Index> for G {
133    type Token = Token<Self::Key, Self::Item>;
134
135    fn update_token(
136        &self,
137        data: &Self::Data,
138        index: Index,
139        update_item: bool,
140        token: &mut Option<Self::Token>,
141    ) -> TokenChanges {
142        let Some(key) = self.key(data, index) else {
143            *token = None;
144            return TokenChanges::None;
145        };
146
147        if !update_item
148            && let Some(token) = token.as_mut()
149            && token.key == key
150        {
151            return TokenChanges::None;
152        }
153
154        let item = self.generate(data, &key);
155        let mut changes = TokenChanges::Any;
156
157        if let Some(token) = token.as_mut()
158            && token.key == key
159        {
160            if token.item == item {
161                return TokenChanges::None;
162            } else {
163                changes = TokenChanges::SameKey;
164            }
165        }
166
167        *token = Some(Token { key, item });
168        changes
169    }
170
171    #[inline]
172    fn item<'r>(&'r self, _: &'r Self::Data, token: &'r Self::Token) -> &'r Self::Item {
173        &token.item
174    }
175}