1use crate::widget::{WidgetId, WidgetIdCommon};
2use intuicio_data::{
3 lifetime::{ValueReadAccess, ValueWriteAccess},
4 managed::DynamicManaged,
5 managed::{Managed, ManagedLazy, ManagedRef, ManagedRefMut},
6};
7use std::{
8 collections::{HashMap, HashSet},
9 ops::{Deref, DerefMut},
10};
11
12pub struct ViewModelBindings {
13 widgets: HashSet<WidgetId>,
14 common_root: WidgetIdCommon,
15 notify: bool,
16}
17
18impl Default for ViewModelBindings {
19 fn default() -> Self {
20 Self {
21 widgets: Default::default(),
22 common_root: Default::default(),
23 notify: true,
24 }
25 }
26}
27
28impl ViewModelBindings {
29 pub fn bind(&mut self, id: WidgetId) {
30 self.widgets.insert(id);
31 self.rebuild_common_root();
32 }
33
34 pub fn unbind(&mut self, id: &WidgetId) {
35 self.widgets.remove(id);
36 self.rebuild_common_root();
37 }
38
39 pub fn clear(&mut self) {
40 self.widgets.clear();
41 self.common_root = Default::default();
42 }
43
44 pub fn is_empty(&self) -> bool {
45 self.widgets.is_empty()
46 }
47
48 pub fn is_bound(&self, id: &WidgetId) -> bool {
49 self.widgets.contains(id)
50 }
51
52 pub fn widgets(&self) -> impl Iterator<Item = &WidgetId> {
53 self.widgets.iter()
54 }
55
56 pub fn common_root(&self) -> &WidgetIdCommon {
57 &self.common_root
58 }
59
60 pub fn notify(&mut self) {
61 self.notify = true;
62 }
63
64 pub fn is_notified(&self) -> bool {
65 self.notify
66 }
67
68 pub fn consume_notification(&mut self) -> bool {
69 !self.widgets.is_empty() && std::mem::take(&mut self.notify)
70 }
71
72 fn rebuild_common_root(&mut self) {
73 self.common_root = WidgetIdCommon::from_iter(self.widgets.iter());
74 }
75}
76
77#[derive(Default)]
78pub struct ViewModelProperties {
79 inner: HashMap<String, Managed<ViewModelBindings>>,
80}
81
82impl ViewModelProperties {
83 pub fn unbind_all(&mut self, id: &WidgetId) {
84 for bindings in self.inner.values_mut() {
85 if let Some(mut bindings) = bindings.write() {
86 bindings.unbind(id);
87 }
88 }
89 }
90
91 pub fn remove(&mut self, id: &str) {
92 self.inner.remove(id);
93 }
94
95 pub fn clear(&mut self) {
96 self.inner.clear();
97 }
98
99 pub fn is_empty(&self) -> bool {
100 self.inner.is_empty()
101 }
102
103 pub fn has(&self, id: &str) -> bool {
104 self.inner.contains_key(id)
105 }
106
107 pub fn remove_empty_bindings(&mut self) {
108 let to_remove = self
109 .inner
110 .iter()
111 .filter_map(|(key, bindings)| {
112 if let Some(bindings) = bindings.read() {
113 if bindings.is_empty() {
114 return Some(key.to_owned());
115 }
116 }
117 None
118 })
119 .collect::<Vec<_>>();
120 for key in to_remove {
121 self.inner.remove(&key);
122 }
123 }
124
125 pub fn bindings(&mut self, id: impl ToString) -> Option<ValueWriteAccess<ViewModelBindings>> {
126 self.inner.entry(id.to_string()).or_default().write()
127 }
128
129 pub fn notifier(&mut self, id: impl ToString) -> ViewModelNotifier {
130 ViewModelNotifier {
131 inner: self.inner.entry(id.to_string()).or_default().lazy(),
132 }
133 }
134
135 pub fn consume_notification(&mut self) -> bool {
136 self.inner.values_mut().any(|bindings| {
137 bindings
138 .write()
139 .map(|mut bindings| bindings.consume_notification())
140 .unwrap_or_default()
141 })
142 }
143
144 pub fn consume_notified_common_root(&mut self) -> WidgetIdCommon {
145 let mut result = WidgetIdCommon::default();
146 for bindings in self.inner.values_mut() {
147 if let Some(mut bindings) = bindings.write() {
148 if bindings.consume_notification() {
149 let root = bindings.common_root();
150 result.include_other(root);
151 }
152 }
153 }
154 result
155 }
156}
157
158#[derive(Clone)]
159pub struct ViewModelNotifier {
160 inner: ManagedLazy<ViewModelBindings>,
161}
162
163impl ViewModelNotifier {
164 pub fn notify(&mut self) -> bool {
165 if let Some(mut bindings) = self.inner.write() {
166 bindings.notify();
167 true
168 } else {
169 false
170 }
171 }
172}
173
174pub struct ViewModel {
175 object: DynamicManaged,
176 pub properties: ViewModelProperties,
177}
178
179impl ViewModel {
180 pub fn new<T: 'static>(object: T, properties: ViewModelProperties) -> Self {
181 Self {
182 object: DynamicManaged::new(object).ok().unwrap(),
183 properties,
184 }
185 }
186
187 pub fn new_object<T: 'static>(object: T) -> Self {
188 Self::new(object, Default::default())
189 }
190
191 pub fn produce<T: 'static>(producer: impl FnOnce(&mut ViewModelProperties) -> T) -> Self {
192 let mut properties = Default::default();
193 let object = DynamicManaged::new(producer(&mut properties)).ok().unwrap();
194 Self { object, properties }
195 }
196
197 pub fn borrow<T: 'static>(&self) -> Option<ManagedRef<T>> {
198 self.object
199 .borrow()
200 .and_then(|object| object.into_typed::<T>().ok())
201 }
202
203 pub fn borrow_mut<T: 'static>(&mut self) -> Option<ManagedRefMut<T>> {
204 self.object
205 .borrow_mut()
206 .and_then(|object| object.into_typed::<T>().ok())
207 }
208
209 pub fn lazy<T: 'static>(&self) -> Option<ManagedLazy<T>> {
210 self.object.lazy().into_typed::<T>().ok()
211 }
212
213 pub fn read<T: 'static>(&self) -> Option<ValueReadAccess<T>> {
214 self.object.read::<T>()
215 }
216
217 pub fn write<T: 'static>(&mut self) -> Option<ValueWriteAccess<T>> {
218 self.object.write::<T>()
219 }
220
221 pub fn write_notified<T: 'static>(&mut self) -> Option<ViewModelObject<T>> {
222 if let Some(access) = self.object.write::<T>() {
223 Some(ViewModelObject {
224 access,
225 notifier: self.properties.notifier(""),
226 })
227 } else {
228 None
229 }
230 }
231}
232
233#[derive(Default)]
234pub struct ViewModelCollection {
235 named: HashMap<String, ViewModel>,
236 widgets: HashMap<WidgetId, HashMap<String, ViewModel>>,
237}
238
239impl ViewModelCollection {
240 pub fn unbind_all(&mut self, id: &WidgetId) {
241 for view_model in self.named.values_mut() {
242 view_model.properties.unbind_all(id);
243 }
244 for view_model in self.widgets.values_mut() {
245 for view_model in view_model.values_mut() {
246 view_model.properties.unbind_all(id);
247 }
248 }
249 }
250
251 pub fn remove_empty_bindings(&mut self) {
252 for view_model in self.named.values_mut() {
253 view_model.properties.remove_empty_bindings();
254 }
255 for view_model in self.widgets.values_mut() {
256 for view_model in view_model.values_mut() {
257 view_model.properties.remove_empty_bindings();
258 }
259 }
260 }
261
262 pub fn consume_notification(&mut self) -> bool {
263 let mut result = false;
264 for view_model in self.named.values_mut() {
265 result = result || view_model.properties.consume_notification();
266 }
267 for view_model in self.widgets.values_mut() {
268 for view_model in view_model.values_mut() {
269 result = result || view_model.properties.consume_notification();
270 }
271 }
272 result
273 }
274
275 pub fn consume_notified_common_root(&mut self) -> WidgetIdCommon {
276 let mut result = WidgetIdCommon::default();
277 for view_model in self.named.values_mut() {
278 result.include_other(&view_model.properties.consume_notified_common_root());
279 }
280 for view_model in self.widgets.values_mut() {
281 for view_model in view_model.values_mut() {
282 result.include_other(&view_model.properties.consume_notified_common_root());
283 }
284 }
285 result
286 }
287
288 pub fn remove_widget_view_models(&mut self, id: &WidgetId) {
289 self.widgets.remove(id);
290 }
291}
292
293impl Deref for ViewModelCollection {
294 type Target = HashMap<String, ViewModel>;
295
296 fn deref(&self) -> &Self::Target {
297 &self.named
298 }
299}
300
301impl DerefMut for ViewModelCollection {
302 fn deref_mut(&mut self) -> &mut Self::Target {
303 &mut self.named
304 }
305}
306
307pub struct ViewModelCollectionView<'a> {
308 id: &'a WidgetId,
309 collection: &'a mut ViewModelCollection,
310}
311
312impl<'a> ViewModelCollectionView<'a> {
313 pub fn new(id: &'a WidgetId, collection: &'a mut ViewModelCollection) -> Self {
314 Self { id, collection }
315 }
316
317 pub fn id(&self) -> &WidgetId {
318 self.id
319 }
320
321 pub fn collection(&'a self) -> &'a ViewModelCollection {
322 self.collection
323 }
324
325 pub fn collection_mut(&'a mut self) -> &'a mut ViewModelCollection {
326 self.collection
327 }
328
329 pub fn bindings(
330 &mut self,
331 view_model: &str,
332 property: impl ToString,
333 ) -> Option<ValueWriteAccess<ViewModelBindings>> {
334 self.collection
335 .get_mut(view_model)?
336 .properties
337 .bindings(property)
338 }
339
340 pub fn view_model(&self, name: &str) -> Option<&ViewModel> {
341 self.collection.get(name)
342 }
343
344 pub fn view_model_mut(&mut self, name: &str) -> Option<&mut ViewModel> {
345 self.collection.get_mut(name)
346 }
347
348 pub fn widget_register(&mut self, name: impl ToString, view_model: ViewModel) {
349 self.collection
350 .widgets
351 .entry(self.id.to_owned())
352 .or_default()
353 .insert(name.to_string(), view_model);
354 }
355
356 pub fn widget_unregister(&mut self, name: &str) -> Option<ViewModel> {
357 let view_models = self.collection.widgets.get_mut(self.id)?;
358 let result = view_models.remove(name)?;
359 if view_models.is_empty() {
360 self.collection.widgets.remove(self.id);
361 }
362 Some(result)
363 }
364
365 pub fn widget_bindings(
366 &mut self,
367 view_model: &str,
368 property: impl ToString,
369 ) -> Option<ValueWriteAccess<ViewModelBindings>> {
370 self.collection
371 .widgets
372 .get_mut(self.id)?
373 .get_mut(view_model)?
374 .properties
375 .bindings(property)
376 }
377
378 pub fn widget_view_model(&self, name: &str) -> Option<&ViewModel> {
379 self.collection.widgets.get(self.id)?.get(name)
380 }
381
382 pub fn widget_view_model_mut(&mut self, name: &str) -> Option<&mut ViewModel> {
383 self.collection.widgets.get_mut(self.id)?.get_mut(name)
384 }
385
386 pub fn hierarchy_view_model(&self, name: &str) -> Option<&ViewModel> {
387 self.collection
388 .widgets
389 .iter()
390 .filter_map(|(id, view_models)| {
391 id.distance_to(self.id).ok().and_then(|distance| {
392 if distance <= 0 {
393 Some((distance, view_models.get(name)?))
394 } else {
395 None
396 }
397 })
398 })
399 .min_by(|(a, _), (b, _)| a.cmp(b))
400 .map(|(_, view_model)| view_model)
401 }
402
403 pub fn hierarchy_view_model_mut(&mut self, name: &str) -> Option<&mut ViewModel> {
404 self.collection
405 .widgets
406 .iter_mut()
407 .filter_map(|(id, view_models)| {
408 id.distance_to(self.id).ok().and_then(|distance| {
409 if distance <= 0 {
410 Some((distance, view_models.get_mut(name)?))
411 } else {
412 None
413 }
414 })
415 })
416 .min_by(|(a, _), (b, _)| a.cmp(b))
417 .map(|(_, view_model)| view_model)
418 }
419}
420
421pub struct ViewModelObject<'a, T> {
422 access: ValueWriteAccess<'a, T>,
423 notifier: ViewModelNotifier,
424}
425
426impl<T> ViewModelObject<'_, T> {
427 pub fn set_unique_notify(&mut self, value: T)
428 where
429 T: PartialEq,
430 {
431 if *self.access != value {
432 *self.access = value;
433 self.notifier.notify();
434 }
435 }
436}
437
438impl<T> Deref for ViewModelObject<'_, T> {
439 type Target = T;
440
441 fn deref(&self) -> &Self::Target {
442 &self.access
443 }
444}
445
446impl<T> DerefMut for ViewModelObject<'_, T> {
447 fn deref_mut(&mut self) -> &mut Self::Target {
448 self.notifier.notify();
449 &mut self.access
450 }
451}
452
453pub struct ViewModelValue<T> {
454 value: T,
455 notifier: ViewModelNotifier,
456}
457
458impl<T> ViewModelValue<T> {
459 pub fn new(value: T, notifier: ViewModelNotifier) -> Self {
460 Self { value, notifier }
461 }
462
463 pub fn consume(self) -> T {
464 self.value
465 }
466
467 pub fn set_unique_notify(&mut self, value: T)
468 where
469 T: PartialEq,
470 {
471 if self.value != value {
472 self.value = value;
473 self.notifier.notify();
474 }
475 }
476}
477
478impl<T> Deref for ViewModelValue<T> {
479 type Target = T;
480
481 fn deref(&self) -> &Self::Target {
482 &self.value
483 }
484}
485
486impl<T> DerefMut for ViewModelValue<T> {
487 fn deref_mut(&mut self) -> &mut Self::Target {
488 self.notifier.notify();
489 &mut self.value
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496 use std::str::FromStr;
497
498 const FOO_VIEW_MODEL: &str = "foo";
499 const COUNTER_PROPERTY: &str = "counter";
500 const FLAG_PROPERTY: &str = "flag";
501
502 struct Foo {
504 counter: ViewModelValue<usize>,
506 flag: bool,
508 flag_notifier: ViewModelNotifier,
509 }
510
511 impl Foo {
512 fn toggle(&mut self) {
513 self.flag = !self.flag;
514 self.flag_notifier.notify();
515 }
516 }
517
518 #[test]
519 fn test_view_model() {
520 let a = WidgetId::from_str("a:root/a").unwrap();
521 let b = WidgetId::from_str("b:root/b").unwrap();
522 let mut collection = ViewModelCollection::default();
523
524 let view_model = ViewModel::produce(|properties| Foo {
527 counter: ViewModelValue::new(0, properties.notifier(COUNTER_PROPERTY)),
528 flag: false,
529 flag_notifier: properties.notifier(FLAG_PROPERTY),
530 });
531 let handle = view_model.lazy::<Foo>().unwrap();
534 collection.insert(FOO_VIEW_MODEL.to_owned(), view_model);
535
536 assert!(!collection.consume_notified_common_root().is_valid());
538 handle.write().unwrap().toggle();
539 assert!(!collection.consume_notified_common_root().is_valid());
540 assert!(collection
541 .get_mut(FOO_VIEW_MODEL)
542 .unwrap()
543 .properties
544 .bindings(COUNTER_PROPERTY)
545 .unwrap()
546 .is_notified());
547 assert!(collection
548 .get_mut(FOO_VIEW_MODEL)
549 .unwrap()
550 .properties
551 .bindings(FLAG_PROPERTY)
552 .unwrap()
553 .is_notified());
554
555 collection
558 .get_mut(FOO_VIEW_MODEL)
559 .unwrap()
560 .properties
561 .bindings(COUNTER_PROPERTY)
562 .unwrap()
563 .bind(a);
564 collection
565 .get_mut(FOO_VIEW_MODEL)
566 .unwrap()
567 .properties
568 .bindings(FLAG_PROPERTY)
569 .unwrap()
570 .bind(b);
571
572 assert_eq!(
574 collection.consume_notified_common_root().path(),
575 Some("root")
576 );
577
578 *handle.write().unwrap().counter += 1;
580 assert_eq!(
581 collection.consume_notified_common_root().path(),
582 Some("root/a"),
583 );
584
585 handle.write().unwrap().toggle();
587 assert_eq!(
588 collection.consume_notified_common_root().path(),
589 Some("root/b"),
590 );
591
592 *handle.write().unwrap().counter += 1;
594 handle.write().unwrap().toggle();
595 assert_eq!(
596 collection.consume_notified_common_root().path(),
597 Some("root"),
598 );
599 }
600}