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