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;
  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}