app.rs

  1mod async_context;
  2mod entity_map;
  3mod model_context;
  4
  5pub use async_context::*;
  6pub use entity_map::*;
  7pub use model_context::*;
  8use refineable::Refineable;
  9
 10use crate::{
 11    current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor,
 12    FocusEvent, FocusHandle, FocusId, LayoutId, MainThread, MainThreadOnly, Platform,
 13    SubscriberSet, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
 14    WindowContext, WindowHandle, WindowId,
 15};
 16use anyhow::{anyhow, Result};
 17use collections::{HashMap, HashSet, VecDeque};
 18use futures::Future;
 19use parking_lot::Mutex;
 20use slotmap::SlotMap;
 21use std::{
 22    any::{type_name, Any, TypeId},
 23    mem,
 24    sync::{Arc, Weak},
 25};
 26use util::http::{self, HttpClient};
 27
 28#[derive(Clone)]
 29pub struct App(Arc<Mutex<AppContext>>);
 30
 31impl App {
 32    pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
 33        let http_client = http::client();
 34        Self::new(current_platform(), asset_source, http_client)
 35    }
 36
 37    #[cfg(any(test, feature = "test"))]
 38    pub fn test() -> Self {
 39        let platform = Arc::new(super::TestPlatform::new());
 40        let asset_source = Arc::new(());
 41        let http_client = util::http::FakeHttpClient::with_404_response();
 42        Self::new(platform, asset_source, http_client)
 43    }
 44
 45    fn new(
 46        platform: Arc<dyn Platform>,
 47        asset_source: Arc<dyn AssetSource>,
 48        http_client: Arc<dyn HttpClient>,
 49    ) -> Self {
 50        let executor = platform.executor();
 51        let entities = EntityMap::new();
 52        let unit_entity = entities.insert(entities.reserve(), ());
 53        Self(Arc::new_cyclic(|this| {
 54            Mutex::new(AppContext {
 55                this: this.clone(),
 56                text_system: Arc::new(TextSystem::new(platform.text_system())),
 57                pending_updates: 0,
 58                flushing_effects: false,
 59                next_frame_callbacks: Default::default(),
 60                platform: MainThreadOnly::new(platform, executor.clone()),
 61                executor,
 62                svg_renderer: SvgRenderer::new(asset_source),
 63                image_cache: ImageCache::new(http_client),
 64                text_style_stack: Vec::new(),
 65                global_stacks_by_type: HashMap::default(),
 66                unit_entity,
 67                entities,
 68                windows: SlotMap::with_key(),
 69                pending_notifications: Default::default(),
 70                pending_effects: Default::default(),
 71                observers: SubscriberSet::new(),
 72                event_handlers: SubscriberSet::new(),
 73                release_handlers: SubscriberSet::new(),
 74                layout_id_buffer: Default::default(),
 75            })
 76        }))
 77    }
 78
 79    pub fn run<F>(self, on_finish_launching: F)
 80    where
 81        F: 'static + FnOnce(&mut MainThread<AppContext>),
 82    {
 83        let this = self.clone();
 84        let platform = self.0.lock().platform.clone();
 85        platform.borrow_on_main_thread().run(Box::new(move || {
 86            let cx = &mut *this.0.lock();
 87            let cx = unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) };
 88            on_finish_launching(cx);
 89        }));
 90    }
 91}
 92
 93type Handler = Box<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>;
 94type EventHandler = Box<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
 95type ReleaseHandler = Box<dyn Fn(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>;
 96type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
 97
 98pub struct AppContext {
 99    this: Weak<Mutex<AppContext>>,
100    pub(crate) platform: MainThreadOnly<dyn Platform>,
101    text_system: Arc<TextSystem>,
102    flushing_effects: bool,
103    pending_updates: usize,
104    pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
105    pub(crate) executor: Executor,
106    pub(crate) svg_renderer: SvgRenderer,
107    pub(crate) image_cache: ImageCache,
108    pub(crate) text_style_stack: Vec<TextStyleRefinement>,
109    pub(crate) global_stacks_by_type: HashMap<TypeId, Vec<Box<dyn Any + Send + Sync>>>,
110    pub(crate) unit_entity: Handle<()>,
111    pub(crate) entities: EntityMap,
112    pub(crate) windows: SlotMap<WindowId, Option<Window>>,
113    pub(crate) pending_notifications: HashSet<EntityId>,
114    pending_effects: VecDeque<Effect>,
115    pub(crate) observers: SubscriberSet<EntityId, Handler>,
116    pub(crate) event_handlers: SubscriberSet<EntityId, EventHandler>,
117    pub(crate) release_handlers: SubscriberSet<EntityId, ReleaseHandler>,
118    pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
119}
120
121impl AppContext {
122    pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
123        self.pending_updates += 1;
124        let result = update(self);
125        if !self.flushing_effects && self.pending_updates == 1 {
126            self.flushing_effects = true;
127            self.flush_effects();
128            self.flushing_effects = false;
129        }
130        self.pending_updates -= 1;
131        result
132    }
133
134    pub(crate) fn update_window<R>(
135        &mut self,
136        id: WindowId,
137        update: impl FnOnce(&mut WindowContext) -> R,
138    ) -> Result<R> {
139        self.update(|cx| {
140            let mut window = cx
141                .windows
142                .get_mut(id)
143                .ok_or_else(|| anyhow!("window not found"))?
144                .take()
145                .unwrap();
146
147            let result = update(&mut WindowContext::mutable(cx, &mut window));
148
149            cx.windows
150                .get_mut(id)
151                .ok_or_else(|| anyhow!("window not found"))?
152                .replace(window);
153
154            Ok(result)
155        })
156    }
157
158    pub(crate) fn push_effect(&mut self, effect: Effect) {
159        match &effect {
160            Effect::Notify { emitter } => {
161                if self.pending_notifications.insert(*emitter) {
162                    self.pending_effects.push_back(effect);
163                }
164            }
165            Effect::Emit { .. } => self.pending_effects.push_back(effect),
166            Effect::FocusChanged { .. } => self.pending_effects.push_back(effect),
167        }
168    }
169
170    fn flush_effects(&mut self) {
171        loop {
172            self.release_dropped_entities();
173            if let Some(effect) = self.pending_effects.pop_front() {
174                match effect {
175                    Effect::Notify { emitter } => self.apply_notify_effect(emitter),
176                    Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
177                    Effect::FocusChanged { window_id, focused } => {
178                        self.apply_focus_changed(window_id, focused)
179                    }
180                }
181            } else {
182                break;
183            }
184        }
185
186        let dirty_window_ids = self
187            .windows
188            .iter()
189            .filter_map(|(window_id, window)| {
190                let window = window.as_ref().unwrap();
191                if window.dirty {
192                    Some(window_id)
193                } else {
194                    None
195                }
196            })
197            .collect::<Vec<_>>();
198
199        for dirty_window_id in dirty_window_ids {
200            self.update_window(dirty_window_id, |cx| cx.draw()).unwrap();
201        }
202    }
203
204    fn release_dropped_entities(&mut self) {
205        loop {
206            let dropped = self.entities.take_dropped();
207            if dropped.is_empty() {
208                break;
209            }
210
211            for (entity_id, mut entity) in dropped {
212                self.observers.remove(&entity_id);
213                self.event_handlers.remove(&entity_id);
214                for release_callback in self.release_handlers.remove(&entity_id) {
215                    release_callback(&mut entity, self);
216                }
217            }
218        }
219    }
220
221    fn apply_notify_effect(&mut self, emitter: EntityId) {
222        self.pending_notifications.remove(&emitter);
223        self.observers
224            .clone()
225            .retain(&emitter, |handler| handler(self));
226    }
227
228    fn apply_emit_effect(&mut self, emitter: EntityId, event: Box<dyn Any>) {
229        self.event_handlers
230            .clone()
231            .retain(&emitter, |handler| handler(&event, self));
232    }
233
234    fn apply_focus_changed(&mut self, window_id: WindowId, focused: Option<FocusId>) {
235        self.update_window(window_id, |cx| {
236            if cx.window.focus == focused {
237                let mut listeners = mem::take(&mut cx.window.focus_listeners);
238                let focused = focused.map(FocusHandle::new);
239                let blurred = cx.window.last_blur.unwrap().map(FocusHandle::new);
240                let event = FocusEvent { focused, blurred };
241                for listener in &listeners {
242                    listener(&event, cx);
243                }
244
245                listeners.extend(cx.window.focus_listeners.drain(..));
246                cx.window.focus_listeners = listeners;
247            }
248        })
249        .ok();
250    }
251
252    pub fn to_async(&self) -> AsyncAppContext {
253        AsyncAppContext(unsafe { mem::transmute(self.this.clone()) })
254    }
255
256    pub fn executor(&self) -> &Executor {
257        &self.executor
258    }
259
260    pub fn run_on_main<R>(
261        &mut self,
262        f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
263    ) -> Task<R>
264    where
265        R: Send + 'static,
266    {
267        if self.executor.is_main_thread() {
268            Task::ready(f(unsafe {
269                mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(self)
270            }))
271        } else {
272            let this = self.this.upgrade().unwrap();
273            self.executor.run_on_main(move || {
274                let cx = &mut *this.lock();
275                cx.update(|cx| f(unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(cx) }))
276            })
277        }
278    }
279
280    pub fn spawn_on_main<F, R>(
281        &self,
282        f: impl FnOnce(&mut MainThread<AppContext>) -> F + Send + 'static,
283    ) -> Task<R>
284    where
285        F: Future<Output = R> + 'static,
286        R: Send + 'static,
287    {
288        let this = self.this.upgrade().unwrap();
289        self.executor.spawn_on_main(move || {
290            let cx = &mut *this.lock();
291            cx.update(|cx| {
292                f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })
293            })
294        })
295    }
296
297    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
298    where
299        Fut: Future<Output = R> + Send + 'static,
300        R: Send + 'static,
301    {
302        let cx = self.to_async();
303        self.executor.spawn(async move {
304            let future = f(cx);
305            future.await
306        })
307    }
308
309    pub fn text_system(&self) -> &Arc<TextSystem> {
310        &self.text_system
311    }
312
313    pub fn text_style(&self) -> TextStyle {
314        let mut style = TextStyle::default();
315        for refinement in &self.text_style_stack {
316            style.refine(refinement);
317        }
318        style
319    }
320
321    pub fn global<G: 'static>(&self) -> &G {
322        self.global_stacks_by_type
323            .get(&TypeId::of::<G>())
324            .and_then(|stack| stack.last())
325            .and_then(|any_state| any_state.downcast_ref::<G>())
326            .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
327            .unwrap()
328    }
329
330    pub fn global_mut<G: 'static>(&mut self) -> &mut G {
331        self.global_stacks_by_type
332            .get_mut(&TypeId::of::<G>())
333            .and_then(|stack| stack.last_mut())
334            .and_then(|any_state| any_state.downcast_mut::<G>())
335            .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
336            .unwrap()
337    }
338
339    pub fn default_global<G: 'static + Default + Sync + Send>(&mut self) -> &mut G {
340        let stack = self
341            .global_stacks_by_type
342            .entry(TypeId::of::<G>())
343            .or_default();
344        if stack.is_empty() {
345            stack.push(Box::new(G::default()));
346        }
347        stack.last_mut().unwrap().downcast_mut::<G>().unwrap()
348    }
349
350    pub(crate) fn push_global<T: Send + Sync + 'static>(&mut self, state: T) {
351        self.global_stacks_by_type
352            .entry(TypeId::of::<T>())
353            .or_default()
354            .push(Box::new(state));
355    }
356
357    pub(crate) fn pop_global<T: 'static>(&mut self) {
358        self.global_stacks_by_type
359            .get_mut(&TypeId::of::<T>())
360            .and_then(|stack| stack.pop())
361            .expect("state stack underflow");
362    }
363
364    pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
365        self.text_style_stack.push(text_style);
366    }
367
368    pub(crate) fn pop_text_style(&mut self) {
369        self.text_style_stack.pop();
370    }
371}
372
373impl Context for AppContext {
374    type EntityContext<'a, 'w, T: Send + Sync + 'static> = ModelContext<'a, T>;
375    type Result<T> = T;
376
377    fn entity<T: Send + Sync + 'static>(
378        &mut self,
379        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
380    ) -> Handle<T> {
381        self.update(|cx| {
382            let slot = cx.entities.reserve();
383            let entity = build_entity(&mut ModelContext::mutable(cx, slot.id));
384            cx.entities.insert(slot, entity)
385        })
386    }
387
388    fn update_entity<T: Send + Sync + 'static, R>(
389        &mut self,
390        handle: &Handle<T>,
391        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
392    ) -> R {
393        self.update(|cx| {
394            let mut entity = cx.entities.lease(handle);
395            let result = update(&mut entity, &mut ModelContext::mutable(cx, handle.id));
396            cx.entities.end_lease(entity);
397            result
398        })
399    }
400}
401
402impl MainThread<AppContext> {
403    fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
404        self.0.update(|cx| {
405            update(unsafe {
406                std::mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx)
407            })
408        })
409    }
410
411    pub(crate) fn update_window<R>(
412        &mut self,
413        id: WindowId,
414        update: impl FnOnce(&mut MainThread<WindowContext>) -> R,
415    ) -> Result<R> {
416        self.0.update_window(id, |cx| {
417            update(unsafe {
418                std::mem::transmute::<&mut WindowContext, &mut MainThread<WindowContext>>(cx)
419            })
420        })
421    }
422
423    pub(crate) fn platform(&self) -> &dyn Platform {
424        self.platform.borrow_on_main_thread()
425    }
426
427    pub fn activate(&mut self, ignoring_other_apps: bool) {
428        self.platform().activate(ignoring_other_apps);
429    }
430
431    pub fn open_window<S: 'static + Send + Sync>(
432        &mut self,
433        options: crate::WindowOptions,
434        build_root_view: impl FnOnce(&mut WindowContext) -> View<S> + Send + 'static,
435    ) -> WindowHandle<S> {
436        self.update(|cx| {
437            let id = cx.windows.insert(None);
438            let handle = WindowHandle::new(id);
439            let mut window = Window::new(handle.into(), options, cx);
440            let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
441            window.root_view.replace(root_view.into_any());
442            cx.windows.get_mut(id).unwrap().replace(window);
443            handle
444        })
445    }
446}
447
448pub(crate) enum Effect {
449    Notify {
450        emitter: EntityId,
451    },
452    Emit {
453        emitter: EntityId,
454        event: Box<dyn Any + Send + Sync + 'static>,
455    },
456    FocusChanged {
457        window_id: WindowId,
458        focused: Option<FocusId>,
459    },
460}
461
462#[cfg(test)]
463mod tests {
464    use super::AppContext;
465
466    #[test]
467    fn test_app_context_send_sync() {
468        // This will not compile if `AppContext` does not implement `Send`
469        fn assert_send<T: Send>() {}
470        assert_send::<AppContext>();
471    }
472}