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