model_context.rs

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