model_context.rs

  1use crate::{
  2    AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId,
  3    EventEmitter, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle,
  4};
  5use anyhow::Result;
  6use derive_more::{Deref, DerefMut};
  7use futures::FutureExt;
  8use std::{
  9    any::{Any, TypeId},
 10    borrow::{Borrow, BorrowMut},
 11    future::Future,
 12};
 13
 14/// The app context, with specialized behavior for the given model.
 15#[derive(Deref, DerefMut)]
 16pub struct ModelContext<'a, T> {
 17    #[deref]
 18    #[deref_mut]
 19    app: &'a mut AppContext,
 20    model_state: WeakModel<T>,
 21}
 22
 23impl<'a, T: 'static> ModelContext<'a, T> {
 24    pub(crate) fn new(app: &'a mut AppContext, model_state: WeakModel<T>) -> Self {
 25        Self { app, model_state }
 26    }
 27
 28    /// The entity id of the model backing this context.
 29    pub fn entity_id(&self) -> EntityId {
 30        self.model_state.entity_id
 31    }
 32
 33    /// Returns a handle to the model belonging to this context.
 34    pub fn handle(&self) -> Model<T> {
 35        self.weak_model()
 36            .upgrade()
 37            .expect("The entity must be alive if we have a model context")
 38    }
 39
 40    /// Returns a weak handle to the model belonging to this context.
 41    pub fn weak_model(&self) -> WeakModel<T> {
 42        self.model_state.clone()
 43    }
 44
 45    /// Arranges for the given function to be called whenever [`ModelContext::notify`] or
 46    /// [`ViewContext::notify`](crate::ViewContext::notify) is called with the given model or view.
 47    pub fn observe<W, E>(
 48        &mut self,
 49        entity: &E,
 50        mut on_notify: impl FnMut(&mut T, E, &mut ModelContext<'_, T>) + 'static,
 51    ) -> Subscription
 52    where
 53        T: 'static,
 54        W: 'static,
 55        E: Entity<W>,
 56    {
 57        let this = self.weak_model();
 58        self.app.observe_internal(entity, move |e, cx| {
 59            if let Some(this) = this.upgrade() {
 60                this.update(cx, |this, cx| on_notify(this, e, cx));
 61                true
 62            } else {
 63                false
 64            }
 65        })
 66    }
 67
 68    /// Subscribe to an event type from another model or view
 69    pub fn subscribe<T2, E, Evt>(
 70        &mut self,
 71        entity: &E,
 72        mut on_event: impl FnMut(&mut T, E, &Evt, &mut ModelContext<'_, T>) + 'static,
 73    ) -> Subscription
 74    where
 75        T: 'static,
 76        T2: 'static + EventEmitter<Evt>,
 77        E: Entity<T2>,
 78        Evt: 'static,
 79    {
 80        let this = self.weak_model();
 81        self.app.subscribe_internal(entity, move |e, event, cx| {
 82            if let Some(this) = this.upgrade() {
 83                this.update(cx, |this, cx| on_event(this, e, event, cx));
 84                true
 85            } else {
 86                false
 87            }
 88        })
 89    }
 90
 91    /// Register a callback to be invoked when GPUI releases this model.
 92    pub fn on_release(
 93        &mut self,
 94        on_release: impl FnOnce(&mut T, &mut AppContext) + 'static,
 95    ) -> Subscription
 96    where
 97        T: 'static,
 98    {
 99        let (subscription, activate) = 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        activate();
107        subscription
108    }
109
110    /// Register a callback to be run on the release of another model or view
111    pub fn observe_release<T2, E>(
112        &mut self,
113        entity: &E,
114        on_release: impl FnOnce(&mut T, &mut T2, &mut ModelContext<'_, T>) + 'static,
115    ) -> Subscription
116    where
117        T: Any,
118        T2: 'static,
119        E: Entity<T2>,
120    {
121        let entity_id = entity.entity_id();
122        let this = self.weak_model();
123        let (subscription, activate) = self.app.release_listeners.insert(
124            entity_id,
125            Box::new(move |entity, cx| {
126                let entity = entity.downcast_mut().expect("invalid entity type");
127                if let Some(this) = this.upgrade() {
128                    this.update(cx, |this, cx| on_release(this, entity, cx));
129                }
130            }),
131        );
132        activate();
133        subscription
134    }
135
136    /// Register a callback to for updates to the given global
137    pub fn observe_global<G: 'static>(
138        &mut self,
139        mut f: impl FnMut(&mut T, &mut ModelContext<'_, T>) + 'static,
140    ) -> Subscription
141    where
142        T: 'static,
143    {
144        let handle = self.weak_model();
145        let (subscription, activate) = self.global_observers.insert(
146            TypeId::of::<G>(),
147            Box::new(move |cx| handle.update(cx, |view, cx| f(view, cx)).is_ok()),
148        );
149        self.defer(move |_| activate());
150        subscription
151    }
152
153    /// Arrange for the given function to be invoked whenever the application is quit.
154    /// The future returned from this callback will be polled for up to [crate::SHUTDOWN_TIMEOUT] until the app fully quits.
155    pub fn on_app_quit<Fut>(
156        &mut self,
157        mut on_quit: impl FnMut(&mut T, &mut ModelContext<T>) -> Fut + 'static,
158    ) -> Subscription
159    where
160        Fut: 'static + Future<Output = ()>,
161        T: 'static,
162    {
163        let handle = self.weak_model();
164        let (subscription, activate) = self.app.quit_observers.insert(
165            (),
166            Box::new(move |cx| {
167                let future = handle.update(cx, |entity, cx| on_quit(entity, cx)).ok();
168                async move {
169                    if let Some(future) = future {
170                        future.await;
171                    }
172                }
173                .boxed_local()
174            }),
175        );
176        activate();
177        subscription
178    }
179
180    /// Tell GPUI that this model has changed and observers of it should be notified.
181    pub fn notify(&mut self) {
182        if self
183            .app
184            .pending_notifications
185            .insert(self.model_state.entity_id)
186        {
187            self.app.pending_effects.push_back(Effect::Notify {
188                emitter: self.model_state.entity_id,
189            });
190        }
191    }
192
193    /// Updates the given global
194    pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
195    where
196        G: 'static,
197    {
198        let mut global = self.app.lease_global::<G>();
199        let result = f(&mut global, self);
200        self.app.end_global_lease(global);
201        result
202    }
203
204    /// Spawn the future returned by the given function.
205    /// The function is provided a weak handle to the model owned by this context and a context that can be held across await points.
206    /// The returned task must be held or detached.
207    pub fn spawn<Fut, R>(&self, f: impl FnOnce(WeakModel<T>, AsyncAppContext) -> Fut) -> Task<R>
208    where
209        T: 'static,
210        Fut: Future<Output = R> + 'static,
211        R: 'static,
212    {
213        let this = self.weak_model();
214        self.app.spawn(|cx| f(this, cx))
215    }
216}
217
218impl<'a, T> ModelContext<'a, T> {
219    /// Emit an event of the specified type, which can be handled by other entities that have subscribed via `subscribe` methods on their respective contexts.
220    pub fn emit<Evt>(&mut self, event: Evt)
221    where
222        T: EventEmitter<Evt>,
223        Evt: 'static,
224    {
225        self.app.pending_effects.push_back(Effect::Emit {
226            emitter: self.model_state.entity_id,
227            event_type: TypeId::of::<Evt>(),
228            event: Box::new(event),
229        });
230    }
231}
232
233impl<'a, T> Context for ModelContext<'a, T> {
234    type Result<U> = U;
235
236    fn new_model<U: 'static>(
237        &mut self,
238        build_model: impl FnOnce(&mut ModelContext<'_, U>) -> U,
239    ) -> Model<U> {
240        self.app.new_model(build_model)
241    }
242
243    fn update_model<U: 'static, R>(
244        &mut self,
245        handle: &Model<U>,
246        update: impl FnOnce(&mut U, &mut ModelContext<'_, U>) -> R,
247    ) -> R {
248        self.app.update_model(handle, update)
249    }
250
251    fn update_window<R, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<R>
252    where
253        F: FnOnce(AnyView, &mut WindowContext<'_>) -> R,
254    {
255        self.app.update_window(window, update)
256    }
257
258    fn read_model<U, R>(
259        &self,
260        handle: &Model<U>,
261        read: impl FnOnce(&U, &AppContext) -> R,
262    ) -> Self::Result<R>
263    where
264        U: 'static,
265    {
266        self.app.read_model(handle, read)
267    }
268
269    fn read_window<U, R>(
270        &self,
271        window: &WindowHandle<U>,
272        read: impl FnOnce(View<U>, &AppContext) -> R,
273    ) -> Result<R>
274    where
275        U: 'static,
276    {
277        self.app.read_window(window, read)
278    }
279}
280
281impl<T> Borrow<AppContext> for ModelContext<'_, T> {
282    fn borrow(&self) -> &AppContext {
283        self.app
284    }
285}
286
287impl<T> BorrowMut<AppContext> for ModelContext<'_, T> {
288    fn borrow_mut(&mut self) -> &mut AppContext {
289        self.app
290    }
291}