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