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