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 && bindings.is_empty()
114 {
115 return Some(key.to_owned());
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(
126 &'_ mut self,
127 id: impl ToString,
128 ) -> Option<ValueWriteAccess<'_, ViewModelBindings>> {
129 self.inner.entry(id.to_string()).or_default().write()
130 }
131
132 pub fn notifier(&mut self, id: impl ToString) -> ViewModelNotifier {
133 ViewModelNotifier {
134 inner: self.inner.entry(id.to_string()).or_default().lazy(),
135 }
136 }
137
138 pub fn consume_notification(&mut self) -> bool {
139 self.inner.values_mut().any(|bindings| {
140 bindings
141 .write()
142 .map(|mut bindings| bindings.consume_notification())
143 .unwrap_or_default()
144 })
145 }
146
147 pub fn consume_notified_common_root(&mut self) -> WidgetIdCommon {
148 let mut result = WidgetIdCommon::default();
149 for bindings in self.inner.values_mut() {
150 if let Some(mut bindings) = bindings.write()
151 && bindings.consume_notification()
152 {
153 let root = bindings.common_root();
154 result.include_other(root);
155 }
156 }
157 result
158 }
159}
160
161#[derive(Clone)]
162pub struct ViewModelNotifier {
163 inner: ManagedLazy<ViewModelBindings>,
164}
165
166impl ViewModelNotifier {
167 pub fn notify(&mut self) -> bool {
168 if let Some(mut bindings) = self.inner.write() {
169 bindings.notify();
170 true
171 } else {
172 false
173 }
174 }
175}
176
177pub struct ViewModel {
178 object: DynamicManaged,
179 pub properties: ViewModelProperties,
180}
181
182impl ViewModel {
183 pub fn new<T: 'static>(object: T, properties: ViewModelProperties) -> Self {
184 Self {
185 object: DynamicManaged::new(object).ok().unwrap(),
186 properties,
187 }
188 }
189
190 pub fn new_object<T: 'static>(object: T) -> Self {
191 Self::new(object, Default::default())
192 }
193
194 pub fn produce<T: 'static>(producer: impl FnOnce(&mut ViewModelProperties) -> T) -> Self {
195 let mut properties = Default::default();
196 let object = DynamicManaged::new(producer(&mut properties)).ok().unwrap();
197 Self { object, properties }
198 }
199
200 pub fn borrow<T: 'static>(&self) -> Option<ManagedRef<T>> {
201 self.object
202 .borrow()
203 .and_then(|object| object.into_typed::<T>().ok())
204 }
205
206 pub fn borrow_mut<T: 'static>(&mut self) -> Option<ManagedRefMut<T>> {
207 self.object
208 .borrow_mut()
209 .and_then(|object| object.into_typed::<T>().ok())
210 }
211
212 pub fn lazy<T: 'static>(&self) -> Option<ManagedLazy<T>> {
213 self.object.lazy().into_typed::<T>().ok()
214 }
215
216 pub fn read<T: 'static>(&'_ self) -> Option<ValueReadAccess<'_, T>> {
217 self.object.read::<T>()
218 }
219
220 pub fn write<T: 'static>(&'_ mut self) -> Option<ValueWriteAccess<'_, T>> {
221 self.object.write::<T>()
222 }
223
224 pub fn write_notified<T: 'static>(&'_ mut self) -> Option<ViewModelObject<'_, T>> {
225 if let Some(access) = self.object.write::<T>() {
226 Some(ViewModelObject {
227 access,
228 notifier: self.properties.notifier(""),
229 })
230 } else {
231 None
232 }
233 }
234}
235
236#[derive(Default)]
237pub struct ViewModelCollection {
238 named: HashMap<String, ViewModel>,
239 widgets: HashMap<WidgetId, HashMap<String, ViewModel>>,
240}
241
242impl ViewModelCollection {
243 pub fn unbind_all(&mut self, id: &WidgetId) {
244 for view_model in self.named.values_mut() {
245 view_model.properties.unbind_all(id);
246 }
247 for view_model in self.widgets.values_mut() {
248 for view_model in view_model.values_mut() {
249 view_model.properties.unbind_all(id);
250 }
251 }
252 }
253
254 pub fn remove_empty_bindings(&mut self) {
255 for view_model in self.named.values_mut() {
256 view_model.properties.remove_empty_bindings();
257 }
258 for view_model in self.widgets.values_mut() {
259 for view_model in view_model.values_mut() {
260 view_model.properties.remove_empty_bindings();
261 }
262 }
263 }
264
265 pub fn consume_notification(&mut self) -> bool {
266 let mut result = false;
267 for view_model in self.named.values_mut() {
268 result = result || view_model.properties.consume_notification();
269 }
270 for view_model in self.widgets.values_mut() {
271 for view_model in view_model.values_mut() {
272 result = result || view_model.properties.consume_notification();
273 }
274 }
275 result
276 }
277
278 pub fn consume_notified_common_root(&mut self) -> WidgetIdCommon {
279 let mut result = WidgetIdCommon::default();
280 for view_model in self.named.values_mut() {
281 result.include_other(&view_model.properties.consume_notified_common_root());
282 }
283 for view_model in self.widgets.values_mut() {
284 for view_model in view_model.values_mut() {
285 result.include_other(&view_model.properties.consume_notified_common_root());
286 }
287 }
288 result
289 }
290
291 pub fn remove_widget_view_models(&mut self, id: &WidgetId) {
292 self.widgets.remove(id);
293 }
294}
295
296impl Deref for ViewModelCollection {
297 type Target = HashMap<String, ViewModel>;
298
299 fn deref(&self) -> &Self::Target {
300 &self.named
301 }
302}
303
304impl DerefMut for ViewModelCollection {
305 fn deref_mut(&mut self) -> &mut Self::Target {
306 &mut self.named
307 }
308}
309
310pub struct ViewModelCollectionView<'a> {
311 id: &'a WidgetId,
312 collection: &'a mut ViewModelCollection,
313}
314
315impl<'a> ViewModelCollectionView<'a> {
316 pub fn new(id: &'a WidgetId, collection: &'a mut ViewModelCollection) -> Self {
317 Self { id, collection }
318 }
319
320 pub fn id(&self) -> &WidgetId {
321 self.id
322 }
323
324 pub fn collection(&'a self) -> &'a ViewModelCollection {
325 self.collection
326 }
327
328 pub fn collection_mut(&'a mut self) -> &'a mut ViewModelCollection {
329 self.collection
330 }
331
332 pub fn bindings(
333 &'_ mut self,
334 view_model: &str,
335 property: impl ToString,
336 ) -> Option<ValueWriteAccess<'_, ViewModelBindings>> {
337 self.collection
338 .get_mut(view_model)?
339 .properties
340 .bindings(property)
341 }
342
343 pub fn view_model(&self, name: &str) -> Option<&ViewModel> {
344 self.collection.get(name)
345 }
346
347 pub fn view_model_mut(&mut self, name: &str) -> Option<&mut ViewModel> {
348 self.collection.get_mut(name)
349 }
350
351 pub fn widget_register(&mut self, name: impl ToString, view_model: ViewModel) {
352 self.collection
353 .widgets
354 .entry(self.id.to_owned())
355 .or_default()
356 .insert(name.to_string(), view_model);
357 }
358
359 pub fn widget_unregister(&mut self, name: &str) -> Option<ViewModel> {
360 let view_models = self.collection.widgets.get_mut(self.id)?;
361 let result = view_models.remove(name)?;
362 if view_models.is_empty() {
363 self.collection.widgets.remove(self.id);
364 }
365 Some(result)
366 }
367
368 pub fn widget_bindings(
369 &'_ mut self,
370 view_model: &str,
371 property: impl ToString,
372 ) -> Option<ValueWriteAccess<'_, ViewModelBindings>> {
373 self.collection
374 .widgets
375 .get_mut(self.id)?
376 .get_mut(view_model)?
377 .properties
378 .bindings(property)
379 }
380
381 pub fn widget_view_model(&self, name: &str) -> Option<&ViewModel> {
382 self.collection.widgets.get(self.id)?.get(name)
383 }
384
385 pub fn widget_view_model_mut(&mut self, name: &str) -> Option<&mut ViewModel> {
386 self.collection.widgets.get_mut(self.id)?.get_mut(name)
387 }
388
389 pub fn hierarchy_view_model(&self, name: &str) -> Option<&ViewModel> {
390 self.collection
391 .widgets
392 .iter()
393 .filter_map(|(id, view_models)| {
394 id.distance_to(self.id).ok().and_then(|distance| {
395 if distance <= 0 {
396 Some((distance, view_models.get(name)?))
397 } else {
398 None
399 }
400 })
401 })
402 .min_by(|(a, _), (b, _)| a.cmp(b))
403 .map(|(_, view_model)| view_model)
404 }
405
406 pub fn hierarchy_view_model_mut(&mut self, name: &str) -> Option<&mut ViewModel> {
407 self.collection
408 .widgets
409 .iter_mut()
410 .filter_map(|(id, view_models)| {
411 id.distance_to(self.id).ok().and_then(|distance| {
412 if distance <= 0 {
413 Some((distance, view_models.get_mut(name)?))
414 } else {
415 None
416 }
417 })
418 })
419 .min_by(|(a, _), (b, _)| a.cmp(b))
420 .map(|(_, view_model)| view_model)
421 }
422}
423
424pub struct ViewModelObject<'a, T> {
425 access: ValueWriteAccess<'a, T>,
426 notifier: ViewModelNotifier,
427}
428
429impl<T> ViewModelObject<'_, T> {
430 pub fn set_unique_notify(&mut self, value: T)
431 where
432 T: PartialEq,
433 {
434 if *self.access != value {
435 *self.access = value;
436 self.notifier.notify();
437 }
438 }
439}
440
441impl<T> Deref for ViewModelObject<'_, T> {
442 type Target = T;
443
444 fn deref(&self) -> &Self::Target {
445 &self.access
446 }
447}
448
449impl<T> DerefMut for ViewModelObject<'_, T> {
450 fn deref_mut(&mut self) -> &mut Self::Target {
451 self.notifier.notify();
452 &mut self.access
453 }
454}
455
456pub struct ViewModelValue<T> {
457 value: T,
458 notifier: ViewModelNotifier,
459}
460
461impl<T> ViewModelValue<T> {
462 pub fn new(value: T, notifier: ViewModelNotifier) -> Self {
463 Self { value, notifier }
464 }
465
466 pub fn consume(self) -> T {
467 self.value
468 }
469
470 pub fn set_unique_notify(&mut self, value: T)
471 where
472 T: PartialEq,
473 {
474 if self.value != value {
475 self.value = value;
476 self.notifier.notify();
477 }
478 }
479}
480
481impl<T> Deref for ViewModelValue<T> {
482 type Target = T;
483
484 fn deref(&self) -> &Self::Target {
485 &self.value
486 }
487}
488
489impl<T> DerefMut for ViewModelValue<T> {
490 fn deref_mut(&mut self) -> &mut Self::Target {
491 self.notifier.notify();
492 &mut self.value
493 }
494}
495
496impl<T> std::fmt::Display for ViewModelValue<T>
497where
498 T: std::fmt::Display,
499{
500 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501 write!(f, "{}", self.value)
502 }
503}
504
505impl<T> std::fmt::Debug for ViewModelValue<T>
506where
507 T: std::fmt::Debug,
508{
509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510 f.debug_struct("ViewModelValue")
511 .field("value", &self.value)
512 .finish()
513 }
514}
515
516#[cfg(test)]
517mod tests {
518 use super::*;
519 use std::str::FromStr;
520
521 const FOO_VIEW_MODEL: &str = "foo";
522 const COUNTER_PROPERTY: &str = "counter";
523 const FLAG_PROPERTY: &str = "flag";
524
525 struct Foo {
527 counter: ViewModelValue<usize>,
529 flag: bool,
531 flag_notifier: ViewModelNotifier,
532 }
533
534 impl Foo {
535 fn toggle(&mut self) {
536 self.flag = !self.flag;
537 self.flag_notifier.notify();
538 }
539 }
540
541 #[test]
542 fn test_view_model() {
543 let a = WidgetId::from_str("a:root/a").unwrap();
544 let b = WidgetId::from_str("b:root/b").unwrap();
545 let mut collection = ViewModelCollection::default();
546
547 let view_model = ViewModel::produce(|properties| Foo {
550 counter: ViewModelValue::new(0, properties.notifier(COUNTER_PROPERTY)),
551 flag: false,
552 flag_notifier: properties.notifier(FLAG_PROPERTY),
553 });
554 let handle = view_model.lazy::<Foo>().unwrap();
557 collection.insert(FOO_VIEW_MODEL.to_owned(), view_model);
558
559 assert!(!collection.consume_notified_common_root().is_valid());
561 handle.write().unwrap().toggle();
562 assert!(!collection.consume_notified_common_root().is_valid());
563 assert!(
564 collection
565 .get_mut(FOO_VIEW_MODEL)
566 .unwrap()
567 .properties
568 .bindings(COUNTER_PROPERTY)
569 .unwrap()
570 .is_notified()
571 );
572 assert!(
573 collection
574 .get_mut(FOO_VIEW_MODEL)
575 .unwrap()
576 .properties
577 .bindings(FLAG_PROPERTY)
578 .unwrap()
579 .is_notified()
580 );
581
582 collection
585 .get_mut(FOO_VIEW_MODEL)
586 .unwrap()
587 .properties
588 .bindings(COUNTER_PROPERTY)
589 .unwrap()
590 .bind(a);
591 collection
592 .get_mut(FOO_VIEW_MODEL)
593 .unwrap()
594 .properties
595 .bindings(FLAG_PROPERTY)
596 .unwrap()
597 .bind(b);
598
599 assert_eq!(
601 collection.consume_notified_common_root().path(),
602 Some("root")
603 );
604
605 *handle.write().unwrap().counter += 1;
607 assert_eq!(
608 collection.consume_notified_common_root().path(),
609 Some("root/a"),
610 );
611
612 handle.write().unwrap().toggle();
614 assert_eq!(
615 collection.consume_notified_common_root().path(),
616 Some("root/b"),
617 );
618
619 *handle.write().unwrap().counter += 1;
621 handle.write().unwrap().toggle();
622 assert_eq!(
623 collection.consume_notified_common_root().path(),
624 Some("root"),
625 );
626 }
627}