app.rs

  1use crate::{
  2    current_platform, AnyWindowHandle, Context, LayoutId, MainThreadOnly, Platform, Reference,
  3    RootView, TextSystem, Window, WindowContext, WindowHandle, WindowId,
  4};
  5use anyhow::{anyhow, Result};
  6use collections::{HashMap, VecDeque};
  7use futures::{future, Future};
  8use parking_lot::Mutex;
  9use slotmap::SlotMap;
 10use smallvec::SmallVec;
 11use std::{
 12    any::Any,
 13    marker::PhantomData,
 14    sync::{Arc, Weak},
 15};
 16
 17#[derive(Clone)]
 18pub struct App(Arc<Mutex<AppContext>>);
 19
 20impl App {
 21    pub fn production() -> Self {
 22        Self::new(current_platform())
 23    }
 24
 25    #[cfg(any(test, feature = "test"))]
 26    pub fn test() -> Self {
 27        Self::new(Arc::new(super::TestPlatform::new()))
 28    }
 29
 30    fn new(platform: Arc<dyn Platform>) -> Self {
 31        let dispatcher = platform.dispatcher();
 32        let text_system = Arc::new(TextSystem::new(platform.text_system()));
 33        let mut entities = SlotMap::with_key();
 34        let unit_entity_id = entities.insert(Some(Box::new(()) as Box<dyn Any + Send>));
 35        Self(Arc::new_cyclic(|this| {
 36            Mutex::new(AppContext {
 37                this: this.clone(),
 38                platform: MainThreadOnly::new(platform, dispatcher),
 39                text_system,
 40                unit_entity_id,
 41                entities,
 42                windows: SlotMap::with_key(),
 43                pending_updates: 0,
 44                pending_effects: Default::default(),
 45                observers: Default::default(),
 46                layout_id_buffer: Default::default(),
 47            })
 48        }))
 49    }
 50
 51    pub fn run<F>(self, on_finish_launching: F)
 52    where
 53        F: 'static + FnOnce(&mut AppContext),
 54    {
 55        let this = self.clone();
 56        let platform = self.0.lock().platform.clone();
 57        platform.borrow_on_main_thread().run(Box::new(move || {
 58            let cx = &mut *this.0.lock();
 59            on_finish_launching(cx);
 60        }));
 61    }
 62}
 63
 64type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>; 2]>;
 65
 66pub struct AppContext {
 67    this: Weak<Mutex<AppContext>>,
 68    platform: MainThreadOnly<dyn Platform>,
 69    text_system: Arc<TextSystem>,
 70    pub(crate) unit_entity_id: EntityId,
 71    pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any + Send>>>,
 72    pub(crate) windows: SlotMap<WindowId, Option<Window>>,
 73    pending_updates: usize,
 74    pub(crate) pending_effects: VecDeque<Effect>,
 75    pub(crate) observers: HashMap<EntityId, Handlers>,
 76    // We recycle this memory across layout requests.
 77    pub(crate) layout_id_buffer: Vec<LayoutId>,
 78}
 79
 80impl AppContext {
 81    pub fn text_system(&self) -> &Arc<TextSystem> {
 82        &self.text_system
 83    }
 84
 85    pub fn spawn_on_main<F, R>(
 86        &mut self,
 87        f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static,
 88    ) -> impl Future<Output = R>
 89    where
 90        F: Future<Output = R> + 'static,
 91        R: Send + 'static,
 92    {
 93        let this = self.this.upgrade().unwrap();
 94        self.platform.read(move |platform| {
 95            let cx = &mut *this.lock();
 96            f(platform, cx)
 97        })
 98    }
 99
100    pub fn open_window<S: 'static + Send>(
101        &mut self,
102        options: crate::WindowOptions,
103        build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
104    ) -> impl Future<Output = WindowHandle<S>> {
105        let id = self.windows.insert(None);
106        let handle = WindowHandle::new(id);
107        self.spawn_on_main(move |platform, cx| {
108            let mut window = Window::new(handle.into(), options, platform);
109            let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
110            window.root_view.replace(Box::new(root_view));
111            cx.windows.get_mut(id).unwrap().replace(window);
112            future::ready(handle)
113        })
114    }
115
116    pub(crate) fn update_window<R>(
117        &mut self,
118        handle: AnyWindowHandle,
119        update: impl FnOnce(&mut WindowContext) -> R,
120    ) -> Result<R> {
121        let mut window = self
122            .windows
123            .get_mut(handle.id)
124            .ok_or_else(|| anyhow!("window not found"))?
125            .take()
126            .unwrap();
127
128        let result = update(&mut WindowContext::mutable(self, &mut window));
129        window.dirty = true;
130
131        self.windows
132            .get_mut(handle.id)
133            .ok_or_else(|| anyhow!("window not found"))?
134            .replace(window);
135
136        Ok(result)
137    }
138
139    fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
140        self.pending_updates += 1;
141        let result = update(self);
142        self.pending_updates -= 1;
143        if self.pending_updates == 0 {
144            self.flush_effects();
145        }
146        result
147    }
148
149    fn flush_effects(&mut self) {
150        while let Some(effect) = self.pending_effects.pop_front() {
151            match effect {
152                Effect::Notify(entity_id) => self.apply_notify_effect(entity_id),
153            }
154        }
155    }
156
157    fn apply_notify_effect(&mut self, updated_entity: EntityId) {
158        if let Some(mut handlers) = self.observers.remove(&updated_entity) {
159            handlers.retain(|handler| handler(self));
160            if let Some(new_handlers) = self.observers.remove(&updated_entity) {
161                handlers.extend(new_handlers);
162            }
163            self.observers.insert(updated_entity, handlers);
164        }
165    }
166}
167
168impl Context for AppContext {
169    type EntityContext<'a, 'w, T: Send + Sync + 'static> = ModelContext<'a, T>;
170
171    fn entity<T: Send + Sync + 'static>(
172        &mut self,
173        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
174    ) -> Handle<T> {
175        let id = self.entities.insert(None);
176        let entity = Box::new(build_entity(&mut ModelContext::mutable(self, id)));
177        self.entities.get_mut(id).unwrap().replace(entity);
178
179        Handle::new(id)
180    }
181
182    fn update_entity<T: Send + Sync + 'static, R>(
183        &mut self,
184        handle: &Handle<T>,
185        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
186    ) -> R {
187        let mut entity = self
188            .entities
189            .get_mut(handle.id)
190            .unwrap()
191            .take()
192            .unwrap()
193            .downcast::<T>()
194            .unwrap();
195
196        let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
197        self.entities.get_mut(handle.id).unwrap().replace(entity);
198        result
199    }
200}
201
202pub struct ModelContext<'a, T> {
203    app: Reference<'a, AppContext>,
204    entity_type: PhantomData<T>,
205    entity_id: EntityId,
206}
207
208impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
209    pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
210        Self {
211            app: Reference::Mutable(app),
212            entity_type: PhantomData,
213            entity_id,
214        }
215    }
216
217    fn immutable(app: &'a AppContext, entity_id: EntityId) -> Self {
218        Self {
219            app: Reference::Immutable(app),
220            entity_type: PhantomData,
221            entity_id,
222        }
223    }
224
225    fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
226        let mut entity = self
227            .app
228            .entities
229            .get_mut(self.entity_id)
230            .unwrap()
231            .take()
232            .unwrap();
233        let result = update(entity.downcast_mut::<T>().unwrap(), self);
234        self.app
235            .entities
236            .get_mut(self.entity_id)
237            .unwrap()
238            .replace(entity);
239        result
240    }
241
242    pub fn handle(&self) -> WeakHandle<T> {
243        WeakHandle {
244            id: self.entity_id,
245            entity_type: PhantomData,
246        }
247    }
248
249    pub fn observe<E: Send + Sync + 'static>(
250        &mut self,
251        handle: &Handle<E>,
252        on_notify: impl Fn(&mut T, Handle<E>, &mut ModelContext<'_, T>) + Send + Sync + 'static,
253    ) {
254        let this = self.handle();
255        let handle = handle.downgrade();
256        self.app
257            .observers
258            .entry(handle.id)
259            .or_default()
260            .push(Arc::new(move |cx| {
261                if let Some((this, handle)) = this.upgrade(cx).zip(handle.upgrade(cx)) {
262                    this.update(cx, |this, cx| on_notify(this, handle, cx));
263                    true
264                } else {
265                    false
266                }
267            }));
268    }
269
270    pub fn notify(&mut self) {
271        self.app
272            .pending_effects
273            .push_back(Effect::Notify(self.entity_id));
274    }
275}
276
277impl<'a, T: 'static> Context for ModelContext<'a, T> {
278    type EntityContext<'b, 'c, U: Send + Sync + 'static> = ModelContext<'b, U>;
279
280    fn entity<U: Send + Sync + 'static>(
281        &mut self,
282        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
283    ) -> Handle<U> {
284        self.app.entity(build_entity)
285    }
286
287    fn update_entity<U: Send + Sync + 'static, R>(
288        &mut self,
289        handle: &Handle<U>,
290        update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
291    ) -> R {
292        self.app.update_entity(handle, update)
293    }
294}
295
296slotmap::new_key_type! { pub struct EntityId; }
297
298pub struct Handle<T> {
299    pub(crate) id: EntityId,
300    pub(crate) entity_type: PhantomData<T>,
301}
302
303impl<T: Send + Sync + 'static> Handle<T> {
304    fn new(id: EntityId) -> Self {
305        Self {
306            id,
307            entity_type: PhantomData,
308        }
309    }
310
311    pub fn downgrade(&self) -> WeakHandle<T> {
312        WeakHandle {
313            id: self.id,
314            entity_type: self.entity_type,
315        }
316    }
317
318    /// Update the entity referenced by this handle with the given function.
319    ///
320    /// The update function receives a context appropriate for its environment.
321    /// When updating in an `AppContext`, it receives a `ModelContext`.
322    /// When updating an a `WindowContext`, it receives a `ViewContext`.
323    pub fn update<C: Context, R>(
324        &self,
325        cx: &mut C,
326        update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
327    ) -> R {
328        cx.update_entity(self, update)
329    }
330}
331
332impl<T> Clone for Handle<T> {
333    fn clone(&self) -> Self {
334        Self {
335            id: self.id,
336            entity_type: PhantomData,
337        }
338    }
339}
340
341pub struct WeakHandle<T> {
342    pub(crate) id: EntityId,
343    pub(crate) entity_type: PhantomData<T>,
344}
345
346impl<T: Send + Sync + 'static> WeakHandle<T> {
347    pub fn upgrade(&self, cx: &impl Context) -> Option<Handle<T>> {
348        // todo!("Actually upgrade")
349        Some(Handle {
350            id: self.id,
351            entity_type: self.entity_type,
352        })
353    }
354
355    /// Update the entity referenced by this handle with the given function if
356    /// the referenced entity still exists. Returns an error if the entity has
357    /// been released.
358    ///
359    /// The update function receives a context appropriate for its environment.
360    /// When updating in an `AppContext`, it receives a `ModelContext`.
361    /// When updating an a `WindowContext`, it receives a `ViewContext`.
362    pub fn update<C: Context, R>(
363        &self,
364        cx: &mut C,
365        update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
366    ) -> Result<R> {
367        if let Some(this) = self.upgrade(cx) {
368            Ok(cx.update_entity(&this, update))
369        } else {
370            Err(anyhow!("entity released"))
371        }
372    }
373}
374
375pub(crate) enum Effect {
376    Notify(EntityId),
377}
378
379#[cfg(test)]
380mod tests {
381    use super::AppContext;
382
383    #[test]
384    fn test_app_context_send_sync() {
385        // This will not compile if `AppContext` does not implement `Send`
386        fn assert_send<T: Send>() {}
387        assert_send::<AppContext>();
388    }
389}