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