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;
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> + Send + '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 let window =
98 self.spawn_on_main(move |platform, _cx| Window::new(handle.into(), options, platform));
99
100 let this = self.this.upgrade().unwrap();
101 async move {
102 let mut window = window.await;
103 let cx = &mut *this.lock();
104 let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
105 window.root_view.replace(Box::new(root_view));
106
107 cx.windows.get_mut(id).unwrap().replace(window);
108 handle
109 }
110 }
111
112 pub(crate) fn update_window<R>(
113 &mut self,
114 window_id: WindowId,
115 update: impl FnOnce(&mut WindowContext) -> R,
116 ) -> Result<R> {
117 let mut window = self
118 .windows
119 .get_mut(window_id)
120 .ok_or_else(|| anyhow!("window not found"))?
121 .take()
122 .unwrap();
123
124 let result = update(&mut WindowContext::mutable(self, &mut window));
125
126 self.windows
127 .get_mut(window_id)
128 .ok_or_else(|| anyhow!("window not found"))?
129 .replace(window);
130
131 Ok(result)
132 }
133}
134
135impl Context for AppContext {
136 type EntityContext<'a, 'w, T: Send + 'static> = ModelContext<'a, T>;
137
138 fn entity<T: Send + 'static>(
139 &mut self,
140 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
141 ) -> Handle<T> {
142 let id = self.entities.insert(None);
143 let entity = Box::new(build_entity(&mut ModelContext::mutable(self, id)));
144 self.entities.get_mut(id).unwrap().replace(entity);
145
146 Handle::new(id)
147 }
148
149 fn update_entity<T: Send + 'static, R>(
150 &mut self,
151 handle: &Handle<T>,
152 update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
153 ) -> R {
154 let mut entity = self
155 .entities
156 .get_mut(handle.id)
157 .unwrap()
158 .take()
159 .unwrap()
160 .downcast::<T>()
161 .unwrap();
162
163 let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
164 self.entities.get_mut(handle.id).unwrap().replace(entity);
165 result
166 }
167}
168
169pub struct ModelContext<'a, T> {
170 app: Reference<'a, AppContext>,
171 entity_type: PhantomData<T>,
172 entity_id: EntityId,
173}
174
175impl<'a, T: 'static> ModelContext<'a, T> {
176 pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
177 Self {
178 app: Reference::Mutable(app),
179 entity_type: PhantomData,
180 entity_id,
181 }
182 }
183
184 fn immutable(app: &'a AppContext, entity_id: EntityId) -> Self {
185 Self {
186 app: Reference::Immutable(app),
187 entity_type: PhantomData,
188 entity_id,
189 }
190 }
191
192 fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
193 let mut entity = self
194 .app
195 .entities
196 .get_mut(self.entity_id)
197 .unwrap()
198 .take()
199 .unwrap();
200 let result = update(entity.downcast_mut::<T>().unwrap(), self);
201 self.app
202 .entities
203 .get_mut(self.entity_id)
204 .unwrap()
205 .replace(entity);
206 result
207 }
208}
209
210impl<'a, T: 'static> Context for ModelContext<'a, T> {
211 type EntityContext<'b, 'c, U: Send + 'static> = ModelContext<'b, U>;
212
213 fn entity<U: Send + 'static>(
214 &mut self,
215 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
216 ) -> Handle<U> {
217 self.app.entity(build_entity)
218 }
219
220 fn update_entity<U: Send + 'static, R>(
221 &mut self,
222 handle: &Handle<U>,
223 update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
224 ) -> R {
225 self.app.update_entity(handle, update)
226 }
227}
228
229pub struct Handle<T> {
230 pub(crate) id: EntityId,
231 pub(crate) entity_type: PhantomData<T>,
232}
233
234slotmap::new_key_type! { pub struct EntityId; }
235
236impl<T: Send + 'static> Handle<T> {
237 fn new(id: EntityId) -> Self {
238 Self {
239 id,
240 entity_type: PhantomData,
241 }
242 }
243
244 /// Update the entity referenced by this handle with the given function.
245 ///
246 /// The update function receives a context appropriate for its environment.
247 /// When updating in an `AppContext`, it receives a `ModelContext`.
248 /// When updating an a `WindowContext`, it receives a `ViewContext`.
249 pub fn update<C: Context, R>(
250 &self,
251 cx: &mut C,
252 update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
253 ) -> R {
254 cx.update_entity(self, update)
255 }
256}
257
258impl<T> Clone for Handle<T> {
259 fn clone(&self) -> Self {
260 Self {
261 id: self.id,
262 entity_type: PhantomData,
263 }
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::AppContext;
270
271 #[test]
272 fn test_app_context_send_sync() {
273 // This will not compile if `AppContext` does not implement `Send`
274 fn assert_send<T: Send>() {}
275 assert_send::<AppContext>();
276 }
277}