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