vizia_core/binding/
binding_view.rs

1use hashbrown::{HashMap, HashSet};
2use std::any::TypeId;
3
4use crate::binding::{BasicStore, Store, StoreId};
5use crate::context::{CURRENT, MAPS, MAP_MANAGER};
6use crate::model::ModelOrView;
7use crate::prelude::*;
8
9/// A view with a binding which rebuilds its contents when the observed data changes.
10///
11/// This view is typically used to switch between two or more views when the bound data changes. The binding view will destroy and then recreate its
12/// contents whenever the bound data changes, so it is usually preferable to bind a view directly to the data (if supported) or to bind to a view modifier,
13/// which will update the properties of a view without rebuilding it.
14pub struct Binding<L>
15where
16    L: Lens,
17{
18    entity: Entity,
19    lens: L,
20    #[allow(clippy::type_complexity)]
21    content: Option<Box<dyn Fn(&mut Context, L)>>,
22}
23
24impl<L> Binding<L>
25where
26    L: 'static + Lens<Source: 'static, Target: Data>,
27{
28    /// Creates a new binding view.
29    ///
30    /// A binding view observes application data through a lens and rebuilds its contents if the data changes.
31    ///
32    /// # Example
33    /// When the value of `AppData::some_data` changes, the label inside of the binding will be rebuilt.
34    /// ```ignore
35    /// Binding::new(cx, AppData::some_data, |cx, lens|{
36    ///     // Retrieve the data from context
37    ///     let value = *lens.get(cx);
38    ///     Label::new(cx, value.to_string());
39    /// });
40    /// ```
41    #[allow(clippy::new_ret_no_self)]
42    pub fn new<F>(cx: &mut Context, lens: L, builder: F)
43    where
44        F: 'static + Fn(&mut Context, L),
45    {
46        let id = cx.entity_manager.create();
47        let current = cx.current();
48        cx.tree.add(id, current).expect("Failed to add to tree");
49        cx.cache.add(id);
50        cx.style.add(id);
51        cx.tree.set_ignored(id, true);
52
53        let binding = Self { entity: id, lens, content: Some(Box::new(builder)) };
54
55        CURRENT.with_borrow_mut(|f| *f = id);
56
57        let ancestors = cx.current().parent_iter(&cx.tree).collect::<HashSet<_>>();
58        let new_ancestors = id.parent_iter(&cx.tree).collect::<Vec<_>>();
59
60        fn insert_store<L>(
61            entity: Entity,
62            ancestors: &HashSet<Entity>,
63            stores: &mut HashMap<Entity, HashMap<StoreId, Box<dyn Store>>>,
64            model_data: ModelOrView,
65            lens: L,
66            id: Entity,
67        ) where
68            L: Lens<Target: Data>,
69        {
70            if !stores.contains_key(&entity) {
71                stores.insert(entity, HashMap::new());
72            }
73
74            if let Some(stores) = stores.get_mut(&entity) {
75                let key = lens.id();
76
77                if let Some(store) = stores.get_mut(&key) {
78                    let observers = store.observers();
79
80                    if ancestors.intersection(observers).next().is_none() {
81                        store.add_observer(id);
82                    }
83                } else {
84                    let mut observers = HashSet::new();
85                    observers.insert(id);
86
87                    let model = model_data.downcast_ref::<L::Source>().unwrap();
88
89                    let old = lens.view(model).map(|val| val.into_owned());
90
91                    let store = Box::new(BasicStore { lens, old, observers });
92
93                    stores.insert(key, store);
94                }
95            }
96        }
97
98        // Check if there's already a store with the same lens somewhere up the tree. If there is, add this binding as an observer,
99        // else create a new store with this binding as an observer.
100        for entity in new_ancestors {
101            // Check for view store
102            if let Some(view_handler) = cx.views.get(&entity) {
103                if view_handler.as_any_ref().is::<L::Source>() {
104                    insert_store(
105                        entity,
106                        &ancestors,
107                        &mut cx.stores,
108                        ModelOrView::View(view_handler.as_ref()),
109                        lens,
110                        id,
111                    );
112
113                    break;
114                }
115            }
116
117            if let Some(models) = cx.models.get_mut(&entity) {
118                // Check for model store
119                if let Some(model_data) = models.get(&TypeId::of::<L::Source>()) {
120                    insert_store(
121                        entity,
122                        &ancestors,
123                        &mut cx.stores,
124                        ModelOrView::Model(model_data.as_ref()),
125                        lens,
126                        id,
127                    );
128
129                    break;
130                }
131            }
132        }
133
134        cx.bindings.insert(id, Box::new(binding));
135
136        cx.with_current(id, |cx| {
137            // Call the body of the binding
138            if let Some(mut binding) = cx.bindings.remove(&id) {
139                binding.update(cx);
140                cx.bindings.insert(id, binding);
141            }
142        });
143
144        let _: Handle<Self> =
145            Handle { current: id, entity: id, p: Default::default(), cx }.ignore();
146    }
147}
148
149pub(crate) trait BindingHandler {
150    fn update(&mut self, cx: &mut Context);
151    fn remove(&self, cx: &mut Context);
152    fn debug(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result;
153}
154
155impl<L: 'static + Lens> BindingHandler for Binding<L> {
156    fn update(&mut self, cx: &mut Context) {
157        cx.remove_children(cx.current());
158
159        // Remove all maps that are associated with this binding.
160        MAP_MANAGER.with_borrow_mut(|manager| {
161            MAPS.with_borrow_mut(|maps| {
162                maps.retain(|id, (e, _)| {
163                    if *e == self.entity {
164                        manager.destroy(*id);
165                        false
166                    } else {
167                        true
168                    }
169                });
170            });
171        });
172
173        if let Some(builder) = &self.content {
174            CURRENT.with_borrow_mut(|f| *f = self.entity);
175            (builder)(cx, self.lens);
176        }
177    }
178
179    fn remove(&self, cx: &mut Context) {
180        for entity in self.entity.parent_iter(&cx.tree) {
181            let key = self.lens.id();
182
183            if let Some(stores) = cx.stores.get_mut(&entity) {
184                if let Some(store) = stores.get_mut(&key) {
185                    let source = store.source();
186                    if cx.views.get(&entity).filter(|view| view.id() == source).is_some()
187                        || cx
188                            .models
189                            .get(&entity)
190                            .filter(|models| models.contains_key(&source))
191                            .is_some()
192                    {
193                        store.remove_observer(&self.entity);
194
195                        if store.num_observers() == 0 {
196                            stores.remove(&key);
197                        }
198
199                        break;
200                    }
201                }
202            }
203        }
204    }
205
206    fn debug(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
207        self.lens.fmt(f)
208    }
209}
210
211impl std::fmt::Debug for dyn BindingHandler {
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        self.debug(f)
214    }
215}