Skip to main content

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