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