app.rs

  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}