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