Skip to main content

ferroui_dynamic_data/binding/
i_observable_collection.rs

1//! `IObservableCollection<T>` and `INotifyCollectionChangedSuspender`
2//! (`Binding/IObservableCollection.cs`, `Binding/INotifyCollectionChangedSuspender.cs`).
3
4use crate::list::{ChangeSet, IList};
5use ferroui_system::collections::specialized::INotifyCollectionChanged;
6use ferroui_system::component_model::INotifyPropertyChanged;
7use ferroui_system::IDisposable;
8use std::rc::Rc;
9
10/// A collection whose notifications can be held back (`INotifyCollectionChangedSuspender`).
11pub trait INotifyCollectionChangedSuspender {
12    /// Holds back the notifications of `Count` until the handle is disposed, which raises `Count`
13    /// once if it changed (`SuspendCount`).
14    fn suspend_count(&self) -> Rc<dyn IDisposable>;
15
16    /// Holds back the notifications of changes and of `Count` until the handle is disposed, which
17    /// raises `Count` and a reset (`SuspendNotifications`).
18    fn suspend_notifications(&self) -> Rc<dyn IDisposable>;
19}
20
21/// The collection that the binding operators fill (`IObservableCollection<T>`): a list that
22/// notifies its changes and can hold its notifications back.
23///
24/// Deviation (DEVIATIONS.md, DynamicData): the collection is a shared handle, as the object of
25/// .NET is a reference, so its methods take `&self`; the members of `IList<T>` that the binding
26/// uses are part of the trait.
27pub trait IObservableCollection<T: Clone + PartialEq + 'static>: INotifyCollectionChanged + INotifyPropertyChanged + INotifyCollectionChangedSuspender {
28    /// The number of items (`Count`).
29    fn count(&self) -> usize;
30    /// The item at `index` (the indexer's getter).
31    fn get(&self, index: usize) -> T;
32    /// Replaces the item at `index` (the indexer's setter).
33    fn set(&self, index: usize, item: T);
34    /// Inserts an item (`Insert`).
35    fn insert(&self, index: usize, item: T);
36    /// Removes the item at `index` (`RemoveAt`).
37    fn remove_at(&self, index: usize);
38    /// Removes every item (`Clear`).
39    fn clear(&self);
40    /// A copy of the items.
41    fn to_vec(&self) -> Vec<T>;
42
43    /// Adds an item at the end (`Add`).
44    fn add(&self, item: T) {
45        let index = self.count();
46        self.insert(index, item);
47    }
48
49    /// The index of `item` (`IndexOf`).
50    fn index_of(&self, item: &T) -> Option<usize> {
51        (0..self.count()).find(|&index| &self.get(index) == item)
52    }
53
54    /// Whether the collection has `item` (`Contains`).
55    fn contains(&self, item: &T) -> bool {
56        self.index_of(item).is_some()
57    }
58
59    /// Removes `item`; false when it is not there (`Remove`).
60    fn remove(&self, item: &T) -> bool {
61        match self.index_of(item) {
62            Some(index) => {
63                self.remove_at(index);
64                true
65            }
66            None => false,
67        }
68    }
69
70    /// Clears the collection and adds the items (`Load`).
71    fn load(&self, items: Vec<T>);
72
73    /// Moves the item at `old_index` to `new_index` (`Move`).
74    fn move_item(&self, old_index: usize, new_index: usize);
75
76    /// Applies the changes of a list (`ListEx.Clone(IList<T>, IChangeSet<T>)`): the general form,
77    /// which moves items with [`move_item`](Self::move_item) as upstream does for an
78    /// `ObservableCollection<T>`.
79    fn clone_changes(&self, changes: &ChangeSet<T>) {
80        crate::list::clone_changes(&mut CollectionAsList::new(self), changes);
81    }
82}
83
84/// An [`IObservableCollection`] seen as an [`IList`], for the functions of `ListEx`.
85pub(crate) struct CollectionAsList<'a, T: Clone + PartialEq + 'static, C: IObservableCollection<T> + ?Sized> {
86    collection: &'a C,
87    item: std::marker::PhantomData<T>,
88}
89
90impl<'a, T: Clone + PartialEq + 'static, C: IObservableCollection<T> + ?Sized> CollectionAsList<'a, T, C> {
91    pub(crate) fn new(collection: &'a C) -> Self {
92        Self { collection, item: std::marker::PhantomData }
93    }
94}
95
96impl<T: Clone + PartialEq + 'static, C: IObservableCollection<T> + ?Sized> IList<T> for CollectionAsList<'_, T, C> {
97    fn count(&self) -> usize {
98        self.collection.count()
99    }
100
101    fn get(&self, index: usize) -> T {
102        self.collection.get(index)
103    }
104
105    fn set(&mut self, index: usize, item: T) {
106        self.collection.set(index, item);
107    }
108
109    fn insert(&mut self, index: usize, item: T) {
110        self.collection.insert(index, item);
111    }
112
113    fn remove_at(&mut self, index: usize) {
114        self.collection.remove_at(index);
115    }
116
117    fn clear(&mut self) {
118        self.collection.clear();
119    }
120
121    fn add(&mut self, item: T) {
122        self.collection.add(item);
123    }
124
125    fn index_of(&self, item: &T) -> Option<usize> {
126        self.collection.index_of(item)
127    }
128
129    fn remove(&mut self, item: &T) -> bool {
130        self.collection.remove(item)
131    }
132
133    fn move_item(&mut self, original: usize, destination: usize) {
134        self.collection.move_item(original, destination);
135    }
136}