ferroui_dynamic_data/list/
source_list.rs1use super::internal::{filter_static, ReaderWriter};
5use super::{Change, ChangeSet, IExtendedList, ListChangeReason};
6use ferroui_reactive::disposables::Disposable;
7use ferroui_reactive::subjects::{BehaviorSubject, Subject, SubjectBase};
8use ferroui_reactive::{Observable, ObservableExt};
9use ferroui_system::{IDisposable, IObservable, IObserver, ObservableError};
10use std::cell::{Cell, RefCell};
11use std::rc::{Rc, Weak};
12
13pub trait IObservableList<T>: IDisposable {
15 fn count(&self) -> usize;
17 fn count_changed(&self) -> Rc<dyn IObservable<usize>>;
19 fn items(&self) -> Vec<T>;
21 fn connect_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>) -> Rc<dyn IObservable<ChangeSet<T>>>;
24 fn preview_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>) -> Rc<dyn IObservable<ChangeSet<T>>>;
26
27 fn connect(&self) -> Rc<dyn IObservable<ChangeSet<T>>> {
29 self.connect_with(None)
30 }
31
32 fn preview(&self) -> Rc<dyn IObservable<ChangeSet<T>>> {
34 self.preview_with(None)
35 }
36}
37
38pub trait ISourceList<T: Clone + PartialEq>: IObservableList<T> {
40 fn edit_dyn(&self, update: &mut dyn FnMut(&mut dyn IExtendedList<T>));
42}
43
44struct Inner<T> {
45 this: Weak<Inner<T>>,
46 changes: Rc<Subject<ChangeSet<T>>>,
47 changes_preview: Rc<Subject<ChangeSet<T>>>,
48 count_changed: RefCell<Option<Rc<Subject<usize>>>>,
49 reader_writer: ReaderWriter<T>,
50 is_edit_in_progress: RefCell<Option<Rc<BehaviorSubject<bool>>>>,
51 edit_level: Cell<usize>,
52 loader: RefCell<Option<Rc<dyn IDisposable>>>,
53 disposed: Cell<bool>,
54}
55
56pub struct SourceList<T> {
59 inner: Rc<Inner<T>>,
60}
61
62impl<T> Clone for SourceList<T> {
63 fn clone(&self) -> Self {
64 Self { inner: self.inner.clone() }
65 }
66}
67
68impl<T: Clone + PartialEq + 'static> Default for SourceList<T> {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74impl<T: Clone + PartialEq + 'static> SourceList<T> {
75 fn create() -> Self {
76 let inner = Rc::new_cyclic(|this| Inner {
77 this: this.clone(),
78 changes: Subject::new(),
79 changes_preview: Subject::new(),
80 count_changed: RefCell::new(None),
81 reader_writer: ReaderWriter::new(),
82 is_edit_in_progress: RefCell::new(None),
83 edit_level: Cell::new(0),
84 loader: RefCell::new(None),
85 disposed: Cell::new(false),
86 });
87 Self { inner }
88 }
89
90 pub fn new() -> Self {
92 Self::create()
93 }
94
95 pub fn from_source<C: super::IChangeSet<T>>(source: Rc<dyn IObservable<C>>) -> Self {
97 let list = Self::create();
98 let (next, error, completed) = (Rc::downgrade(&list.inner), Rc::downgrade(&list.inner), Rc::downgrade(&list.inner));
99 let finally_target = completed.clone();
100 let loader = source
101 .finally(move || {
102 if let Some(inner) = finally_target.upgrade() {
103 inner.on_completed();
104 }
105 })
106 .subscribe_all(
107 move |changes: C| {
108 if let Some(inner) = next.upgrade() {
109 let changes = inner.reader_writer.write_changes(changes.change_set());
110 inner.invoke_next(&changes);
111 }
112 },
113 move |e: ObservableError| {
114 if let Some(inner) = error.upgrade() {
115 inner.on_error(e);
116 }
117 },
118 move || {
119 if let Some(inner) = completed.upgrade() {
120 inner.on_completed();
121 }
122 },
123 );
124 *list.inner.loader.borrow_mut() = Some(loader);
125 list
126 }
127
128 pub fn edit(&self, update: impl FnOnce(&mut dyn IExtendedList<T>)) {
131 let mut update = Some(update);
132 self.inner.edit(&mut |list| (update.take().expect("an edit runs once"))(list));
133 }
134}
135
136impl<T: Clone + PartialEq + 'static> Inner<T> {
137 fn edit_in_progress_subject(&self) -> Option<Rc<BehaviorSubject<bool>>> {
138 self.is_edit_in_progress.borrow().clone()
139 }
140
141 fn edit(&self, update: &mut dyn FnMut(&mut dyn IExtendedList<T>)) {
143 let level = self.edit_level.get() + 1;
144 self.edit_level.set(level);
145 if level == 1 {
146 if let Some(subject) = self.edit_in_progress_subject() {
147 subject.on_next(true);
148 }
149 }
150 struct Level<'a, T: Clone + PartialEq + 'static>(&'a Inner<T>, bool);
154 impl<T: Clone + PartialEq + 'static> Drop for Level<'_, T> {
155 fn drop(&mut self) {
156 if self.1 {
157 let level = self.0.edit_level.get() - 1;
158 self.0.edit_level.set(level);
159 if level == 0 {
160 if let Some(subject) = self.0.edit_in_progress_subject() {
161 subject.on_next(false);
162 }
163 }
164 }
165 }
166 }
167 let mut guard = Level(self, true);
168 let changes = if level == 1 {
169 if self.changes_preview.has_observers() {
170 let this = self.this.clone();
171 Some(self.reader_writer.write_with_preview(update, &move |changes| {
172 if let Some(inner) = this.upgrade() {
173 inner.invoke_next_preview(changes);
174 }
175 }))
176 } else {
177 Some(self.reader_writer.write(update))
178 }
179 } else {
180 self.reader_writer.write_nested(update);
181 None
182 };
183 guard.1 = false;
184 drop(guard);
185 let level = self.edit_level.get() - 1;
186 self.edit_level.set(level);
187 if let Some(changes) = changes {
188 if level == 0 {
189 self.invoke_next(&changes);
190 }
191 }
192 if level == 0 {
194 if let Some(subject) = self.edit_in_progress_subject() {
195 subject.on_next(false);
196 }
197 }
198 }
199
200 fn invoke_next(&self, changes: &ChangeSet<T>) {
201 if changes.count() == 0 {
202 return;
203 }
204 self.changes.on_next(changes.clone());
205 let count = self.count_changed.borrow().clone();
206 if let Some(subject) = count {
207 subject.on_next(self.reader_writer.count());
208 }
209 }
210
211 fn invoke_next_preview(&self, changes: &ChangeSet<T>) {
212 if changes.count() == 0 {
213 return;
214 }
215 self.changes_preview.on_next(changes.clone());
216 }
217
218 fn on_completed(&self) {
219 self.changes_preview.on_completed();
220 self.changes.on_completed();
221 if let Some(subject) = self.edit_in_progress_subject() {
222 subject.on_completed();
223 }
224 }
225
226 fn on_error(&self, error: ObservableError) {
227 self.changes_preview.on_error(error.clone());
228 self.changes.on_error(error.clone());
229 if let Some(subject) = self.edit_in_progress_subject() {
230 subject.on_error(error);
231 }
232 }
233
234 fn create_connect_observable(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>) -> Rc<dyn IObservable<ChangeSet<T>>> {
236 let this = self.this.clone();
237 let observable = Observable::create(move |observer: Rc<dyn IObserver<ChangeSet<T>>>| {
238 let Some(inner) = this.upgrade() else { return Disposable::empty() };
239 let items = inner.reader_writer.items();
240 if !items.is_empty() {
241 observer.on_next(ChangeSet::from(vec![Change::range_change(ListChangeReason::AddRange, items, Some(0))]));
242 }
243 let completed = observer.clone();
244 inner.changes.as_observable().finally(move || completed.on_completed()).subscribe(observer)
245 });
246 match predicate {
247 Some(predicate) => filter_static(observable, predicate),
248 None => observable,
249 }
250 }
251}
252
253impl<T: Clone + PartialEq + 'static> IObservableList<T> for SourceList<T> {
254 fn count(&self) -> usize {
255 self.inner.reader_writer.count()
256 }
257
258 fn count_changed(&self) -> Rc<dyn IObservable<usize>> {
259 let this = Rc::downgrade(&self.inner);
260 Observable::create(move |observer: Rc<dyn IObserver<usize>>| {
261 let Some(inner) = this.upgrade() else { return Disposable::empty() };
262 let subject = inner.count_changed.borrow_mut().get_or_insert_with(Subject::new).clone();
263 subject.as_observable().start_with([inner.reader_writer.count()]).distinct_until_changed().subscribe(observer)
264 })
265 }
266
267 fn items(&self) -> Vec<T> {
268 self.inner.reader_writer.items()
269 }
270
271 fn connect_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>) -> Rc<dyn IObservable<ChangeSet<T>>> {
272 let this = Rc::downgrade(&self.inner);
273 Observable::create(move |observer: Rc<dyn IObserver<ChangeSet<T>>>| {
274 let Some(inner) = this.upgrade() else { return Disposable::empty() };
275 let editing = inner.is_edit_in_progress.borrow().is_some() || inner.edit_level.get() != 0;
276 let observable = if editing {
277 let subject = inner.is_edit_in_progress.borrow_mut().get_or_insert_with(|| BehaviorSubject::new(inner.edit_level.get() != 0)).clone();
279 let (target, predicate) = (this.clone(), predicate.clone());
280 subject.as_observable().where_(|editing| !*editing).take(1).select_many(move |_| match target.upgrade() {
281 Some(inner) => inner.create_connect_observable(predicate.clone()),
282 None => Observable::empty(),
283 })
284 } else {
285 inner.create_connect_observable(predicate.clone())
286 };
287 observable.subscribe(observer)
288 })
289 }
290
291 fn preview_with(&self, predicate: Option<Rc<dyn Fn(&T) -> bool>>) -> Rc<dyn IObservable<ChangeSet<T>>> {
292 let preview = self.inner.changes_preview.as_observable();
293 match predicate {
294 Some(predicate) => filter_static(preview, predicate),
295 None => preview,
296 }
297 }
298}
299
300impl<T: Clone + PartialEq + 'static> ISourceList<T> for SourceList<T> {
301 fn edit_dyn(&self, update: &mut dyn FnMut(&mut dyn IExtendedList<T>)) {
302 self.inner.edit(update);
303 }
304}
305
306impl<T: Clone + PartialEq + 'static> IDisposable for SourceList<T> {
307 fn dispose(&self) {
309 if self.inner.disposed.replace(true) {
310 return;
311 }
312 let loader = self.inner.loader.borrow_mut().take();
313 if let Some(loader) = loader {
314 loader.dispose();
315 }
316 self.inner.on_completed();
317 let count = self.inner.count_changed.borrow().clone();
318 if let Some(subject) = count {
319 subject.on_completed();
320 }
321 }
322}