anathema_runtime/runtime/
mod.rs

1use std::sync::atomic::Ordering;
2use std::time::{Duration, Instant};
3
4use anathema_backend::{Backend, WidgetCycle};
5use anathema_geometry::Size;
6use anathema_state::{Changes, StateId, States, clear_all_changes, clear_all_subs, drain_changes};
7use anathema_store::tree::root_node;
8use anathema_templates::blueprints::Blueprint;
9use anathema_templates::{Document, Globals};
10use anathema_value_resolver::{AttributeStorage, Scope};
11use anathema_widgets::components::deferred::{CommandKind, DeferredComponents};
12use anathema_widgets::components::events::Event;
13use anathema_widgets::components::{
14    AnyComponentContext, AssociatedEvents, ComponentKind, ComponentRegistry, Emitter, ViewMessage,
15};
16use anathema_widgets::layout::{LayoutCtx, Viewport};
17use anathema_widgets::query::Children;
18use anathema_widgets::tabindex::{Index, TabIndex};
19use anathema_widgets::{
20    Component, Components, Factory, FloatingWidgets, GlyphMap, WidgetContainer, WidgetId, WidgetKind, WidgetTree,
21    eval_blueprint, update_widget,
22};
23use flume::Receiver;
24use notify::RecommendedWatcher;
25
26pub(crate) use self::error::show_error;
27use crate::builder::Builder;
28pub use crate::error::Result;
29use crate::events::GlobalEventHandler;
30use crate::{Error, REBUILD};
31
32mod error;
33mod testing;
34
35/// Anathema runtime
36pub struct Runtime<G> {
37    pub(super) blueprint: Blueprint,
38    pub(super) globals: Globals,
39    pub(super) factory: Factory,
40    pub(super) states: States,
41    pub(super) component_registry: ComponentRegistry,
42    pub(super) components: Components,
43    pub(super) document: Document,
44    pub(super) floating_widgets: FloatingWidgets,
45    pub(super) assoc_events: AssociatedEvents,
46    pub(super) glyph_map: GlyphMap,
47    pub(super) changes: Changes,
48    pub(super) viewport: Viewport,
49    pub(super) emitter: Emitter,
50    pub(super) sleep_micros: u64,
51    pub(super) message_receiver: flume::Receiver<ViewMessage>,
52    pub(super) dt: Instant,
53    pub(super) _watcher: Option<RecommendedWatcher>,
54    pub(super) deferred_components: DeferredComponents,
55    pub(super) global_event_handler: G,
56}
57
58impl Runtime<()> {
59    /// Create a runtime builder
60    pub fn builder<B: Backend>(doc: Document, backend: &B) -> Builder<()> {
61        Builder::new(doc, backend.size(), ())
62    }
63}
64
65impl<G: GlobalEventHandler> Runtime<G> {
66    pub(crate) fn new(
67        blueprint: Blueprint,
68        globals: Globals,
69        component_registry: ComponentRegistry,
70        document: Document,
71        factory: Factory,
72        message_receiver: Receiver<ViewMessage>,
73        emitter: Emitter,
74        watcher: Option<RecommendedWatcher>,
75        size: Size,
76        fps: u32,
77        global_event_handler: G,
78    ) -> Self {
79        let sleep_micros: u64 = ((1.0 / fps as f64) * 1000.0 * 1000.0) as u64;
80
81        Self {
82            component_registry,
83            components: Components::new(),
84            document,
85            factory,
86            states: States::new(),
87            floating_widgets: FloatingWidgets::empty(),
88            assoc_events: AssociatedEvents::new(),
89            glyph_map: GlyphMap::empty(),
90            blueprint,
91            globals,
92            changes: Changes::empty(),
93            viewport: Viewport::new(size),
94            message_receiver,
95            emitter,
96            dt: Instant::now(),
97            _watcher: watcher,
98            deferred_components: DeferredComponents::new(),
99            sleep_micros,
100            global_event_handler,
101        }
102    }
103
104    // TODO
105    // Rename Frame as it does not represent an individual frame
106    // but rather something that can continuously draw.
107    pub fn with_frame<B, F>(&mut self, backend: &mut B, mut f: F) -> Result<()>
108    where
109        B: Backend,
110        F: FnMut(&mut B, Frame<'_, '_, G>) -> Result<()>,
111    {
112        let mut tree = WidgetTree::empty();
113        let mut attribute_storage = AttributeStorage::empty();
114        let mut frame = self.next_frame(&mut tree, &mut attribute_storage)?;
115        frame.init_tree()?;
116        f(backend, frame)
117    }
118
119    pub fn run<B: Backend>(&mut self, backend: &mut B) -> Result<()> {
120        let sleep_micros = self.sleep_micros;
121        self.with_frame(backend, |backend, mut frame| {
122            // Perform the initial tick so tab index has a tree to work with.
123            // This means we can not react to any events in this tick as the tree does not
124            // yet have any widgets or components.
125            frame.tick(backend)?;
126
127            let mut tabindex = TabIndex::new(&mut frame.tabindex, frame.tree.view_mut());
128            tabindex.next();
129
130            if let Some(current) = frame.tabindex.as_ref() {
131                frame.with_component(current.widget_id, current.state_id, |comp, children, ctx| {
132                    comp.dyn_component.any_focus(children, ctx)
133                });
134            }
135
136            loop {
137                frame.tick(backend)?;
138                if frame.stop {
139                    return Err(Error::Stop);
140                }
141
142                frame.present(backend);
143                frame.cleanup();
144                std::thread::sleep(Duration::from_micros(sleep_micros));
145
146                if REBUILD.swap(false, Ordering::Relaxed) {
147                    frame.force_rebuild()?;
148                    backend.clear();
149                    break Ok(());
150                }
151            }
152        })?;
153
154        Ok(())
155    }
156
157    pub fn next_frame<'frame, 'bp>(
158        &'bp mut self,
159        tree: &'frame mut WidgetTree<'bp>,
160        attribute_storage: &'frame mut AttributeStorage<'bp>,
161    ) -> Result<Frame<'frame, 'bp, G>> {
162        let layout_ctx = LayoutCtx::new(
163            &self.globals,
164            &self.factory,
165            &mut self.states,
166            attribute_storage,
167            &mut self.components,
168            &mut self.component_registry,
169            &mut self.floating_widgets,
170            &mut self.glyph_map,
171            &mut self.viewport,
172        );
173
174        let inst = Frame {
175            document: &self.document,
176            blueprint: &self.blueprint,
177            tree,
178            layout_ctx,
179            changes: &mut self.changes,
180            sleep_micros: self.sleep_micros,
181
182            assoc_events: &mut self.assoc_events,
183            deferred_components: &mut self.deferred_components,
184
185            emitter: &self.emitter,
186            message_receiver: &self.message_receiver,
187
188            dt: &mut self.dt,
189            needs_layout: true,
190            stop: false,
191
192            global_event_handler: &self.global_event_handler,
193            tabindex: None,
194        };
195
196        Ok(inst)
197    }
198
199    pub(super) fn reload(&mut self) -> Result<()> {
200        clear_all_changes();
201        clear_all_subs();
202
203        self.components = Components::new();
204        self.floating_widgets = FloatingWidgets::empty();
205
206        // Reload templates
207        self.document.reload_templates()?;
208
209        let (blueprint, globals) = self.document.compile()?;
210        self.blueprint = blueprint;
211        self.globals = globals;
212
213        Ok(())
214    }
215}
216
217pub struct Frame<'rt, 'bp, G> {
218    document: &'bp Document,
219    blueprint: &'bp Blueprint,
220    pub tree: &'rt mut WidgetTree<'bp>,
221    deferred_components: &'rt mut DeferredComponents,
222    layout_ctx: LayoutCtx<'rt, 'bp>,
223    changes: &'rt mut Changes,
224    assoc_events: &'rt mut AssociatedEvents,
225    sleep_micros: u64,
226    emitter: &'rt Emitter,
227    message_receiver: &'rt flume::Receiver<ViewMessage>,
228    dt: &'rt mut Instant,
229    needs_layout: bool,
230    stop: bool,
231    global_event_handler: &'rt G,
232    pub tabindex: Option<Index>,
233}
234
235impl<'rt, 'bp, G: GlobalEventHandler> Frame<'rt, 'bp, G> {
236    pub fn force_rebuild(mut self) -> Result<()> {
237        // call unmount on all components
238        for i in 0..self.layout_ctx.components.len() {
239            let Some((widget_id, state_id)) = self.layout_ctx.components.get_ticking(i) else { continue };
240            let event = Event::Unmount;
241            self.send_event_to_component(event, widget_id, state_id);
242        }
243
244        self.return_state_and_component();
245        Ok(())
246    }
247
248    pub fn handle_global_event(&mut self, event: Event) -> Option<Event> {
249        let mut tabindex = TabIndex::new(&mut self.tabindex, self.tree.view_mut());
250
251        let event = self
252            .global_event_handler
253            .handle(event, &mut tabindex, self.deferred_components);
254
255        if tabindex.changed {
256            let prev = tabindex.consume();
257            if let Some(prev) = prev {
258                self.with_component(prev.widget_id, prev.state_id, |comp, children, ctx| {
259                    comp.dyn_component.any_blur(children, ctx)
260                });
261            }
262
263            if let Some(current) = self.tabindex.as_ref() {
264                self.with_component(current.widget_id, current.state_id, |comp, children, ctx| {
265                    comp.dyn_component.any_focus(children, ctx)
266                });
267            }
268        }
269
270        event
271    }
272
273    pub fn event(&mut self, event: Event) {
274        #[cfg(feature = "profile")]
275        puffin::profile_function!();
276
277        let Some(event) = self.handle_global_event(event) else { return };
278        if let Event::Stop = event {
279            self.stop = true;
280            return;
281        }
282
283        match event {
284            Event::Noop => (),
285            Event::Stop => todo!(),
286
287            // Component specific event
288            Event::Blur | Event::Focus | Event::Key(_) => {
289                let Some(Index {
290                    widget_id, state_id, ..
291                }) = self.tabindex
292                else {
293                    return;
294                };
295                self.send_event_to_component(event, widget_id, state_id);
296            }
297            Event::Mouse(_) | Event::Resize(_) => {
298                for i in 0..self.layout_ctx.components.len() {
299                    let Some((widget_id, state_id)) = self.layout_ctx.components.get(i) else { continue };
300                    self.send_event_to_component(event, widget_id, state_id);
301                }
302            }
303            Event::Tick(_) | Event::Mount | Event::Unmount => panic!("this event should never be sent to the runtime"),
304        }
305    }
306
307    // Should be called only once to initialise the node tree.
308    pub fn init_tree(&mut self) -> Result<()> {
309        let mut ctx = self.layout_ctx.eval_ctx(None);
310        eval_blueprint(
311            self.blueprint,
312            &mut ctx,
313            &Scope::root(),
314            root_node(),
315            &mut self.tree.view_mut(),
316        )?;
317
318        Ok(())
319    }
320
321    pub fn tick<B: Backend>(&mut self, backend: &mut B) -> Result<Duration> {
322        #[cfg(feature = "profile")]
323        puffin::GlobalProfiler::lock().new_frame();
324
325        let now = Instant::now();
326        self.cycle(backend)?;
327        self.init_new_components();
328        self.tick_components(self.dt.elapsed());
329        let elapsed = self.handle_messages(now);
330        self.poll_events(elapsed, now, backend);
331        self.drain_deferred_commands();
332        self.drain_assoc_events();
333        self.apply_changes()?;
334        // TODO: this secondary call is here to deal with changes causing changes
335        //       which happens when values are removed or inserted and indices needs updating
336        self.apply_changes()?;
337
338        *self.dt = Instant::now();
339
340        match self.layout_ctx.stop_runtime {
341            false => Ok(now.elapsed()),
342            true => Err(Error::Stop),
343        }
344    }
345
346    pub fn present<B: Backend>(&mut self, backend: &mut B) -> Duration {
347        #[cfg(feature = "profile")]
348        puffin::profile_function!();
349
350        let now = Instant::now();
351        backend.render(self.layout_ctx.glyph_map);
352        backend.clear();
353        now.elapsed()
354    }
355
356    pub fn cleanup(&mut self) {
357        #[cfg(feature = "profile")]
358        puffin::profile_function!();
359        for key in self.tree.drain_removed() {
360            self.layout_ctx.attribute_storage.try_remove(key);
361            self.layout_ctx.floating_widgets.try_remove(key);
362            self.layout_ctx.components.try_remove(key);
363            if let Some(Index { widget_id, .. }) = self.tabindex {
364                if widget_id == key {
365                    self.tabindex.take();
366                }
367            }
368        }
369    }
370
371    fn handle_messages(&mut self, fps_now: Instant) -> Duration {
372        #[cfg(feature = "profile")]
373        puffin::profile_function!();
374
375        while let Ok(msg) = self.message_receiver.try_recv() {
376            if let Some((widget_id, state_id)) = self
377                .layout_ctx
378                .components
379                .get_by_component_id(msg.recipient())
380                .map(|e| (e.widget_id, e.state_id))
381            {
382                self.with_component(widget_id, state_id, |comp, elements, ctx| {
383                    comp.dyn_component.any_message(elements, ctx, msg.payload())
384                });
385            }
386
387            // Make sure event handling isn't holding up the rest of the event loop.
388            if fps_now.elapsed().as_micros() as u64 >= self.sleep_micros / 2 {
389                break;
390            }
391        }
392
393        fps_now.elapsed()
394    }
395
396    fn poll_events<B: Backend>(&mut self, remaining: Duration, fps_now: Instant, backend: &mut B) {
397        while let Some(event) = backend.next_event(remaining) {
398            if let Event::Resize(size) = event {
399                self.layout_ctx.viewport.resize(size);
400                self.needs_layout = true;
401                backend.resize(size, self.layout_ctx.glyph_map);
402            }
403
404            self.event(event);
405
406            // Make sure event handling isn't holding up the rest of the event loop.
407            if fps_now.elapsed().as_micros() as u64 > self.sleep_micros {
408                break;
409            }
410        }
411    }
412
413    fn drain_deferred_commands(&mut self) {
414        #[cfg(feature = "profile")]
415        puffin::profile_function!();
416
417        // TODO: let's keep some memory around to drain this into instead of allocating
418        // a new vector every time.
419        // E.g `self.deferred_components.drain_into(&mut self.deferred_buffer)`
420        // Nb: Add drain_into to DeferredComponents
421        let commands = self.deferred_components.drain().collect::<Vec<_>>();
422        for mut cmd in commands {
423            for index in 0..self.layout_ctx.components.len() {
424                let Some((widget_id, state_id)) = self.layout_ctx.components.get(index) else { continue };
425                let Some(comp) = self.tree.get_ref(widget_id) else { continue };
426                let WidgetContainer {
427                    kind: WidgetKind::Component(comp),
428                    ..
429                } = comp
430                else {
431                    continue;
432                };
433                let attributes = self.layout_ctx.attribute_storage.get(widget_id);
434                if !cmd.filter_component(comp, attributes) {
435                    continue;
436                }
437
438                // -----------------------------------------------------------------------------
439                //   - Set focus -
440                //   TODO: here is another candidate for refactoring to make it
441                //   less cludgy and verbose.
442                // -----------------------------------------------------------------------------
443                // Blur the current component if the message is a `Focus` message
444                if let CommandKind::Focus = cmd.kind {
445                    // If this component current has focus ignore this command
446                    if let Some(index) = self.tabindex.as_ref() {
447                        if index.widget_id == widget_id {
448                            continue;
449                        }
450                    }
451
452                    // here we can find the component that should receive focus
453                    let new_index = self
454                        .with_component(widget_id, state_id, |comp, children, ctx| {
455                            if comp.dyn_component.any_accept_focus() {
456                                let index = Index {
457                                    path: children.parent_path().into(),
458                                    index: comp.tabindex,
459                                    widget_id,
460                                    state_id,
461                                };
462
463                                comp.dyn_component.any_focus(children, ctx);
464
465                                Some(index)
466                            } else {
467                                None
468                            }
469                        })
470                        .flatten();
471
472                    if let Some(index) = new_index {
473                        // If there is currently a component with focus that component
474                        // should only lose focus if the selected component accepts focus.
475                        if let Some(old) = self.tabindex.replace(index) {
476                            self.with_component(old.widget_id, old.state_id, |comp, children, ctx| {
477                                comp.dyn_component.any_blur(children, ctx)
478                            });
479                        }
480                    }
481                }
482
483                // -----------------------------------------------------------------------------
484                //   - Send message -
485                // -----------------------------------------------------------------------------
486                if let CommandKind::SendMessage(msg) = cmd.kind {
487                    self.with_component(widget_id, state_id, |comp, children, ctx| {
488                        comp.dyn_component.any_message(children, ctx, msg);
489                    });
490                }
491                break;
492            }
493        }
494    }
495
496    fn drain_assoc_events(&mut self) {
497        #[cfg(feature = "profile")]
498        puffin::profile_function!();
499
500        while let Some(assoc_event) = self.assoc_events.next() {
501            let mut parent = assoc_event.parent;
502            let external_ident = self.document.strings.get_ref_unchecked(assoc_event.external());
503            let internal_ident = self.document.strings.get_ref_unchecked(assoc_event.internal());
504            let sender = self.document.strings.get_ref_unchecked(assoc_event.sender);
505            let sender_id = assoc_event.sender_id;
506            let mut event = assoc_event.to_event(internal_ident, external_ident, sender, sender_id);
507
508            loop {
509                let Some((widget_id, state_id)) = self.layout_ctx.components.get_by_widget_id(parent.into()) else {
510                    return;
511                };
512
513                let stop_propagation = self
514                    .with_component(widget_id, state_id, |comp, children, ctx| {
515                        let next_parent = ctx.parent();
516                        comp.dyn_component.any_component_event(children, ctx, &mut event);
517
518                        parent = match next_parent {
519                            Some(p) => p,
520                            None => return true,
521                        };
522
523                        event.should_stop_propagation()
524                    })
525                    .unwrap_or(true);
526
527                if stop_propagation {
528                    break;
529                }
530            }
531        }
532    }
533
534    fn cycle<B: Backend>(&mut self, backend: &mut B) -> Result<()> {
535        #[cfg(feature = "profile")]
536        puffin::profile_function!();
537
538        let mut cycle = WidgetCycle::new(backend, self.tree.view_mut(), self.layout_ctx.viewport.constraints());
539        cycle.run(&mut self.layout_ctx, self.needs_layout)?;
540
541        self.needs_layout = false;
542        Ok(())
543    }
544
545    fn apply_changes(&mut self) -> Result<()> {
546        #[cfg(feature = "profile")]
547        puffin::profile_function!();
548
549        drain_changes(self.changes);
550
551        if self.changes.is_empty() {
552            return Ok(());
553        }
554
555        self.needs_layout = true;
556        let mut tree = self.tree.view_mut();
557
558        self.changes.iter().try_for_each(|(sub, change)| {
559            sub.iter().try_for_each(|value_id| {
560                let widget_id = value_id.key();
561
562                if let Some(widget) = tree.get_mut(widget_id) {
563                    let kind = &widget.kind;
564                    match kind {
565                        WidgetKind::Element(_element) => {}
566                        WidgetKind::For(_forloop) => {}
567                        WidgetKind::Iteration(_) => {}
568                        _ => (), // WidgetKind::ControlFlow(control_flow) => todo!(),
569                                 // WidgetKind::ControlFlowContainer(_) => todo!(),
570                                 // WidgetKind::Component(component) => todo!(),
571                                 // WidgetKind::Slot => todo!(),
572                    }
573                    if let WidgetKind::Element(element) = &mut widget.kind {
574                        element.invalidate_cache();
575                    }
576                }
577
578                // check that the node hasn't already been removed
579                if !tree.contains(widget_id) {
580                    return Result::Ok(());
581                }
582
583                tree.with_value_mut(widget_id, |_path, widget, tree| {
584                    update_widget(widget, value_id, change, tree, self.layout_ctx.attribute_storage)
585                })
586                .unwrap_or(Ok(()))?;
587
588                Ok(())
589            })?;
590
591            Result::Ok(())
592        })?;
593
594        self.changes.clear();
595
596        Ok(())
597    }
598
599    fn send_event_to_component(&mut self, event: Event, widget_id: WidgetId, state_id: StateId) {
600        self.with_component(widget_id, state_id, |comp, elements, ctx| {
601            comp.dyn_component.any_event(elements, ctx, event);
602        });
603    }
604
605    fn with_component<F, U>(&mut self, widget_id: WidgetId, state_id: StateId, f: F) -> Option<U>
606    where
607        F: FnOnce(&mut Component<'_>, Children<'_, '_>, AnyComponentContext<'_, '_>) -> U,
608    {
609        let mut tree = self.tree.view_mut();
610
611        tree.with_value_mut(widget_id, |_path, container, children| {
612            let WidgetKind::Component(component) = &mut container.kind else {
613                panic!("this is always a component")
614            };
615
616            let Some(state) = self.layout_ctx.states.get_mut(state_id) else {
617                panic!("a component always has a state")
618            };
619
620            self.layout_ctx
621                .attribute_storage
622                .with_mut(widget_id, |attributes, storage| {
623                    let elements = Children::new(children, storage, &mut self.needs_layout);
624
625                    let ctx = AnyComponentContext::new(
626                        component.parent.map(Into::into),
627                        component.name_id,
628                        widget_id,
629                        state_id,
630                        component.assoc_functions,
631                        self.assoc_events,
632                        self.deferred_components,
633                        attributes,
634                        Some(state),
635                        self.emitter,
636                        self.layout_ctx.viewport,
637                        &mut self.layout_ctx.stop_runtime,
638                        &self.document.strings,
639                    );
640
641                    f(component, elements, ctx)
642                })
643        })?
644    }
645
646    fn tick_components(&mut self, dt: Duration) {
647        #[cfg(feature = "profile")]
648        puffin::profile_function!();
649
650        for i in 0..self.layout_ctx.components.len() {
651            let Some((widget_id, state_id)) = self.layout_ctx.components.get_ticking(i) else { continue };
652            let event = Event::Tick(dt);
653            self.send_event_to_component(event, widget_id, state_id);
654        }
655    }
656
657    fn init_new_components(&mut self) {
658        while let Some((widget_id, state_id)) = self.layout_ctx.new_components.pop() {
659            self.with_component(widget_id, state_id, |comp, elements, ctx| {
660                comp.dyn_component.any_event(elements, ctx, Event::Mount);
661            });
662        }
663    }
664
665    // Return the state for each component back into the component registry
666    fn return_state_and_component(self) {
667        // Return all states
668        let mut tree = WidgetTree::empty();
669        std::mem::swap(&mut tree, self.tree);
670
671        for (_, widget) in tree.values().into_iter() {
672            let WidgetKind::Component(comp) = widget.kind else { continue };
673            let ComponentKind::Instance = comp.kind else { continue };
674            let state = self.layout_ctx.states.remove(comp.state_id).take();
675            self.layout_ctx
676                .component_registry
677                .return_component(comp.component_id, comp.dyn_component, state);
678        }
679    }
680
681    // fn display_error(&mut self, backend: &mut impl Backend) {
682    //     let _tpl = "text 'you goofed up'";
683    //     backend.render(self.layout_ctx.glyph_map);
684    // }
685}