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}