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}