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}