app.rs

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