app.rs

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