anathema_runtime/runtime/
mod.rs

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