app.rs

  1use crate::{
  2    current_platform, Context, LayoutId, Platform, Reference, TextSystem, View, Window,
  3    WindowContext, WindowHandle, WindowId,
  4};
  5use anyhow::{anyhow, Result};
  6use slotmap::SlotMap;
  7use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc, sync::Arc};
  8
  9#[derive(Clone)]
 10pub struct App(Rc<RefCell<AppContext>>);
 11
 12impl App {
 13    pub fn new() -> Self {
 14        Self(Rc::new(RefCell::new(AppContext::new(current_platform()))))
 15    }
 16
 17    pub fn run<F>(self, on_finish_launching: F)
 18    where
 19        F: 'static + FnOnce(&mut AppContext),
 20    {
 21        let platform = self.0.borrow().platform().clone();
 22        platform.run(Box::new(move || {
 23            let mut cx = self.0.borrow_mut();
 24            on_finish_launching(&mut *cx);
 25        }));
 26    }
 27}
 28
 29pub struct AppContext {
 30    platform: Rc<dyn Platform>,
 31    text_system: Arc<TextSystem>,
 32    pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any>>>,
 33    pub(crate) windows: SlotMap<WindowId, Option<Window>>,
 34    // We recycle this memory across layout requests.
 35    pub(crate) layout_id_buffer: Vec<LayoutId>,
 36}
 37
 38impl AppContext {
 39    pub fn new(platform: Rc<dyn Platform>) -> Self {
 40        let text_system = Arc::new(TextSystem::new(platform.text_system()));
 41        AppContext {
 42            platform,
 43            text_system,
 44            entities: SlotMap::with_key(),
 45            windows: SlotMap::with_key(),
 46            layout_id_buffer: Default::default(),
 47        }
 48    }
 49
 50    #[cfg(any(test, feature = "test"))]
 51    pub fn test() -> Self {
 52        Self::new(Rc::new(super::TestPlatform::new()))
 53    }
 54
 55    pub fn platform(&self) -> &Rc<dyn Platform> {
 56        &self.platform
 57    }
 58
 59    pub fn text_system(&self) -> &Arc<TextSystem> {
 60        &self.text_system
 61    }
 62
 63    pub fn open_window<S: 'static>(
 64        &mut self,
 65        options: crate::WindowOptions,
 66        build_root_view: impl FnOnce(&mut WindowContext) -> View<S>,
 67    ) -> WindowHandle<S> {
 68        let id = self.windows.insert(None);
 69        let handle = WindowHandle::new(id);
 70        let platform_window = self.platform.open_window(handle.into(), options);
 71
 72        let mut window = Window::new(id, platform_window);
 73        let root_view = build_root_view(&mut WindowContext::mutable(self, &mut window));
 74        window.root_view.replace(Box::new(root_view));
 75
 76        self.windows.get_mut(id).unwrap().replace(window);
 77        handle
 78    }
 79
 80    pub(crate) fn update_window<R>(
 81        &mut self,
 82        window_id: WindowId,
 83        update: impl FnOnce(&mut WindowContext) -> R,
 84    ) -> Result<R> {
 85        let mut window = self
 86            .windows
 87            .get_mut(window_id)
 88            .ok_or_else(|| anyhow!("window not found"))?
 89            .take()
 90            .unwrap();
 91
 92        let result = update(&mut WindowContext::mutable(self, &mut window));
 93
 94        self.windows
 95            .get_mut(window_id)
 96            .ok_or_else(|| anyhow!("window not found"))?
 97            .replace(window);
 98
 99        Ok(result)
100    }
101}
102
103impl Context for AppContext {
104    type EntityContext<'a, 'w, T: 'static> = ModelContext<'a, T>;
105
106    fn entity<T: 'static>(
107        &mut self,
108        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
109    ) -> Handle<T> {
110        let id = self.entities.insert(None);
111        let entity = Box::new(build_entity(&mut ModelContext::mutable(self, id)));
112        self.entities.get_mut(id).unwrap().replace(entity);
113
114        Handle::new(id)
115    }
116
117    fn update_entity<T: 'static, R>(
118        &mut self,
119        handle: &Handle<T>,
120        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
121    ) -> R {
122        let mut entity = self
123            .entities
124            .get_mut(handle.id)
125            .unwrap()
126            .take()
127            .unwrap()
128            .downcast::<T>()
129            .unwrap();
130
131        let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
132        self.entities.get_mut(handle.id).unwrap().replace(entity);
133        result
134    }
135}
136
137pub struct ModelContext<'a, T> {
138    app: Reference<'a, AppContext>,
139    entity_type: PhantomData<T>,
140    entity_id: EntityId,
141}
142
143impl<'a, T: 'static> ModelContext<'a, T> {
144    pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
145        Self {
146            app: Reference::Mutable(app),
147            entity_type: PhantomData,
148            entity_id,
149        }
150    }
151
152    fn immutable(app: &'a AppContext, entity_id: EntityId) -> Self {
153        Self {
154            app: Reference::Immutable(app),
155            entity_type: PhantomData,
156            entity_id,
157        }
158    }
159
160    fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
161        let mut entity = self
162            .app
163            .entities
164            .get_mut(self.entity_id)
165            .unwrap()
166            .take()
167            .unwrap();
168        let result = update(entity.downcast_mut::<T>().unwrap(), self);
169        self.app
170            .entities
171            .get_mut(self.entity_id)
172            .unwrap()
173            .replace(entity);
174        result
175    }
176}
177
178impl<'a, T: 'static> Context for ModelContext<'a, T> {
179    type EntityContext<'b, 'c, U: 'static> = ModelContext<'b, U>;
180
181    fn entity<U: 'static>(
182        &mut self,
183        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
184    ) -> Handle<U> {
185        self.app.entity(build_entity)
186    }
187
188    fn update_entity<U: 'static, R>(
189        &mut self,
190        handle: &Handle<U>,
191        update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
192    ) -> R {
193        self.app.update_entity(handle, update)
194    }
195}
196
197pub struct Handle<T> {
198    pub(crate) id: EntityId,
199    pub(crate) entity_type: PhantomData<T>,
200}
201
202slotmap::new_key_type! { pub struct EntityId; }
203
204impl<T: 'static> Handle<T> {
205    fn new(id: EntityId) -> Self {
206        Self {
207            id,
208            entity_type: PhantomData,
209        }
210    }
211
212    /// Update the entity referenced by this handle with the given function.
213    ///
214    /// The update function receives a context appropriate for its environment.
215    /// When updating in an `AppContext`, it receives a `ModelContext`.
216    /// When updating an a `WindowContext`, it receives a `ViewContext`.
217    pub fn update<C: Context, R>(
218        &self,
219        cx: &mut C,
220        update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
221    ) -> R {
222        cx.update_entity(self, update)
223    }
224}
225
226impl<T> Clone for Handle<T> {
227    fn clone(&self) -> Self {
228        Self {
229            id: self.id,
230            entity_type: PhantomData,
231        }
232    }
233}