app.rs

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