ferroui_system/collections/object_model/
observable_collection.rs1use crate::collections::specialized::{CollectionChange, INotifyCollectionChanged, NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs};
4use crate::component_model::INotifyPropertyChanged;
5use crate::events::{Event, HandlerList};
6use std::cell::{Cell, Ref, RefCell, RefMut};
7use std::rc::Rc;
8
9pub type CollectionChangedHandler<T> = dyn for<'a> Fn(&NotifyCollectionChangedEventArgs<'a, T>);
11
12const INDEXER_NAME: &str = "Item[]";
14
15struct Inner<T> {
16 items: RefCell<Vec<T>>,
17 collection_changed: HandlerList<CollectionChangedHandler<T>>,
18 untyped_collection_changed: Event<CollectionChange>,
19 property_changed: Event<str>,
20 block_reentrancy_count: Cell<usize>,
22 notifications_suspended: Cell<bool>,
25}
26
27pub struct ObservableCollection<T> {
31 inner: Rc<Inner<T>>,
32}
33
34impl<T> Clone for ObservableCollection<T> {
35 fn clone(&self) -> Self {
36 Self { inner: self.inner.clone() }
37 }
38}
39
40impl<T: Clone + PartialEq + 'static> Default for ObservableCollection<T> {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl<T> PartialEq for ObservableCollection<T> {
47 fn eq(&self, other: &Self) -> bool {
49 Rc::ptr_eq(&self.inner, &other.inner)
50 }
51}
52
53impl<T: Clone + PartialEq + 'static> ObservableCollection<T> {
54 pub fn new() -> Self {
56 Self::from_items(Vec::new())
57 }
58
59 pub fn from_items(items: Vec<T>) -> Self {
61 Self {
62 inner: Rc::new(Inner {
63 items: RefCell::new(items),
64 collection_changed: HandlerList::new(),
65 untyped_collection_changed: Event::new(),
66 property_changed: Event::new(),
67 block_reentrancy_count: Cell::new(0),
68 notifications_suspended: Cell::new(false),
69 }),
70 }
71 }
72
73 pub fn ptr_eq(&self, other: &Self) -> bool {
75 Rc::ptr_eq(&self.inner, &other.inner)
76 }
77
78 pub fn add_collection_changed(&self, handler: Rc<CollectionChangedHandler<T>>) -> u64 {
81 self.inner.collection_changed.add(handler)
82 }
83
84 pub fn remove_collection_changed(&self, token: u64) -> bool {
86 self.inner.collection_changed.remove(token)
87 }
88
89 pub fn count(&self) -> usize {
91 self.inner.items.borrow().len()
92 }
93
94 pub fn is_empty(&self) -> bool {
95 self.inner.items.borrow().is_empty()
96 }
97
98 pub fn get(&self, index: usize) -> T {
103 self.inner.items.borrow()[index].clone()
104 }
105
106 pub fn items(&self) -> Ref<'_, Vec<T>> {
108 self.inner.items.borrow()
109 }
110
111 pub fn to_vec(&self) -> Vec<T> {
113 self.inner.items.borrow().clone()
114 }
115
116 pub fn contains(&self, item: &T) -> bool {
118 self.inner.items.borrow().contains(item)
119 }
120
121 pub fn index_of(&self, item: &T) -> Option<usize> {
123 self.inner.items.borrow().iter().position(|candidate| candidate == item)
124 }
125
126 pub fn add(&self, item: T) {
128 let index = self.count();
129 self.insert(index, item);
130 }
131
132 pub fn insert(&self, index: usize, item: T) {
134 self.check_reentrancy();
135 self.inner.items.borrow_mut().insert(index, item.clone());
136 self.on_count_property_changed();
137 self.on_indexer_property_changed();
138 self.on_collection_changed(NotifyCollectionChangedAction::Add, std::slice::from_ref(&item), &[], index as i32, -1);
139 }
140
141 pub fn set(&self, index: usize, item: T) {
143 self.check_reentrancy();
144 let original = std::mem::replace(&mut self.inner.items.borrow_mut()[index], item.clone());
145 self.on_indexer_property_changed();
146 self.on_collection_changed(NotifyCollectionChangedAction::Replace, std::slice::from_ref(&item), std::slice::from_ref(&original), index as i32, index as i32);
147 }
148
149 pub fn remove_at(&self, index: usize) {
151 self.check_reentrancy();
152 let removed = self.inner.items.borrow_mut().remove(index);
153 self.on_count_property_changed();
154 self.on_indexer_property_changed();
155 self.on_collection_changed(NotifyCollectionChangedAction::Remove, &[], std::slice::from_ref(&removed), -1, index as i32);
156 }
157
158 pub fn remove(&self, item: &T) -> bool {
160 match self.index_of(item) {
161 Some(index) => {
162 self.remove_at(index);
163 true
164 }
165 None => false,
166 }
167 }
168
169 pub fn move_item(&self, old_index: usize, new_index: usize) {
171 self.check_reentrancy();
172 let item = {
173 let mut items = self.inner.items.borrow_mut();
174 let item = items.remove(old_index);
175 items.insert(new_index, item.clone());
176 item
177 };
178 self.on_indexer_property_changed();
179 self.on_collection_changed(NotifyCollectionChangedAction::Move, std::slice::from_ref(&item), std::slice::from_ref(&item), new_index as i32, old_index as i32);
180 }
181
182 pub fn clear(&self) {
184 self.check_reentrancy();
185 self.inner.items.borrow_mut().clear();
186 self.on_count_property_changed();
187 self.on_indexer_property_changed();
188 self.on_collection_reset();
189 }
190
191 pub fn items_mut_silent(&self) -> RefMut<'_, Vec<T>> {
194 self.inner.items.borrow_mut()
195 }
196
197 pub fn set_notifications_suspended(&self, suspended: bool) {
200 self.inner.notifications_suspended.set(suspended);
201 }
202
203 pub fn notifications_suspended(&self) -> bool {
204 self.inner.notifications_suspended.get()
205 }
206
207 pub fn check_reentrancy(&self) {
210 if self.inner.block_reentrancy_count.get() > 0 && self.inner.collection_changed.len() + usize::from(self.inner.untyped_collection_changed.has_handlers()) > 1 {
211 panic!("Cannot change ObservableCollection during a CollectionChanged event.");
212 }
213 }
214
215 pub fn on_property_changed(&self, name: &str) {
217 if self.inner.notifications_suspended.get() {
218 return;
219 }
220 self.inner.property_changed.raise(name);
221 }
222
223 pub fn on_count_property_changed(&self) {
225 self.on_property_changed("Count");
226 }
227
228 pub fn on_indexer_property_changed(&self) {
230 self.on_property_changed(INDEXER_NAME);
231 }
232
233 pub fn on_collection_reset(&self) {
235 self.on_collection_changed(NotifyCollectionChangedAction::Reset, &[], &[], -1, -1);
236 }
237
238 pub fn on_collection_changed(&self, action: NotifyCollectionChangedAction, new_items: &[T], old_items: &[T], new_starting_index: i32, old_starting_index: i32) {
241 if self.inner.notifications_suspended.get() {
242 return;
243 }
244 self.inner.block_reentrancy_count.set(self.inner.block_reentrancy_count.get() + 1);
245 struct Unblock<'a>(&'a Cell<usize>);
246 impl Drop for Unblock<'_> {
247 fn drop(&mut self) {
248 self.0.set(self.0.get() - 1);
249 }
250 }
251 let _unblock = Unblock(&self.inner.block_reentrancy_count);
252 let args = NotifyCollectionChangedEventArgs { action, new_items, old_items, new_starting_index, old_starting_index };
253 for (_, handler) in self.inner.collection_changed.snapshot().iter() {
254 handler(&args);
255 }
256 self.inner.untyped_collection_changed.raise(&CollectionChange {
257 action,
258 new_starting_index,
259 new_count: new_items.len(),
260 old_starting_index,
261 old_count: old_items.len(),
262 });
263 }
264}
265
266impl<T> INotifyCollectionChanged for ObservableCollection<T> {
267 fn collection_changed(&self) -> &Event<CollectionChange> {
268 &self.inner.untyped_collection_changed
269 }
270}
271
272impl<T> INotifyPropertyChanged for ObservableCollection<T> {
273 fn property_changed(&self) -> &Event<str> {
274 &self.inner.property_changed
275 }
276}