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