1use crate::{
2 AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, Context,
3 DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, PromptLevel,
4 Render, Reservation, Result, Task, View, ViewContext, VisualContext, WindowContext,
5 WindowHandle,
6};
7use anyhow::{anyhow, Context as _};
8use derive_more::{Deref, DerefMut};
9use futures::channel::oneshot;
10use std::{future::Future, rc::Weak};
11
12/// An async-friendly version of [AppContext] with a static lifetime so it can be held across `await` points in async code.
13/// You're provided with an instance when calling [AppContext::spawn], and you can also create one with [AppContext::to_async].
14/// Internally, this holds a weak reference to an `AppContext`, so its methods are fallible to protect against cases where the [AppContext] is dropped.
15#[derive(Clone)]
16pub struct AsyncAppContext {
17 pub(crate) app: Weak<AppCell>,
18 pub(crate) background_executor: BackgroundExecutor,
19 pub(crate) foreground_executor: ForegroundExecutor,
20}
21
22impl Context for AsyncAppContext {
23 type Result<T> = Result<T>;
24
25 fn new_model<T: 'static>(
26 &mut self,
27 build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
28 ) -> Self::Result<Model<T>> {
29 let app = self
30 .app
31 .upgrade()
32 .ok_or_else(|| anyhow!("app was released"))?;
33 let mut app = app.borrow_mut();
34 Ok(app.new_model(build_model))
35 }
36
37 fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {
38 let app = self
39 .app
40 .upgrade()
41 .ok_or_else(|| anyhow!("app was released"))?;
42 let mut app = app.borrow_mut();
43 Ok(app.reserve_model())
44 }
45
46 fn insert_model<T: 'static>(
47 &mut self,
48 reservation: Reservation<T>,
49 build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
50 ) -> Result<Model<T>> {
51 let app = self
52 .app
53 .upgrade()
54 .ok_or_else(|| anyhow!("app was released"))?;
55 let mut app = app.borrow_mut();
56 Ok(app.insert_model(reservation, build_model))
57 }
58
59 fn update_model<T: 'static, R>(
60 &mut self,
61 handle: &Model<T>,
62 update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
63 ) -> Self::Result<R> {
64 let app = self
65 .app
66 .upgrade()
67 .ok_or_else(|| anyhow!("app was released"))?;
68 let mut app = app.borrow_mut();
69 Ok(app.update_model(handle, update))
70 }
71
72 fn read_model<T, R>(
73 &self,
74 handle: &Model<T>,
75 callback: impl FnOnce(&T, &AppContext) -> R,
76 ) -> Self::Result<R>
77 where
78 T: 'static,
79 {
80 let app = self.app.upgrade().context("app was released")?;
81 let lock = app.borrow();
82 Ok(lock.read_model(handle, callback))
83 }
84
85 fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
86 where
87 F: FnOnce(AnyView, &mut WindowContext) -> T,
88 {
89 let app = self.app.upgrade().context("app was released")?;
90 let mut lock = app.borrow_mut();
91 lock.update_window(window, f)
92 }
93
94 fn read_window<T, R>(
95 &self,
96 window: &WindowHandle<T>,
97 read: impl FnOnce(View<T>, &AppContext) -> R,
98 ) -> Result<R>
99 where
100 T: 'static,
101 {
102 let app = self.app.upgrade().context("app was released")?;
103 let lock = app.borrow();
104 lock.read_window(window, read)
105 }
106}
107
108impl AsyncAppContext {
109 /// Schedules all windows in the application to be redrawn.
110 pub fn refresh(&self) -> Result<()> {
111 let app = self
112 .app
113 .upgrade()
114 .ok_or_else(|| anyhow!("app was released"))?;
115 let mut lock = app.borrow_mut();
116 lock.refresh();
117 Ok(())
118 }
119
120 /// Get an executor which can be used to spawn futures in the background.
121 pub fn background_executor(&self) -> &BackgroundExecutor {
122 &self.background_executor
123 }
124
125 /// Get an executor which can be used to spawn futures in the foreground.
126 pub fn foreground_executor(&self) -> &ForegroundExecutor {
127 &self.foreground_executor
128 }
129
130 /// Invoke the given function in the context of the app, then flush any effects produced during its invocation.
131 pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {
132 let app = self
133 .app
134 .upgrade()
135 .ok_or_else(|| anyhow!("app was released"))?;
136 let mut lock = app.borrow_mut();
137 Ok(f(&mut lock))
138 }
139
140 /// Open a window with the given options based on the root view returned by the given function.
141 pub fn open_window<V>(
142 &self,
143 options: crate::WindowOptions,
144 build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
145 ) -> Result<WindowHandle<V>>
146 where
147 V: 'static + Render,
148 {
149 let app = self
150 .app
151 .upgrade()
152 .ok_or_else(|| anyhow!("app was released"))?;
153 let mut lock = app.borrow_mut();
154 lock.open_window(options, build_root_view)
155 }
156
157 /// Schedule a future to be polled in the background.
158 pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
159 where
160 Fut: Future<Output = R> + 'static,
161 R: 'static,
162 {
163 self.foreground_executor.spawn(f(self.clone()))
164 }
165
166 /// Determine whether global state of the specified type has been assigned.
167 /// Returns an error if the `AppContext` has been dropped.
168 pub fn has_global<G: Global>(&self) -> Result<bool> {
169 let app = self
170 .app
171 .upgrade()
172 .ok_or_else(|| anyhow!("app was released"))?;
173 let app = app.borrow_mut();
174 Ok(app.has_global::<G>())
175 }
176
177 /// Reads the global state of the specified type, passing it to the given callback.
178 ///
179 /// Panics if no global state of the specified type has been assigned.
180 /// Returns an error if the `AppContext` has been dropped.
181 pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
182 let app = self
183 .app
184 .upgrade()
185 .ok_or_else(|| anyhow!("app was released"))?;
186 let app = app.borrow_mut();
187 Ok(read(app.global(), &app))
188 }
189
190 /// Reads the global state of the specified type, passing it to the given callback.
191 ///
192 /// Similar to [`AsyncAppContext::read_global`], but returns an error instead of panicking
193 /// if no state of the specified type has been assigned.
194 ///
195 /// Returns an error if no state of the specified type has been assigned the `AppContext` has been dropped.
196 pub fn try_read_global<G: Global, R>(
197 &self,
198 read: impl FnOnce(&G, &AppContext) -> R,
199 ) -> Option<R> {
200 let app = self.app.upgrade()?;
201 let app = app.borrow_mut();
202 Some(read(app.try_global()?, &app))
203 }
204
205 /// A convenience method for [AppContext::update_global]
206 /// for updating the global state of the specified type.
207 pub fn update_global<G: Global, R>(
208 &self,
209 update: impl FnOnce(&mut G, &mut AppContext) -> R,
210 ) -> Result<R> {
211 let app = self
212 .app
213 .upgrade()
214 .ok_or_else(|| anyhow!("app was released"))?;
215 let mut app = app.borrow_mut();
216 Ok(app.update(|cx| cx.update_global(update)))
217 }
218}
219
220/// A cloneable, owned handle to the application context,
221/// composed with the window associated with the current task.
222#[derive(Clone, Deref, DerefMut)]
223pub struct AsyncWindowContext {
224 #[deref]
225 #[deref_mut]
226 app: AsyncAppContext,
227 window: AnyWindowHandle,
228}
229
230impl AsyncWindowContext {
231 pub(crate) fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self {
232 Self { app, window }
233 }
234
235 /// Get the handle of the window this context is associated with.
236 pub fn window_handle(&self) -> AnyWindowHandle {
237 self.window
238 }
239
240 /// A convenience method for [`AppContext::update_window`].
241 pub fn update<R>(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result<R> {
242 self.app.update_window(self.window, |_, cx| update(cx))
243 }
244
245 /// A convenience method for [`AppContext::update_window`].
246 pub fn update_root<R>(
247 &mut self,
248 update: impl FnOnce(AnyView, &mut WindowContext) -> R,
249 ) -> Result<R> {
250 self.app.update_window(self.window, update)
251 }
252
253 /// A convenience method for [`WindowContext::on_next_frame`].
254 pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
255 self.window.update(self, |_, cx| cx.on_next_frame(f)).ok();
256 }
257
258 /// A convenience method for [`AppContext::global`].
259 pub fn read_global<G: Global, R>(
260 &mut self,
261 read: impl FnOnce(&G, &WindowContext) -> R,
262 ) -> Result<R> {
263 self.window.update(self, |_, cx| read(cx.global(), cx))
264 }
265
266 /// A convenience method for [`AppContext::update_global`].
267 /// for updating the global state of the specified type.
268 pub fn update_global<G, R>(
269 &mut self,
270 update: impl FnOnce(&mut G, &mut WindowContext) -> R,
271 ) -> Result<R>
272 where
273 G: Global,
274 {
275 self.window.update(self, |_, cx| cx.update_global(update))
276 }
277
278 /// Schedule a future to be executed on the main thread. This is used for collecting
279 /// the results of background tasks and updating the UI.
280 pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task<R>
281 where
282 Fut: Future<Output = R> + 'static,
283 R: 'static,
284 {
285 self.foreground_executor.spawn(f(self.clone()))
286 }
287
288 /// Present a platform dialog.
289 /// The provided message will be presented, along with buttons for each answer.
290 /// When a button is clicked, the returned Receiver will receive the index of the clicked button.
291 pub fn prompt(
292 &mut self,
293 level: PromptLevel,
294 message: &str,
295 detail: Option<&str>,
296 answers: &[&str],
297 ) -> oneshot::Receiver<usize> {
298 self.window
299 .update(self, |_, cx| cx.prompt(level, message, detail, answers))
300 .unwrap_or_else(|_| oneshot::channel().1)
301 }
302}
303
304impl Context for AsyncWindowContext {
305 type Result<T> = Result<T>;
306
307 fn new_model<T>(
308 &mut self,
309 build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
310 ) -> Result<Model<T>>
311 where
312 T: 'static,
313 {
314 self.window.update(self, |_, cx| cx.new_model(build_model))
315 }
316
317 fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {
318 self.window.update(self, |_, cx| cx.reserve_model())
319 }
320
321 fn insert_model<T: 'static>(
322 &mut self,
323 reservation: Reservation<T>,
324 build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
325 ) -> Self::Result<Model<T>> {
326 self.window
327 .update(self, |_, cx| cx.insert_model(reservation, build_model))
328 }
329
330 fn update_model<T: 'static, R>(
331 &mut self,
332 handle: &Model<T>,
333 update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
334 ) -> Result<R> {
335 self.window
336 .update(self, |_, cx| cx.update_model(handle, update))
337 }
338
339 fn read_model<T, R>(
340 &self,
341 handle: &Model<T>,
342 read: impl FnOnce(&T, &AppContext) -> R,
343 ) -> Self::Result<R>
344 where
345 T: 'static,
346 {
347 self.app.read_model(handle, read)
348 }
349
350 fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
351 where
352 F: FnOnce(AnyView, &mut WindowContext) -> T,
353 {
354 self.app.update_window(window, update)
355 }
356
357 fn read_window<T, R>(
358 &self,
359 window: &WindowHandle<T>,
360 read: impl FnOnce(View<T>, &AppContext) -> R,
361 ) -> Result<R>
362 where
363 T: 'static,
364 {
365 self.app.read_window(window, read)
366 }
367}
368
369impl VisualContext for AsyncWindowContext {
370 fn new_view<V>(
371 &mut self,
372 build_view_state: impl FnOnce(&mut ViewContext<V>) -> V,
373 ) -> Self::Result<View<V>>
374 where
375 V: 'static + Render,
376 {
377 self.window
378 .update(self, |_, cx| cx.new_view(build_view_state))
379 }
380
381 fn update_view<V: 'static, R>(
382 &mut self,
383 view: &View<V>,
384 update: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
385 ) -> Self::Result<R> {
386 self.window
387 .update(self, |_, cx| cx.update_view(view, update))
388 }
389
390 fn replace_root_view<V>(
391 &mut self,
392 build_view: impl FnOnce(&mut ViewContext<V>) -> V,
393 ) -> Self::Result<View<V>>
394 where
395 V: 'static + Render,
396 {
397 self.window
398 .update(self, |_, cx| cx.replace_root_view(build_view))
399 }
400
401 fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
402 where
403 V: FocusableView,
404 {
405 self.window.update(self, |_, cx| {
406 view.read(cx).focus_handle(cx).clone().focus(cx);
407 })
408 }
409
410 fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
411 where
412 V: crate::ManagedView,
413 {
414 self.window
415 .update(self, |_, cx| view.update(cx, |_, cx| cx.emit(DismissEvent)))
416 }
417}