ferroui_dynamic_data/list/
change_aware_list.rs1use super::{Change, ChangeSet, ListChangeReason};
4use crate::list::internal::unified;
5
6pub trait IExtendedList<T: Clone + PartialEq>: super::IList<T> {
11 fn insert_range(&mut self, items: Vec<T>, index: usize);
13 fn move_index(&mut self, original: usize, destination: usize);
15}
16
17pub(crate) trait ChangeAwareListHooks<T> {
20 fn on_insert_item(&mut self, _item: &T) {}
21 fn on_insert_items(&mut self, _items: &[T]) {}
22 fn on_remove_item(&mut self, _item: &T) {}
23 fn on_remove_items(&mut self, _items: &[T]) {}
24 fn on_set_item(&mut self, _new_item: &T, _old_item: &T) {}
25 fn on_clear(&mut self) {}
26 fn contains(&self, _item: &T) -> Option<bool> {
28 None
29 }
30}
31
32pub struct ChangeAwareList<T> {
35 inner: Vec<T>,
36 changes: Vec<Change<T>>,
37 hooks: Option<Box<dyn ChangeAwareListHooks<T>>>,
38}
39
40impl<T: Clone + PartialEq> Default for ChangeAwareList<T> {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl<T: Clone + PartialEq> ChangeAwareList<T> {
47 pub fn new() -> Self {
49 Self { inner: Vec::new(), changes: Vec::new(), hooks: None }
50 }
51
52 pub fn with_capacity(capacity: usize) -> Self {
54 Self { inner: Vec::with_capacity(capacity), changes: Vec::new(), hooks: None }
55 }
56
57 pub fn from_items(items: Vec<T>) -> Self {
59 let mut changes = Vec::new();
60 if !items.is_empty() {
61 changes.push(Change::range_change(ListChangeReason::AddRange, items.clone(), None));
62 }
63 Self { inner: items, changes, hooks: None }
64 }
65
66 pub fn from_list(list: &ChangeAwareList<T>, copy_changes: bool) -> Self {
68 Self { inner: list.inner.clone(), changes: if copy_changes { list.changes.clone() } else { Vec::new() }, hooks: None }
69 }
70
71 pub(crate) fn with_hooks(hooks: Box<dyn ChangeAwareListHooks<T>>) -> Self {
72 Self { inner: Vec::new(), changes: Vec::new(), hooks: Some(hooks) }
73 }
74
75 pub fn count(&self) -> usize {
77 self.inner.len()
78 }
79
80 pub fn is_empty(&self) -> bool {
81 self.inner.is_empty()
82 }
83
84 pub fn items(&self) -> &[T] {
86 &self.inner
87 }
88
89 pub fn get(&self, index: usize) -> &T {
91 &self.inner[index]
92 }
93
94 pub fn set(&mut self, index: usize, item: T) {
96 assert!(index <= self.inner.len(), "index cannot be greater than the size of the collection");
97 let previous = self.inner[index].clone();
98 self.changes.push(Change::with_previous(ListChangeReason::Replace, item.clone(), Some(previous.clone()), Some(index), Some(index)));
99 self.inner[index] = item.clone();
100 if let Some(hooks) = &mut self.hooks {
101 hooks.on_set_item(&item, &previous);
102 }
103 }
104
105 pub fn add(&mut self, item: T) {
107 let index = self.inner.len();
108 self.insert_item(index, item);
109 }
110
111 pub fn capture_changes(&mut self) -> ChangeSet<T> {
114 if self.changes.is_empty() {
115 return ChangeSet::empty();
116 }
117 let mut changes = ChangeSet::from(std::mem::take(&mut self.changes));
118 if self.inner.is_empty() && changes.removes() == changes.total_changes() && changes.total_changes() > 1 {
120 let removed: Vec<T> = unified(&changes).into_iter().map(|change| change.current).collect();
121 changes = ChangeSet::from(vec![Change::range_change(ListChangeReason::Clear, removed, None)]);
122 }
123 changes
124 }
125
126 pub fn clear(&mut self) {
128 if let Some(hooks) = &mut self.hooks {
129 hooks.on_clear();
130 }
131 if self.inner.is_empty() {
132 return;
133 }
134 let removed = std::mem::take(&mut self.inner);
135 self.changes.push(Change::range_change(ListChangeReason::Clear, removed, None));
136 }
137
138 pub fn contains(&self, item: &T) -> bool {
140 if let Some(answer) = self.hooks.as_ref().and_then(|hooks| hooks.contains(item)) {
141 return answer;
142 }
143 self.inner.contains(item)
144 }
145
146 pub fn index_of(&self, item: &T) -> Option<usize> {
148 self.inner.iter().position(|candidate| candidate == item)
149 }
150
151 pub fn index_of_with(&self, item: &T, comparer: &dyn Fn(&T, &T) -> bool) -> Option<usize> {
153 self.inner.iter().position(|candidate| comparer(candidate, item))
154 }
155
156 pub fn insert(&mut self, index: usize, item: T) {
161 assert!(index <= self.inner.len(), "index cannot be greater than the size of the collection");
162 self.insert_item(index, item);
163 }
164
165 pub fn move_item(&mut self, item: &T, destination: usize) {
167 assert!(destination <= self.inner.len(), "destination cannot be greater than the size of the collection");
168 let index = self.index_of(item).expect("the item to move is in the list");
169 self.move_index(index, destination);
170 }
171
172 pub fn refresh_with(&mut self, item: T, index: usize) {
174 assert!(index <= self.inner.len(), "index cannot be greater than the size of the collection");
175 let previous = std::mem::replace(&mut self.inner[index], item.clone());
176 self.changes.push(Change::with_previous(ListChangeReason::Refresh, item, Some(previous), Some(index), None));
177 }
178
179 pub fn refresh(&mut self, item: &T) -> bool {
181 let Some(index) = self.index_of(item) else { return false };
182 self.changes.push(Change::new(ListChangeReason::Refresh, item.clone(), Some(index)));
183 true
184 }
185
186 pub fn refresh_at(&mut self, index: usize) {
188 assert!(index <= self.inner.len(), "index cannot be greater than the size of the collection");
189 self.changes.push(Change::new(ListChangeReason::Refresh, self.inner[index].clone(), Some(index)));
190 }
191
192 pub fn remove(&mut self, item: &T) -> bool {
194 let Some(index) = self.index_of(item) else { return false };
195 let item = self.inner[index].clone();
196 self.remove_item(index, item);
197 true
198 }
199
200 pub fn remove_at(&mut self, index: usize) {
202 assert!(index <= self.inner.len(), "index cannot be greater than the size of the collection");
203 let item = self.inner[index].clone();
204 self.remove_item(index, item);
205 }
206
207 fn insert_item(&mut self, index: usize, item: T) {
209 assert!(index <= self.inner.len(), "index cannot be greater than the size of the collection");
210 if let Some(hooks) = &mut self.hooks {
211 hooks.on_insert_item(&item);
212 }
213 let last_reason = self.changes.last().map(Change::reason);
215 match last_reason {
216 Some(ListChangeReason::Add) => {
217 let first_of_batch = self.changes.len() - 1;
218 let previous = self.changes[first_of_batch].item().clone();
219 let previous_index = previous.current_index();
220 if Some(index) == previous_index {
221 self.changes[first_of_batch] = Change::range_change(ListChangeReason::AddRange, vec![item.clone(), previous.current().clone()], Some(index));
222 } else if previous_index.is_some_and(|previous_index| index == previous_index + 1) {
223 self.changes[first_of_batch] = Change::range_change(ListChangeReason::AddRange, vec![previous.current().clone(), item.clone()], previous_index);
224 } else {
225 self.changes.push(Change::new(ListChangeReason::Add, item.clone(), Some(index)));
226 }
227 }
228 Some(ListChangeReason::AddRange) => {
229 let last = self.changes.last_mut().expect("there is a last change");
230 let range = last.range_mut();
231 let range_index = range.index().map_or(-1, |index| index as isize);
233 let minimum = (range_index - 1).max(0);
234 let maximum = range_index + range.count() as isize;
235 let position = index as isize;
236 if position >= minimum && position <= maximum {
237 let insert_position = (position - range_index).clamp(0, range.count() as isize) as usize;
238 range.insert(insert_position, item.clone());
239 if position < range_index {
240 range.set_starting_index(Some(index));
241 }
242 } else {
243 self.changes.push(Change::new(ListChangeReason::Add, item.clone(), Some(index)));
244 }
245 }
246 _ => self.changes.push(Change::new(ListChangeReason::Add, item.clone(), Some(index))),
248 }
249 self.inner.insert(index, item);
250 }
251
252 fn remove_item(&mut self, index: usize, item: T) {
254 assert!(index <= self.inner.len(), "index cannot be greater than the size of the collection");
255 if let Some(hooks) = &mut self.hooks {
256 hooks.on_remove_item(&item);
257 }
258 let last_reason = self.changes.last().map(Change::reason);
259 match last_reason {
260 Some(ListChangeReason::Remove) => {
261 let first_of_batch = self.changes.len() - 1;
262 let previous = self.changes[first_of_batch].item().clone();
263 let previous_index = previous.current_index();
264 if Some(index) == previous_index {
265 self.changes[first_of_batch] = Change::range_change(ListChangeReason::RemoveRange, vec![previous.current().clone(), item.clone()], Some(index));
266 } else if previous_index.is_some_and(|previous_index| index + 1 == previous_index) {
267 self.changes[first_of_batch] = Change::range_change(ListChangeReason::RemoveRange, vec![item.clone(), previous.current().clone()], Some(index));
268 } else {
269 self.changes.push(Change::new(ListChangeReason::Remove, item.clone(), Some(index)));
270 }
271 }
272 Some(ListChangeReason::RemoveRange) => {
273 let range_index = self.changes.last().expect("there is a last change").range().index();
274 if range_index == Some(index) {
275 self.changes.last_mut().expect("there is a last change").range_mut().add(item.clone());
277 } else if range_index == Some(index + 1) {
278 let range = self.changes.last_mut().expect("there is a last change").range_mut();
280 range.insert(0, item.clone());
281 range.set_starting_index(Some(index));
282 } else {
283 self.changes.push(Change::new(ListChangeReason::Remove, item.clone(), Some(index)));
285 }
286 }
287 _ => self.changes.push(Change::new(ListChangeReason::Remove, item.clone(), Some(index))),
289 }
290 self.inner.remove(index);
291 }
292}
293
294impl<T: Clone + PartialEq> ChangeAwareList<T> {
295 pub fn add_range(&mut self, items: Vec<T>) {
297 if items.is_empty() {
298 return;
299 }
300 let index = self.inner.len();
301 self.changes.push(Change::range_change(ListChangeReason::AddRange, items.clone(), Some(index)));
302 if let Some(hooks) = &mut self.hooks {
303 hooks.on_insert_items(&items);
304 }
305 self.inner.extend(items);
306 }
307
308 pub fn remove_range(&mut self, index: usize, count: usize) {
310 assert!(count <= self.inner.len() && index <= self.inner.len() - count, "the range is beyond the end of the collection");
311 let removed: Vec<T> = self.inner[index..index + count].to_vec();
312 if removed.is_empty() {
313 return;
314 }
315 self.changes.push(Change::range_change(ListChangeReason::RemoveRange, removed.clone(), Some(index)));
316 self.inner.drain(index..index + count);
317 if let Some(hooks) = &mut self.hooks {
318 hooks.on_remove_items(&removed);
319 }
320 }
321}
322
323impl<T: Clone + PartialEq> IExtendedList<T> for ChangeAwareList<T> {
324 fn insert_range(&mut self, items: Vec<T>, index: usize) {
325 if items.is_empty() {
326 return;
327 }
328 self.changes.push(Change::range_change(ListChangeReason::AddRange, items.clone(), Some(index)));
329 self.inner.splice(index..index, items.iter().cloned());
330 if let Some(hooks) = &mut self.hooks {
331 hooks.on_insert_items(&items);
332 }
333 }
334
335 fn move_index(&mut self, original: usize, destination: usize) {
336 assert!(original <= self.inner.len(), "original cannot be greater than the size of the collection");
337 assert!(destination <= self.inner.len(), "destination cannot be greater than the size of the collection");
338 let item = self.inner.remove(original);
339 self.inner.insert(destination, item.clone());
340 self.changes.push(Change::moved(item, destination, original));
341 }
342
343}