1mod model_context;
2
3pub use model_context::*;
4
5use crate::{
6 current_platform, AnyWindowHandle, Context, LayoutId, MainThreadOnly, Platform, RootView,
7 TextSystem, Window, WindowContext, WindowHandle, WindowId,
8};
9use anyhow::{anyhow, Result};
10use collections::{HashMap, VecDeque};
11use futures::{future, Future};
12use parking_lot::Mutex;
13use slotmap::SlotMap;
14use smallvec::SmallVec;
15use std::{
16 any::Any,
17 marker::PhantomData,
18 sync::{Arc, Weak},
19};
20use util::ResultExt;
21
22#[derive(Clone)]
23pub struct App(Arc<Mutex<AppContext>>);
24
25impl App {
26 pub fn production() -> Self {
27 Self::new(current_platform())
28 }
29
30 #[cfg(any(test, feature = "test"))]
31 pub fn test() -> Self {
32 Self::new(Arc::new(super::TestPlatform::new()))
33 }
34
35 fn new(platform: Arc<dyn Platform>) -> Self {
36 let dispatcher = platform.dispatcher();
37 let text_system = Arc::new(TextSystem::new(platform.text_system()));
38 let mut entities = SlotMap::with_key();
39 let unit_entity = Handle::new(entities.insert(Some(Box::new(()) as Box<dyn Any + Send>)));
40 Self(Arc::new_cyclic(|this| {
41 Mutex::new(AppContext {
42 this: this.clone(),
43 platform: MainThreadOnly::new(platform, dispatcher),
44 text_system,
45 unit_entity,
46 entities,
47 windows: SlotMap::with_key(),
48 pending_updates: 0,
49 pending_effects: Default::default(),
50 observers: Default::default(),
51 layout_id_buffer: Default::default(),
52 })
53 }))
54 }
55
56 pub fn run<F>(self, on_finish_launching: F)
57 where
58 F: 'static + FnOnce(&mut AppContext),
59 {
60 let this = self.clone();
61 let platform = self.0.lock().platform.clone();
62 platform.borrow_on_main_thread().run(Box::new(move || {
63 let cx = &mut *this.0.lock();
64 on_finish_launching(cx);
65 }));
66 }
67}
68
69type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>; 2]>;
70
71pub struct AppContext {
72 this: Weak<Mutex<AppContext>>,
73 platform: MainThreadOnly<dyn Platform>,
74 text_system: Arc<TextSystem>,
75 pub(crate) unit_entity: Handle<()>,
76 pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any + Send>>>,
77 pub(crate) windows: SlotMap<WindowId, Option<Window>>,
78 pending_updates: usize,
79 pub(crate) pending_effects: VecDeque<Effect>,
80 pub(crate) observers: HashMap<EntityId, Handlers>,
81 // We recycle this memory across layout requests.
82 pub(crate) layout_id_buffer: Vec<LayoutId>,
83}
84
85impl AppContext {
86 pub fn text_system(&self) -> &Arc<TextSystem> {
87 &self.text_system
88 }
89
90 pub fn to_async(&self) -> AsyncContext {
91 AsyncContext(self.this.clone())
92 }
93
94 pub fn spawn_on_main<F, R>(
95 &self,
96 f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static,
97 ) -> impl Future<Output = R>
98 where
99 F: Future<Output = R> + 'static,
100 R: Send + 'static,
101 {
102 let this = self.this.upgrade().unwrap();
103 self.platform.read(move |platform| {
104 let cx = &mut *this.lock();
105 cx.update(|cx| f(platform, cx))
106 })
107 }
108
109 pub fn open_window<S: 'static + Send + Sync>(
110 &mut self,
111 options: crate::WindowOptions,
112 build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
113 ) -> impl Future<Output = WindowHandle<S>> {
114 let id = self.windows.insert(None);
115 let handle = WindowHandle::new(id);
116 self.spawn_on_main(move |platform, cx| {
117 let mut window = Window::new(handle.into(), options, platform, cx);
118 let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
119 window.root_view.replace(root_view.into_any());
120 cx.windows.get_mut(id).unwrap().replace(window);
121 future::ready(handle)
122 })
123 }
124
125 pub(crate) fn update_window<R>(
126 &mut self,
127 id: WindowId,
128 update: impl FnOnce(&mut WindowContext) -> R,
129 ) -> Result<R> {
130 self.update(|cx| {
131 let mut window = cx
132 .windows
133 .get_mut(id)
134 .ok_or_else(|| anyhow!("window not found"))?
135 .take()
136 .unwrap();
137
138 let result = update(&mut WindowContext::mutable(cx, &mut window));
139 window.dirty = true;
140
141 cx.windows
142 .get_mut(id)
143 .ok_or_else(|| anyhow!("window not found"))?
144 .replace(window);
145
146 Ok(result)
147 })
148 }
149
150 fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
151 self.pending_updates += 1;
152 let result = update(self);
153 self.pending_updates -= 1;
154 if self.pending_updates == 0 {
155 self.flush_effects();
156 }
157 result
158 }
159
160 fn flush_effects(&mut self) {
161 while let Some(effect) = self.pending_effects.pop_front() {
162 match effect {
163 Effect::Notify(entity_id) => self.apply_notify_effect(entity_id),
164 }
165 }
166
167 let dirty_window_ids = self
168 .windows
169 .iter()
170 .filter_map(|(window_id, window)| {
171 let window = window.as_ref().unwrap();
172 if window.dirty {
173 Some(window_id)
174 } else {
175 None
176 }
177 })
178 .collect::<Vec<_>>();
179
180 for dirty_window_id in dirty_window_ids {
181 self.update_window(dirty_window_id, |cx| cx.draw())
182 .unwrap() // We know we have the window.
183 .log_err();
184 }
185 }
186
187 fn apply_notify_effect(&mut self, updated_entity: EntityId) {
188 if let Some(mut handlers) = self.observers.remove(&updated_entity) {
189 handlers.retain(|handler| handler(self));
190 if let Some(new_handlers) = self.observers.remove(&updated_entity) {
191 handlers.extend(new_handlers);
192 }
193 self.observers.insert(updated_entity, handlers);
194 }
195 }
196}
197
198impl Context for AppContext {
199 type EntityContext<'a, 'w, T: Send + Sync + 'static> = ModelContext<'a, T>;
200 type Result<T> = T;
201
202 fn entity<T: Send + Sync + 'static>(
203 &mut self,
204 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
205 ) -> Handle<T> {
206 let id = self.entities.insert(None);
207 let entity = Box::new(build_entity(&mut ModelContext::mutable(self, id)));
208 self.entities.get_mut(id).unwrap().replace(entity);
209
210 Handle::new(id)
211 }
212
213 fn update_entity<T: Send + Sync + 'static, R>(
214 &mut self,
215 handle: &Handle<T>,
216 update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
217 ) -> R {
218 let mut entity = self
219 .entities
220 .get_mut(handle.id)
221 .unwrap()
222 .take()
223 .unwrap()
224 .downcast::<T>()
225 .unwrap();
226
227 let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
228 self.entities.get_mut(handle.id).unwrap().replace(entity);
229 result
230 }
231}
232
233#[derive(Clone)]
234pub struct AsyncContext(Weak<Mutex<AppContext>>);
235
236impl Context for AsyncContext {
237 type EntityContext<'a, 'b, T: Send + Sync + 'static> = ModelContext<'a, T>;
238 type Result<T> = Result<T>;
239
240 fn entity<T: Send + Sync + 'static>(
241 &mut self,
242 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
243 ) -> Result<Handle<T>> {
244 let app = self
245 .0
246 .upgrade()
247 .ok_or_else(|| anyhow!("app was released"))?;
248 let mut lock = app.lock();
249 Ok(lock.entity(build_entity))
250 }
251
252 fn update_entity<T: Send + Sync + 'static, R>(
253 &mut self,
254 handle: &Handle<T>,
255 update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
256 ) -> Result<R> {
257 let app = self
258 .0
259 .upgrade()
260 .ok_or_else(|| anyhow!("app was released"))?;
261 let mut lock = app.lock();
262 Ok(lock.update_entity(handle, update))
263 }
264}
265
266impl AsyncContext {
267 pub fn update_window<T>(
268 &self,
269 handle: AnyWindowHandle,
270 update: impl FnOnce(&mut WindowContext) -> T + Send + Sync,
271 ) -> Result<T> {
272 let app = self
273 .0
274 .upgrade()
275 .ok_or_else(|| anyhow!("app was released"))?;
276 let mut app_context = app.lock();
277 app_context.update_window(handle.id, update)
278 }
279}
280
281slotmap::new_key_type! { pub struct EntityId; }
282
283pub struct Handle<T> {
284 pub(crate) id: EntityId,
285 pub(crate) entity_type: PhantomData<T>,
286}
287
288impl<T: Send + Sync + 'static> Handle<T> {
289 fn new(id: EntityId) -> Self {
290 Self {
291 id,
292 entity_type: PhantomData,
293 }
294 }
295
296 pub fn downgrade(&self) -> WeakHandle<T> {
297 WeakHandle {
298 id: self.id,
299 entity_type: self.entity_type,
300 }
301 }
302
303 /// Update the entity referenced by this handle with the given function.
304 ///
305 /// The update function receives a context appropriate for its environment.
306 /// When updating in an `AppContext`, it receives a `ModelContext`.
307 /// When updating an a `WindowContext`, it receives a `ViewContext`.
308 pub fn update<C: Context, R>(
309 &self,
310 cx: &mut C,
311 update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
312 ) -> C::Result<R> {
313 cx.update_entity(self, update)
314 }
315}
316
317impl<T> Clone for Handle<T> {
318 fn clone(&self) -> Self {
319 Self {
320 id: self.id,
321 entity_type: PhantomData,
322 }
323 }
324}
325
326pub struct WeakHandle<T> {
327 pub(crate) id: EntityId,
328 pub(crate) entity_type: PhantomData<T>,
329}
330
331impl<T: Send + Sync + 'static> WeakHandle<T> {
332 pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> {
333 // todo!("Actually upgrade")
334 Some(Handle {
335 id: self.id,
336 entity_type: self.entity_type,
337 })
338 }
339
340 /// Update the entity referenced by this handle with the given function if
341 /// the referenced entity still exists. Returns an error if the entity has
342 /// been released.
343 ///
344 /// The update function receives a context appropriate for its environment.
345 /// When updating in an `AppContext`, it receives a `ModelContext`.
346 /// When updating an a `WindowContext`, it receives a `ViewContext`.
347 pub fn update<C: Context, R>(
348 &self,
349 cx: &mut C,
350 update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
351 ) -> Result<R>
352 where
353 Result<C::Result<R>>: crate::Flatten<R>,
354 {
355 crate::Flatten::flatten(
356 self.upgrade(cx)
357 .ok_or_else(|| anyhow!("entity release"))
358 .map(|this| cx.update_entity(&this, update)),
359 )
360 }
361}
362
363pub(crate) enum Effect {
364 Notify(EntityId),
365}
366
367#[cfg(test)]
368mod tests {
369 use super::AppContext;
370
371 #[test]
372 fn test_app_context_send_sync() {
373 // This will not compile if `AppContext` does not implement `Send`
374 fn assert_send<T: Send>() {}
375 assert_send::<AppContext>();
376 }
377}