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