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