1use crate::{
2 current_platform, Context, LayoutId, MainThreadOnly, Platform, Reference, RootView, TextSystem,
3 Window, WindowContext, WindowHandle, WindowId,
4};
5use anyhow::{anyhow, Result};
6use futures::{future, Future};
7use parking_lot::Mutex;
8use slotmap::SlotMap;
9use std::{
10 any::Any,
11 marker::PhantomData,
12 sync::{Arc, Weak},
13};
14
15#[derive(Clone)]
16pub struct App(Arc<Mutex<AppContext>>);
17
18impl App {
19 pub fn production() -> Self {
20 Self::new(current_platform())
21 }
22
23 #[cfg(any(test, feature = "test"))]
24 pub fn test() -> Self {
25 Self::new(Arc::new(super::TestPlatform::new()))
26 }
27
28 fn new(platform: Arc<dyn Platform>) -> Self {
29 let dispatcher = platform.dispatcher();
30 let text_system = Arc::new(TextSystem::new(platform.text_system()));
31 let mut entities = SlotMap::with_key();
32 let unit_entity_id = entities.insert(Some(Box::new(()) as Box<dyn Any + Send>));
33 Self(Arc::new_cyclic(|this| {
34 Mutex::new(AppContext {
35 this: this.clone(),
36 platform: MainThreadOnly::new(platform, dispatcher),
37 text_system,
38 unit_entity_id,
39 entities,
40 windows: SlotMap::with_key(),
41 layout_id_buffer: Default::default(),
42 })
43 }))
44 }
45
46 pub fn run<F>(self, on_finish_launching: F)
47 where
48 F: 'static + FnOnce(&mut AppContext),
49 {
50 let this = self.clone();
51 let platform = self.0.lock().platform.clone();
52 platform.borrow_on_main_thread().run(Box::new(move || {
53 let cx = &mut *this.0.lock();
54 on_finish_launching(cx);
55 }));
56 }
57}
58
59pub struct AppContext {
60 this: Weak<Mutex<AppContext>>,
61 platform: MainThreadOnly<dyn Platform>,
62 text_system: Arc<TextSystem>,
63 pub(crate) unit_entity_id: EntityId,
64 pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any + Send>>>,
65 pub(crate) windows: SlotMap<WindowId, Option<Window>>,
66 // We recycle this memory across layout requests.
67 pub(crate) layout_id_buffer: Vec<LayoutId>,
68}
69
70impl AppContext {
71 pub fn text_system(&self) -> &Arc<TextSystem> {
72 &self.text_system
73 }
74
75 pub fn spawn_on_main<F, R>(
76 &mut self,
77 f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static,
78 ) -> impl Future<Output = R>
79 where
80 F: Future<Output = R> + 'static,
81 R: Send + 'static,
82 {
83 let this = self.this.upgrade().unwrap();
84 self.platform.read(move |platform| {
85 let cx = &mut *this.lock();
86 f(platform, cx)
87 })
88 }
89
90 pub fn open_window<S: 'static + Send>(
91 &mut self,
92 options: crate::WindowOptions,
93 build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
94 ) -> impl Future<Output = WindowHandle<S>> {
95 let id = self.windows.insert(None);
96 let handle = WindowHandle::new(id);
97 self.spawn_on_main(move |platform, cx| {
98 let mut window = Window::new(handle.into(), options, platform);
99 let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
100 window.root_view.replace(Box::new(root_view));
101 cx.windows.get_mut(id).unwrap().replace(window);
102 future::ready(handle)
103 })
104 }
105
106 pub(crate) fn update_window<R>(
107 &mut self,
108 window_id: WindowId,
109 update: impl FnOnce(&mut WindowContext) -> R,
110 ) -> Result<R> {
111 let mut window = self
112 .windows
113 .get_mut(window_id)
114 .ok_or_else(|| anyhow!("window not found"))?
115 .take()
116 .unwrap();
117
118 let result = update(&mut WindowContext::mutable(self, &mut window));
119
120 self.windows
121 .get_mut(window_id)
122 .ok_or_else(|| anyhow!("window not found"))?
123 .replace(window);
124
125 Ok(result)
126 }
127}
128
129impl Context for AppContext {
130 type EntityContext<'a, 'w, T: Send + 'static> = ModelContext<'a, T>;
131
132 fn entity<T: Send + 'static>(
133 &mut self,
134 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
135 ) -> Handle<T> {
136 let id = self.entities.insert(None);
137 let entity = Box::new(build_entity(&mut ModelContext::mutable(self, id)));
138 self.entities.get_mut(id).unwrap().replace(entity);
139
140 Handle::new(id)
141 }
142
143 fn update_entity<T: Send + 'static, R>(
144 &mut self,
145 handle: &Handle<T>,
146 update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
147 ) -> R {
148 let mut entity = self
149 .entities
150 .get_mut(handle.id)
151 .unwrap()
152 .take()
153 .unwrap()
154 .downcast::<T>()
155 .unwrap();
156
157 let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
158 self.entities.get_mut(handle.id).unwrap().replace(entity);
159 result
160 }
161}
162
163pub struct ModelContext<'a, T> {
164 app: Reference<'a, AppContext>,
165 entity_type: PhantomData<T>,
166 entity_id: EntityId,
167}
168
169impl<'a, T: 'static> ModelContext<'a, T> {
170 pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
171 Self {
172 app: Reference::Mutable(app),
173 entity_type: PhantomData,
174 entity_id,
175 }
176 }
177
178 fn immutable(app: &'a AppContext, entity_id: EntityId) -> Self {
179 Self {
180 app: Reference::Immutable(app),
181 entity_type: PhantomData,
182 entity_id,
183 }
184 }
185
186 fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
187 let mut entity = self
188 .app
189 .entities
190 .get_mut(self.entity_id)
191 .unwrap()
192 .take()
193 .unwrap();
194 let result = update(entity.downcast_mut::<T>().unwrap(), self);
195 self.app
196 .entities
197 .get_mut(self.entity_id)
198 .unwrap()
199 .replace(entity);
200 result
201 }
202}
203
204impl<'a, T: 'static> Context for ModelContext<'a, T> {
205 type EntityContext<'b, 'c, U: Send + 'static> = ModelContext<'b, U>;
206
207 fn entity<U: Send + 'static>(
208 &mut self,
209 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
210 ) -> Handle<U> {
211 self.app.entity(build_entity)
212 }
213
214 fn update_entity<U: Send + 'static, R>(
215 &mut self,
216 handle: &Handle<U>,
217 update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
218 ) -> R {
219 self.app.update_entity(handle, update)
220 }
221}
222
223pub struct Handle<T> {
224 pub(crate) id: EntityId,
225 pub(crate) entity_type: PhantomData<T>,
226}
227
228slotmap::new_key_type! { pub struct EntityId; }
229
230impl<T: Send + 'static> Handle<T> {
231 fn new(id: EntityId) -> Self {
232 Self {
233 id,
234 entity_type: PhantomData,
235 }
236 }
237
238 /// Update the entity referenced by this handle with the given function.
239 ///
240 /// The update function receives a context appropriate for its environment.
241 /// When updating in an `AppContext`, it receives a `ModelContext`.
242 /// When updating an a `WindowContext`, it receives a `ViewContext`.
243 pub fn update<C: Context, R>(
244 &self,
245 cx: &mut C,
246 update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
247 ) -> R {
248 cx.update_entity(self, update)
249 }
250}
251
252impl<T> Clone for Handle<T> {
253 fn clone(&self) -> Self {
254 Self {
255 id: self.id,
256 entity_type: PhantomData,
257 }
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::AppContext;
264
265 #[test]
266 fn test_app_context_send_sync() {
267 // This will not compile if `AppContext` does not implement `Send`
268 fn assert_send<T: Send>() {}
269 assert_send::<AppContext>();
270 }
271}