model_context.rs

  1use crate::{
  2    AppContext, AsyncAppContext, Context, Effect, EntityId, EventEmitter, Handle, Reference,
  3    Subscription, Task, WeakHandle,
  4};
  5use derive_more::{Deref, DerefMut};
  6use futures::FutureExt;
  7use std::{
  8    any::{Any, TypeId},
  9    future::Future,
 10};
 11
 12#[derive(Deref, DerefMut)]
 13pub struct ModelContext<'a, T> {
 14    #[deref]
 15    #[deref_mut]
 16    app: Reference<'a, AppContext>,
 17    model_state: WeakHandle<T>,
 18}
 19
 20impl<'a, T: 'static> ModelContext<'a, T> {
 21    pub(crate) fn mutable(app: &'a mut AppContext, model_state: WeakHandle<T>) -> Self {
 22        Self {
 23            app: Reference::Mutable(app),
 24            model_state,
 25        }
 26    }
 27
 28    pub fn entity_id(&self) -> EntityId {
 29        self.model_state.entity_id
 30    }
 31
 32    pub fn handle(&self) -> Handle<T> {
 33        self.weak_handle()
 34            .upgrade()
 35            .expect("The entity must be alive if we have a model context")
 36    }
 37
 38    pub fn weak_handle(&self) -> WeakHandle<T> {
 39        self.model_state.clone()
 40    }
 41
 42    pub fn observe<T2: 'static>(
 43        &mut self,
 44        handle: &Handle<T2>,
 45        on_notify: impl Fn(&mut T, Handle<T2>, &mut ModelContext<'_, T>) + Send + Sync + 'static,
 46    ) -> Subscription
 47    where
 48        T: Any + Send + Sync,
 49    {
 50        let this = self.weak_handle();
 51        let handle = handle.downgrade();
 52        self.app.observers.insert(
 53            handle.entity_id,
 54            Box::new(move |cx| {
 55                if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
 56                    this.update(cx, |this, cx| on_notify(this, handle, cx));
 57                    true
 58                } else {
 59                    false
 60                }
 61            }),
 62        )
 63    }
 64
 65    pub fn subscribe<E: 'static + EventEmitter>(
 66        &mut self,
 67        handle: &Handle<E>,
 68        on_event: impl Fn(&mut T, Handle<E>, &E::Event, &mut ModelContext<'_, T>)
 69            + Send
 70            + Sync
 71            + 'static,
 72    ) -> Subscription
 73    where
 74        T: Any + Send + Sync,
 75    {
 76        let this = self.weak_handle();
 77        let handle = handle.downgrade();
 78        self.app.event_listeners.insert(
 79            handle.entity_id,
 80            Box::new(move |event, cx| {
 81                let event = event.downcast_ref().expect("invalid event type");
 82                if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
 83                    this.update(cx, |this, cx| on_event(this, handle, event, cx));
 84                    true
 85                } else {
 86                    false
 87                }
 88            }),
 89        )
 90    }
 91
 92    pub fn on_release(
 93        &mut self,
 94        on_release: impl Fn(&mut T, &mut AppContext) + Send + Sync + 'static,
 95    ) -> Subscription
 96    where
 97        T: 'static,
 98    {
 99        self.app.release_listeners.insert(
100            self.model_state.entity_id,
101            Box::new(move |this, cx| {
102                let this = this.downcast_mut().expect("invalid entity type");
103                on_release(this, cx);
104            }),
105        )
106    }
107
108    pub fn observe_release<E: 'static>(
109        &mut self,
110        handle: &Handle<E>,
111        on_release: impl Fn(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + Sync + 'static,
112    ) -> Subscription
113    where
114        T: Any + Send + Sync,
115    {
116        let this = self.weak_handle();
117        self.app.release_listeners.insert(
118            handle.entity_id,
119            Box::new(move |entity, cx| {
120                let entity = entity.downcast_mut().expect("invalid entity type");
121                if let Some(this) = this.upgrade() {
122                    this.update(cx, |this, cx| on_release(this, entity, cx));
123                }
124            }),
125        )
126    }
127
128    pub fn observe_global<G: 'static>(
129        &mut self,
130        f: impl Fn(&mut T, &mut ModelContext<'_, T>) + Send + Sync + 'static,
131    ) -> Subscription
132    where
133        T: Any + Send + Sync,
134    {
135        let handle = self.weak_handle();
136        self.global_observers.insert(
137            TypeId::of::<G>(),
138            Box::new(move |cx| handle.update(cx, |view, cx| f(view, cx)).is_ok()),
139        )
140    }
141
142    pub fn on_app_quit<Fut>(
143        &mut self,
144        on_quit: impl Fn(&mut T, &mut ModelContext<T>) -> Fut + Send + Sync + 'static,
145    ) -> Subscription
146    where
147        Fut: 'static + Future<Output = ()> + Send,
148        T: Any + Send + Sync,
149    {
150        let handle = self.weak_handle();
151        self.app.quit_observers.insert(
152            (),
153            Box::new(move |cx| {
154                let future = handle.update(cx, |entity, cx| on_quit(entity, cx)).ok();
155                async move {
156                    if let Some(future) = future {
157                        future.await;
158                    }
159                }
160                .boxed()
161            }),
162        )
163    }
164
165    pub fn notify(&mut self) {
166        if self
167            .app
168            .pending_notifications
169            .insert(self.model_state.entity_id)
170        {
171            self.app.pending_effects.push_back(Effect::Notify {
172                emitter: self.model_state.entity_id,
173            });
174        }
175    }
176
177    pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
178    where
179        G: 'static + Send + Sync,
180    {
181        let mut global = self.app.lease_global::<G>();
182        let result = f(&mut global, self);
183        self.app.end_global_lease(global);
184        result
185    }
186
187    pub fn spawn<Fut, R>(
188        &self,
189        f: impl FnOnce(WeakHandle<T>, AsyncAppContext) -> Fut + Send + 'static,
190    ) -> Task<R>
191    where
192        T: 'static,
193        Fut: Future<Output = R> + Send + 'static,
194        R: Send + 'static,
195    {
196        let this = self.weak_handle();
197        self.app.spawn(|cx| f(this, cx))
198    }
199}
200
201impl<'a, T> ModelContext<'a, T>
202where
203    T: EventEmitter,
204    T::Event: Send + Sync,
205{
206    pub fn emit(&mut self, event: T::Event) {
207        self.app.pending_effects.push_back(Effect::Emit {
208            emitter: self.model_state.entity_id,
209            event: Box::new(event),
210        });
211    }
212}
213
214impl<'a, T> Context for ModelContext<'a, T> {
215    type EntityContext<'b, 'c, U> = ModelContext<'b, U>;
216    type Result<U> = U;
217
218    fn entity<U>(
219        &mut self,
220        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
221    ) -> Handle<U>
222    where
223        U: 'static + Send + Sync,
224    {
225        self.app.entity(build_entity)
226    }
227
228    fn update_entity<U: 'static, R>(
229        &mut self,
230        handle: &Handle<U>,
231        update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
232    ) -> R {
233        self.app.update_entity(handle, update)
234    }
235}