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