ferroui_dynamic_data/binding/
observable_collection_ex.rs1use super::{ObservableCollectionExtended, ReadOnlyObservableCollection};
5use crate::list::{ChangeAwareList, ChangeSet, IExtendedList, ObservableListEx};
6use ferroui_reactive::disposables::Disposable;
7use ferroui_reactive::linq::EventPattern;
8use ferroui_reactive::{Observable, ObservableExt};
9use ferroui_system::collections::object_model::ObservableCollection;
10use ferroui_system::collections::specialized::{NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs, SharedNotifyCollectionChangedEventArgs};
11use ferroui_system::{IObservable, IObserver};
12use std::cell::RefCell;
13use std::hash::Hash;
14use std::rc::Rc;
15
16pub type CollectionChangedEventPattern<T> = EventPattern<ObservableCollection<T>, SharedNotifyCollectionChangedEventArgs<T>>;
19
20pub trait ObservableCollectionEx<T: Clone + PartialEq + 'static> {
28 fn notifying_collection(&self) -> ObservableCollection<T>;
30
31 fn observe_collection_changes(&self) -> Rc<dyn IObservable<CollectionChangedEventPattern<T>>> {
33 observe_collection_changes(self.notifying_collection())
34 }
35
36 fn to_observable_change_set(&self) -> Rc<dyn IObservable<ChangeSet<T>>> {
39 to_observable_change_set(self.notifying_collection())
40 }
41
42 fn to_observable_change_set_with_key<K: Eq + Hash + Clone + 'static>(&self, key_selector: impl Fn(&T) -> K + 'static) -> Rc<dyn IObservable<crate::cache::ChangeSet<T, K>>> {
45 self.to_observable_change_set().add_key(key_selector)
46 }
47}
48
49impl<T: Clone + PartialEq + 'static> ObservableCollectionEx<T> for ObservableCollection<T> {
50 fn notifying_collection(&self) -> ObservableCollection<T> {
51 self.clone()
52 }
53}
54
55impl<T: Clone + PartialEq + 'static> ObservableCollectionEx<T> for ObservableCollectionExtended<T> {
56 fn notifying_collection(&self) -> ObservableCollection<T> {
57 self.collection().clone()
58 }
59}
60
61impl<T: Clone + PartialEq + 'static> ObservableCollectionEx<T> for ReadOnlyObservableCollection<T> {
62 fn notifying_collection(&self) -> ObservableCollection<T> {
63 self.collection().clone()
64 }
65}
66
67fn observe_collection_changes<T: Clone + PartialEq + 'static>(source: ObservableCollection<T>) -> Rc<dyn IObservable<CollectionChangedEventPattern<T>>> {
68 Observable::create(move |observer: Rc<dyn IObserver<CollectionChangedEventPattern<T>>>| {
69 let sender = source.clone();
70 let token = source.add_collection_changed(Rc::new(move |args: &NotifyCollectionChangedEventArgs<'_, T>| {
71 observer.on_next(EventPattern::new(sender.clone(), args.share()));
72 }));
73 let source = source.clone();
74 Disposable::create(move || {
75 source.remove_collection_changed(token);
76 })
77 })
78}
79
80fn to_observable_change_set<T: Clone + PartialEq + 'static>(source: ObservableCollection<T>) -> Rc<dyn IObservable<ChangeSet<T>>> {
81 Observable::create(move |observer: Rc<dyn IObserver<ChangeSet<T>>>| {
82 let data = Rc::new(RefCell::new(ChangeAwareList::from_items(source.to_vec())));
83
84 let initial = data.borrow_mut().capture_changes();
85 observer.on_next(initial);
86
87 let collection = source.clone();
88 observe_collection_changes(source.clone()).subscribe_next(move |args: CollectionChangedEventPattern<T>| {
89 let changes = args.event_args;
90 let captured = {
91 let mut list = data.borrow_mut();
92 apply(&mut list, &changes, &collection);
93 list.capture_changes()
94 };
95 observer.on_next(captured);
96 })
97 })
98}
99
100fn apply<T: Clone + PartialEq + 'static>(list: &mut ChangeAwareList<T>, changes: &SharedNotifyCollectionChangedEventArgs<T>, source: &ObservableCollection<T>) {
103 match changes.action() {
104 NotifyCollectionChangedAction::Add => {
105 let new_index = if changes.new_starting_index() == -1 { list.count() } else { changes.new_starting_index() as usize };
106 if changes.new_items().len() == 1 {
107 list.insert(new_index, changes.new_items()[0].clone());
108 } else {
109 IExtendedList::insert_range(list, changes.new_items().to_vec(), new_index);
110 }
111 }
112 NotifyCollectionChangedAction::Remove => {
113 if changes.old_starting_index() == -1 {
114 for item in changes.old_items() {
115 list.remove(item);
116 }
117 } else if changes.old_items().len() == 1 {
118 list.remove_at(changes.old_starting_index() as usize);
119 } else {
120 list.remove_range(changes.old_starting_index() as usize, changes.old_items().len());
121 }
122 }
123 NotifyCollectionChangedAction::Replace => {
124 if let Some(replaced_item) = changes.new_items().first() {
125 if changes.new_starting_index() == -1 {
126 let original = changes.old_items().first().expect("a replacement has the item it replaced");
127 let old_index = list.index_of(original).expect("the replaced item is in the list");
128 list.set(old_index, replaced_item.clone());
129 } else {
130 list.set(changes.new_starting_index() as usize, replaced_item.clone());
131 }
132 }
133 }
134 NotifyCollectionChangedAction::Reset => {
135 list.clear();
136 list.add_range(source.to_vec());
137 }
138 NotifyCollectionChangedAction::Move => {
139 assert!(changes.old_starting_index() != -1, "Move -> OldStartingIndex");
141 assert!(changes.new_starting_index() != -1, "Move -> NewStartingIndex");
142 IExtendedList::move_index(list, changes.old_starting_index() as usize, changes.new_starting_index() as usize);
143 }
144 }
145}
146