raui_core/
application.rs

1//! Application foundation used to drive the RAUI interface
2//!
3//! An [`Application`] is the struct that pulls together all the pieces of a RAUI ui such as layout,
4//! interaction, animations, etc.
5//!
6//! In most cases users will not need to manually create and manage an [`Application`]. That will
7//! usually be handled by renderer integration crates like [`raui-tesselation-renderer`].
8//!
9//! [`raui-tesselation-renderer`]: https://docs.rs/raui-tesselation-renderer/
10//!
11//! You _will_ need to interact with [`Application`] if you are building your own RAUI integration
12//! with another renderer or game engine.
13//!
14//! # Example
15//!
16//! ```rust
17//! # use raui_core::prelude::*;
18//! // Create the application
19//! let mut application = Application::default();
20//!
21//! // We need to run the "setup" functions for the application to register components and
22//! // properties if we want to support serialization of the UI. We pass it a function that
23//! // will do the actual registration
24//! application.setup(setup /* the core setup function from the RAUI prelude */);
25//!
26//! // If we used RAUI material we would also want to call it's setup ( but we don't need
27//! // it here )
28//! // application.setup(raui_material::setup);
29//!
30//! // Create the renderer. In this case we use the raw renderer that will return raw
31//! // [`WidgetUnit`]'s, but usually you would have a custom renderer for your game
32//! // engine or renderer.
33//! let mut renderer = RawRenderer;
34//!
35//! // Create the interactions engine. The default interactions engine covers typical
36//! // pointer + keyboard + gamepad navigation/interactions.
37//! let mut interactions = DefaultInteractionsEngine::default();
38//!
39//! // We create our widget tree
40//! let tree = make_widget!(nav_content_box)
41//!     .key("app")
42//!     .listed_slot(make_widget!(button)
43//!         .key("button")
44//!         .with_props(NavItemActive)
45//!         .named_slot("content", make_widget!(image_box).key("icon"))
46//!     );
47//!
48//! // We apply the tree to the application. This must be done again if we wish to change the
49//! // tree.
50//! application.apply(tree);
51//!
52//! // This and the following function calls would need to be called every frame
53//! loop {
54//!     // Telling the app to `process` will make it perform any necessary updates.
55//!     application.process();
56//!
57//!     // To properly handle layout we need to create a mapping of the screen coordinates to
58//!     // the RAUI coordinates. We would update this with the size of the window every frame.
59//!     let mapping = CoordsMapping::new(Rect {
60//!         left: 0.0,
61//!         right: 1024.0,
62//!         top: 0.0,
63//!         bottom: 576.0,
64//!     });
65//!
66//!     // We apply the application layout
67//!     let mut layout_engine = DefaultLayoutEngine::<()>::default();
68//!     application
69//!         // We use the default layout engine, but you could make your own layout engine
70//!         .layout(&mapping, &mut layout_engine)
71//!         .unwrap();
72//!
73//!     // we interact with UI by sending interaction messages to the engine. You would hook this
74//!     // up to whatever game engine or window event loop to perform the proper interactions when
75//!     // different events are emitted.
76//!     interactions.interact(Interaction::PointerMove(Vec2 { x: 200.0, y: 100.0 }));
77//!     interactions.interact(Interaction::PointerDown(
78//!         PointerButton::Trigger,
79//!         Vec2 { x: 200.0, y: 100.0 },
80//!     ));
81//!
82//!     // Since interactions engines require constructed layout to process interactions we
83//!     // have to process interactions after we layout the UI.
84//!     application.interact(&mut interactions).unwrap();
85//!
86//!     // Now we render the app, printing it's raw widget units
87//!     println!("{:?}", application.render(&mapping, &mut renderer).unwrap());
88//! #   break;
89//! }
90//! ```
91
92use crate::{
93    Prefab, PrefabError, PrefabValue, Scalar,
94    animator::{AnimationUpdate, Animator, AnimatorStates},
95    interactive::InteractionsEngine,
96    layout::{CoordsMapping, Layout, LayoutEngine},
97    messenger::{Message, MessageData, MessageSender, Messages, Messenger},
98    prelude::ViewModelCollectionView,
99    props::{Props, PropsData, PropsRegistry},
100    renderer::Renderer,
101    signals::{Signal, SignalSender},
102    state::{State, StateChange, StateUpdate},
103    view_model::ViewModelCollection,
104    widget::{
105        FnWidget, WidgetId, WidgetIdCommon, WidgetLifeCycle,
106        component::{WidgetComponent, WidgetComponentPrefab},
107        context::{WidgetContext, WidgetMountOrChangeContext, WidgetUnmountContext},
108        node::{WidgetNode, WidgetNodePrefab},
109        unit::{
110            WidgetUnit, WidgetUnitNode, WidgetUnitNodePrefab,
111            area::{AreaBoxNode, AreaBoxNodePrefab},
112            content::{
113                ContentBoxItem, ContentBoxItemNode, ContentBoxItemNodePrefab, ContentBoxNode,
114                ContentBoxNodePrefab,
115            },
116            flex::{
117                FlexBoxItem, FlexBoxItemNode, FlexBoxItemNodePrefab, FlexBoxNode, FlexBoxNodePrefab,
118            },
119            grid::{
120                GridBoxItem, GridBoxItemNode, GridBoxItemNodePrefab, GridBoxNode, GridBoxNodePrefab,
121            },
122            image::{ImageBoxNode, ImageBoxNodePrefab},
123            portal::{
124                PortalBox, PortalBoxNode, PortalBoxNodePrefab, PortalBoxSlot, PortalBoxSlotNode,
125                PortalBoxSlotNodePrefab,
126            },
127            size::{SizeBoxNode, SizeBoxNodePrefab},
128            text::{TextBoxNode, TextBoxNodePrefab},
129        },
130    },
131};
132use std::{
133    borrow::Cow,
134    collections::{HashMap, HashSet},
135    convert::TryInto,
136    sync::{
137        Arc, RwLock,
138        mpsc::{Sender, channel},
139    },
140};
141
142/// Errors that can occur while interacting with an application
143#[derive(Debug, Clone)]
144pub enum ApplicationError {
145    Prefab(PrefabError),
146    ComponentMappingNotFound(String),
147}
148
149impl From<PrefabError> for ApplicationError {
150    fn from(error: PrefabError) -> Self {
151        Self::Prefab(error)
152    }
153}
154
155/// Indicates the reason that an [`Application`] state was invalidated and had to be re-rendered
156///
157/// You can get the last invalidation cause of an application using [`last_invalidation_cause`]
158///
159/// [`last_invalidation_cause`]: Application::last_invalidation_cause
160#[derive(Debug, Default, Clone)]
161pub enum InvalidationCause {
162    /// Application not invalidated
163    #[default]
164    None,
165    /// Application update caused by change in widgets common root.
166    CommonRootUpdate(WidgetIdCommon),
167}
168
169#[derive(Clone)]
170pub struct ChangeNotifier(Arc<RwLock<HashSet<WidgetId>>>);
171
172impl ChangeNotifier {
173    pub fn notify(&self, id: WidgetId) {
174        if let Ok(mut ids) = self.0.write() {
175            ids.insert(id);
176        }
177    }
178}
179
180/// Contains and orchestrates application layout, animations, interactions, etc.
181///
182/// See the [`application`][self] module for more information and examples.
183pub struct Application {
184    component_mappings: HashMap<String, FnWidget>,
185    props_registry: PropsRegistry,
186    tree: WidgetNode,
187    rendered_tree: WidgetUnit,
188    layout: Layout,
189    states: HashMap<WidgetId, Props>,
190    state_changes: HashMap<WidgetId, Vec<StateChange>>,
191    animators: HashMap<WidgetId, AnimatorStates>,
192    messages: HashMap<WidgetId, Messages>,
193    signals: Vec<Signal>,
194    pub view_models: ViewModelCollection,
195    changes: ChangeNotifier,
196    #[allow(clippy::type_complexity)]
197    unmount_closures: HashMap<WidgetId, Vec<Box<dyn FnMut(WidgetUnmountContext) + Send + Sync>>>,
198    dirty: WidgetIdCommon,
199    render_changed: bool,
200    last_invalidation_cause: InvalidationCause,
201    /// The amount of time between the last update, used when calculating animation progress
202    pub animations_delta_time: Scalar,
203}
204
205impl Default for Application {
206    fn default() -> Self {
207        Self {
208            component_mappings: Default::default(),
209            props_registry: Default::default(),
210            tree: Default::default(),
211            rendered_tree: Default::default(),
212            layout: Default::default(),
213            states: Default::default(),
214            state_changes: Default::default(),
215            animators: Default::default(),
216            messages: Default::default(),
217            signals: Default::default(),
218            view_models: Default::default(),
219            changes: ChangeNotifier(Default::default()),
220            unmount_closures: Default::default(),
221            dirty: Default::default(),
222            render_changed: false,
223            last_invalidation_cause: Default::default(),
224            animations_delta_time: 0.0,
225        }
226    }
227}
228
229impl Application {
230    /// Setup the application with a given a setup function
231    ///
232    /// We need to run the `setup` function for the application to register components and
233    /// properties if we want to support serialization of the UI. We pass it a function that will do
234    /// the actual registration.
235    ///
236    /// > **Note:** RAUI will work fine without running any `setup` if UI serialization is not
237    /// > required.
238    ///
239    /// # Example
240    ///
241    /// ```
242    /// # use raui_core::prelude::*;
243    /// # let mut application = Application::default();
244    /// application.setup(setup /* the core setup function from the RAUI prelude */);
245    /// ```
246    ///
247    /// If you use crates like the `raui_material` crate you will want to call it's setup function
248    /// as well.
249    ///
250    /// ```ignore
251    /// application.setup(raui_material::setup);
252    /// ```
253    #[inline]
254    pub fn setup<F>(&mut self, mut f: F)
255    where
256        F: FnMut(&mut Self),
257    {
258        (f)(self);
259    }
260
261    pub fn notifier(&self) -> ChangeNotifier {
262        self.changes.clone()
263    }
264
265    /// Register's a component under a string name used when serializing the UI
266    ///
267    /// This function is often used in [`setup`][Self::setup] functions for registering batches of
268    /// components.
269    ///
270    /// # Example
271    ///
272    /// ```
273    /// # use raui_core::prelude::*;
274    /// fn my_widget(ctx: WidgetContext) -> WidgetNode {
275    ///     todo!("make awesome widget");
276    /// }
277    ///
278    /// fn setup_widgets(app: &mut Application) {
279    ///     app.register_component("my_widget", FnWidget::pointer(my_widget));
280    /// }
281    ///
282    /// let mut application = Application::default();
283    ///
284    /// application.setup(setup_widgets);
285    /// ```
286    #[inline]
287    pub fn register_component(&mut self, type_name: &str, processor: FnWidget) {
288        self.component_mappings
289            .insert(type_name.to_owned(), processor);
290    }
291
292    /// Unregisters a component
293    ///
294    /// See [`register_component`][Self::register_component]
295    #[inline]
296    pub fn unregister_component(&mut self, type_name: &str) {
297        self.component_mappings.remove(type_name);
298    }
299
300    /// Register's a property type under a string name used when serializing the UI
301    ///
302    /// This function is often used in [`setup`][Self::setup] functions for registering batches of
303    /// properties.
304    ///
305    /// # Example
306    ///
307    /// ```
308    /// # use raui_core::prelude::*;
309    /// # use serde::{Serialize, Deserialize};
310    /// #[derive(PropsData, Debug, Default, Copy, Clone, Serialize, Deserialize)]
311    /// struct MyProp {
312    ///     awesome: bool,
313    /// }
314    ///
315    /// fn setup_properties(app: &mut Application) {
316    ///     app.register_props::<MyProp>("MyProp");
317    /// }
318    ///
319    /// let mut application = Application::default();
320    ///
321    /// application.setup(setup_properties);
322    /// ```
323    #[inline]
324    pub fn register_props<T>(&mut self, name: &str)
325    where
326        T: 'static + Prefab + PropsData,
327    {
328        self.props_registry.register_factory::<T>(name);
329    }
330
331    /// Unregisters a property type
332    ///
333    /// See [`register_props`][Self::register_props]
334    #[inline]
335    pub fn unregister_props(&mut self, name: &str) {
336        self.props_registry.unregister_factory(name);
337    }
338
339    /// Serialize the given [`Props`] to a [`PrefabValue`]
340    #[inline]
341    pub fn serialize_props(&self, props: &Props) -> Result<PrefabValue, PrefabError> {
342        self.props_registry.serialize(props)
343    }
344
345    /// Deserialize [`Props`] from a [`PrefabValue`]
346    #[inline]
347    pub fn deserialize_props(&self, data: PrefabValue) -> Result<Props, PrefabError> {
348        self.props_registry.deserialize(data)
349    }
350
351    /// Serialize a [`WidgetNode`] to a [`PrefabValue`]
352    #[inline]
353    pub fn serialize_node(&self, data: &WidgetNode) -> Result<PrefabValue, ApplicationError> {
354        Ok(self.node_to_prefab(data)?.to_prefab()?)
355    }
356
357    /// Deserialize a [`WidgetNode`] from a [`PrefabValue`]
358    #[inline]
359    pub fn deserialize_node(&self, data: PrefabValue) -> Result<WidgetNode, ApplicationError> {
360        self.node_from_prefab(WidgetNodePrefab::from_prefab(data)?)
361    }
362
363    /// Get the reason that the application state was last invalidated and caused to re-process
364    #[inline]
365    pub fn last_invalidation_cause(&self) -> &InvalidationCause {
366        &self.last_invalidation_cause
367    }
368
369    /// Return's common root widget ID of widgets that has to be to be re-processed
370    #[inline]
371    pub fn dirty(&self) -> &WidgetIdCommon {
372        &self.dirty
373    }
374
375    /// Force mark the application as needing to re-process its root
376    #[inline]
377    pub fn mark_dirty(&mut self) {
378        self.dirty = WidgetIdCommon::new(WidgetId::empty());
379    }
380
381    #[inline]
382    pub fn does_render_changed(&self) -> bool {
383        self.render_changed
384    }
385
386    /// Get the [`WidgetNode`] for the application tree
387    #[inline]
388    pub fn tree(&self) -> &WidgetNode {
389        &self.tree
390    }
391
392    /// Get the application widget tree rendered to raw [`WidgetUnit`]'s
393    #[inline]
394    pub fn rendered_tree(&self) -> &WidgetUnit {
395        &self.rendered_tree
396    }
397
398    /// Get the application [`Layout`] data
399    #[inline]
400    pub fn layout_data(&self) -> &Layout {
401        &self.layout
402    }
403
404    #[inline]
405    pub fn has_layout_widget(&self, id: &WidgetId) -> bool {
406        self.layout.items.keys().any(|k| k == id)
407    }
408
409    /// Update the application widget tree
410    #[inline]
411    pub fn apply(&mut self, tree: impl Into<WidgetNode>) {
412        self.mark_dirty();
413        self.tree = tree.into();
414    }
415
416    /// Render the application
417    #[inline]
418    pub fn render<R, T, E>(&self, mapping: &CoordsMapping, renderer: &mut R) -> Result<T, E>
419    where
420        R: Renderer<T, E>,
421    {
422        renderer.render(&self.rendered_tree, mapping, &self.layout)
423    }
424
425    /// Render the application, but only if something effecting the rendering has changed and it
426    /// _needs_ to be re-rendered
427    #[inline]
428    pub fn render_change<R, T, E>(
429        &mut self,
430        mapping: &CoordsMapping,
431        renderer: &mut R,
432    ) -> Result<Option<T>, E>
433    where
434        R: Renderer<T, E>,
435    {
436        if self.render_changed {
437            Ok(Some(self.render(mapping, renderer)?))
438        } else {
439            Ok(None)
440        }
441    }
442
443    /// Calculate application layout
444    #[inline]
445    pub fn layout<L, E>(&mut self, mapping: &CoordsMapping, layout_engine: &mut L) -> Result<(), E>
446    where
447        L: LayoutEngine<E>,
448    {
449        self.layout = layout_engine.layout(mapping, &self.rendered_tree)?;
450        Ok(())
451    }
452
453    /// Calculate application layout, but only if something effecting application layout has changed
454    /// and the layout _needs_ to be re-done
455    #[inline]
456    pub fn layout_change<L, E>(
457        &mut self,
458        mapping: &CoordsMapping,
459        layout_engine: &mut L,
460    ) -> Result<bool, E>
461    where
462        L: LayoutEngine<E>,
463    {
464        if self.render_changed {
465            self.layout(mapping, layout_engine)?;
466            Ok(true)
467        } else {
468            Ok(false)
469        }
470    }
471
472    /// Perform interactions on the application using the given interaction engine
473    #[inline]
474    pub fn interact<I, R, E>(&mut self, interactions_engine: &mut I) -> Result<R, E>
475    where
476        I: InteractionsEngine<R, E>,
477    {
478        interactions_engine.perform_interactions(self)
479    }
480
481    /// Send a message to the given widget
482    #[inline]
483    pub fn send_message<T>(&mut self, id: &WidgetId, data: T)
484    where
485        T: 'static + MessageData,
486    {
487        self.send_message_raw(id, Box::new(data));
488    }
489
490    /// Send raw message data to the given widget
491    #[inline]
492    pub fn send_message_raw(&mut self, id: &WidgetId, data: Message) {
493        if let Some(list) = self.messages.get_mut(id) {
494            list.push(data);
495        } else {
496            self.messages.insert(id.to_owned(), vec![data]);
497        }
498    }
499
500    /// Get the list of [signals][crate::signals] that have been sent by widgets
501    #[inline]
502    pub fn signals(&self) -> &[Signal] {
503        &self.signals
504    }
505
506    /// Get the list of [signals][crate::signals] that have been sent by widgets, consuming the
507    /// current list so that further calls will not include previously sent signals
508    #[inline]
509    pub fn consume_signals(&mut self) -> Vec<Signal> {
510        std::mem::take(&mut self.signals)
511    }
512
513    /// [`process()`][Self::process] application, even if no changes have been detected
514    #[inline]
515    pub fn forced_process(&mut self) -> bool {
516        self.mark_dirty();
517        self.process()
518    }
519
520    /// [Process][Self::process] the application.
521    ///
522    /// ## Example
523    ///
524    /// ```
525    /// # use raui_core::prelude::*;
526    /// const APP_DATA: &str = "app-data";
527    /// const COUNTER: &str = "counter";
528    ///
529    /// /// Some sort of application data.
530    /// struct AppData {
531    ///     counter: ViewModelValue<i32>,
532    /// }
533    ///
534    /// // Make view-model of that data.
535    /// let mut view_model = ViewModel::produce(|properties| {
536    ///     AppData {
537    ///         counter: ViewModelValue::new(0, properties.notifier(COUNTER)),
538    ///     }
539    /// });
540    ///
541    /// // Get handle to view-model data for access on host side.
542    /// // This handle is valid as long as it's view-model is alive.
543    /// let mut app_data = view_model.lazy::<AppData>().unwrap();
544    ///
545    /// let mut app = Application::default();
546    /// app.view_models.insert(APP_DATA.to_owned(), view_model);
547    /// // Do application stuff like interactions, layout, etc...
548    ///
549    /// // Now we call `process` with our process context
550    /// app.process();
551    /// ```
552    ///
553    /// Now, in our components we can access the `AppData` from view-model
554    /// through the widget's `WidgetContext`.
555    ///
556    /// ```
557    /// # use raui_core::prelude::*;
558    /// # struct AppData {
559    /// #    counter: ViewModelValue<i32>,
560    /// # }
561    /// # const APP_DATA: &str = "app-data";
562    /// # const COUNTER: &str = "counter";
563    /// fn my_component(mut ctx: WidgetContext) -> WidgetNode {
564    ///     let mut data = ctx
565    ///         .view_models
566    ///         .view_model_mut(APP_DATA)
567    ///         .unwrap()
568    ///         .write::<AppData>()
569    ///         .unwrap();
570    ///     *data.counter += 1;
571    ///
572    ///     // widget stuff...
573    /// #   Default::default()
574    /// }
575    /// ```
576    pub fn process(&mut self) -> bool {
577        self.dirty
578            .include_other(&self.view_models.consume_notified_common_root());
579        if let Ok(mut ids) = self.changes.0.write() {
580            for id in ids.drain() {
581                self.dirty.include(&id);
582            }
583        }
584        self.animations_delta_time = self.animations_delta_time.max(0.0);
585        self.last_invalidation_cause = InvalidationCause::None;
586        self.render_changed = false;
587        let changed_states = std::mem::take(&mut self.state_changes);
588        for id in changed_states.keys() {
589            self.dirty.include(id);
590        }
591        let mut messages = std::mem::take(&mut self.messages);
592        for id in messages.keys() {
593            self.dirty.include(id);
594        }
595        for (id, animator) in &self.animators {
596            if animator.in_progress() {
597                self.dirty.include(id);
598            }
599        }
600        if !self.dirty.is_valid() {
601            return false;
602        }
603        self.last_invalidation_cause = InvalidationCause::CommonRootUpdate(self.dirty.to_owned());
604        let (message_sender, message_receiver) = channel();
605        let message_sender = MessageSender::new(message_sender);
606        for (k, a) in &mut self.animators {
607            a.process(self.animations_delta_time, k, &message_sender);
608        }
609        let mut states = std::mem::take(&mut self.states);
610        for (id, changes) in changed_states {
611            let state = states.entry(id).or_default();
612            for change in changes {
613                match change {
614                    StateChange::Set(props) => {
615                        *state = props;
616                    }
617                    StateChange::Include(props) => {
618                        state.merge_from(props);
619                    }
620                    StateChange::Exclude(type_id) => unsafe {
621                        state.remove_by_type(type_id);
622                    },
623                }
624            }
625        }
626        let (signal_sender, signal_receiver) = channel();
627        let tree = self.tree.clone();
628        let mut used_ids = HashSet::new();
629        let mut new_states = HashMap::new();
630        let rendered_tree = self.process_node(
631            tree,
632            &states,
633            vec![],
634            &mut messages,
635            &mut new_states,
636            &mut used_ids,
637            "<*>".to_string(),
638            None,
639            &message_sender,
640            &signal_sender,
641        );
642        self.states = states
643            .into_iter()
644            .chain(new_states)
645            .filter(|(id, state)| {
646                if used_ids.contains(id) {
647                    true
648                } else {
649                    if let Some(closures) = self.unmount_closures.remove(id) {
650                        for mut closure in closures {
651                            let messenger = &message_sender;
652                            let signals = SignalSender::new(id.clone(), signal_sender.clone());
653                            let view_models =
654                                ViewModelCollectionView::new(id, &mut self.view_models);
655                            let context = WidgetUnmountContext {
656                                id,
657                                state,
658                                messenger,
659                                signals,
660                                view_models,
661                            };
662                            (closure)(context);
663                        }
664                    }
665                    self.animators.remove(id);
666                    self.view_models.unbind_all(id);
667                    self.view_models.remove_widget_view_models(id);
668                    false
669                }
670            })
671            .collect();
672        while let Ok((id, message)) = message_receiver.try_recv() {
673            if let Some(list) = self.messages.get_mut(&id) {
674                list.push(message);
675            } else {
676                self.messages.insert(id, vec![message]);
677            }
678        }
679        self.signals.clear();
680        while let Ok(data) = signal_receiver.try_recv() {
681            self.signals.push(data);
682        }
683        self.animators = std::mem::take(&mut self.animators)
684            .into_iter()
685            .filter_map(|(k, a)| if a.in_progress() { Some((k, a)) } else { None })
686            .collect();
687        self.dirty = Default::default();
688        if let Ok(tree) = rendered_tree.try_into() {
689            self.rendered_tree = Self::teleport_portals(tree);
690            true
691        } else {
692            false
693        }
694    }
695
696    #[allow(clippy::too_many_arguments)]
697    fn process_node(
698        &mut self,
699        node: WidgetNode,
700        states: &HashMap<WidgetId, Props>,
701        path: Vec<Cow<'static, str>>,
702        messages: &mut HashMap<WidgetId, Messages>,
703        new_states: &mut HashMap<WidgetId, Props>,
704        used_ids: &mut HashSet<WidgetId>,
705        possible_key: String,
706        master_shared_props: Option<Props>,
707        message_sender: &MessageSender,
708        signal_sender: &Sender<Signal>,
709    ) -> WidgetNode {
710        match node {
711            WidgetNode::None | WidgetNode::Tuple(_) => node,
712            WidgetNode::Component(component) => self.process_node_component(
713                component,
714                states,
715                path,
716                messages,
717                new_states,
718                used_ids,
719                possible_key,
720                master_shared_props,
721                message_sender,
722                signal_sender,
723            ),
724            WidgetNode::Unit(unit) => self.process_node_unit(
725                unit,
726                states,
727                path,
728                messages,
729                new_states,
730                used_ids,
731                master_shared_props,
732                message_sender,
733                signal_sender,
734            ),
735        }
736    }
737
738    #[allow(clippy::too_many_arguments)]
739    fn process_node_component(
740        &mut self,
741        component: WidgetComponent,
742        states: &HashMap<WidgetId, Props>,
743        mut path: Vec<Cow<'static, str>>,
744        messages: &mut HashMap<WidgetId, Messages>,
745        new_states: &mut HashMap<WidgetId, Props>,
746        used_ids: &mut HashSet<WidgetId>,
747        possible_key: String,
748        master_shared_props: Option<Props>,
749        message_sender: &MessageSender,
750        signal_sender: &Sender<Signal>,
751    ) -> WidgetNode {
752        let WidgetComponent {
753            processor,
754            type_name,
755            key,
756            mut idref,
757            mut props,
758            shared_props,
759            listed_slots,
760            named_slots,
761        } = component;
762        let mut shared_props = match (master_shared_props, shared_props) {
763            (Some(master_shared_props), Some(shared_props)) => {
764                master_shared_props.merge(shared_props)
765            }
766            (None, Some(shared_props)) => shared_props,
767            (Some(master_shared_props), None) => master_shared_props,
768            _ => Default::default(),
769        };
770        let key = match &key {
771            Some(key) => key.to_owned(),
772            None => possible_key.to_owned(),
773        };
774        path.push(key.clone().into());
775        let id = WidgetId::new(&type_name, &path);
776        used_ids.insert(id.clone());
777        if let Some(idref) = &mut idref {
778            idref.write(id.to_owned());
779        }
780        let (state_sender, state_receiver) = channel();
781        let (animation_sender, animation_receiver) = channel();
782        let messages_list = messages.remove(&id).unwrap_or_default();
783        let mut life_cycle = WidgetLifeCycle::default();
784        let default_animator_state = AnimatorStates::default();
785        let (new_node, mounted) = match states.get(&id) {
786            Some(state) => {
787                let state = State::new(state, StateUpdate::new(state_sender.clone()));
788                let animator = self.animators.get(&id).unwrap_or(&default_animator_state);
789                let view_models = ViewModelCollectionView::new(&id, &mut self.view_models);
790                let context = WidgetContext {
791                    id: &id,
792                    idref: idref.as_ref(),
793                    key: &key,
794                    props: &mut props,
795                    shared_props: &mut shared_props,
796                    state,
797                    animator,
798                    life_cycle: &mut life_cycle,
799                    named_slots,
800                    listed_slots,
801                    view_models,
802                };
803                (processor.call(context), false)
804            }
805            None => {
806                let state_data = Props::default();
807                let state = State::new(&state_data, StateUpdate::new(state_sender.clone()));
808                let animator = self.animators.get(&id).unwrap_or(&default_animator_state);
809                let view_models = ViewModelCollectionView::new(&id, &mut self.view_models);
810                let context = WidgetContext {
811                    id: &id,
812                    idref: idref.as_ref(),
813                    key: &key,
814                    props: &mut props,
815                    shared_props: &mut shared_props,
816                    state,
817                    animator,
818                    life_cycle: &mut life_cycle,
819                    named_slots,
820                    listed_slots,
821                    view_models,
822                };
823                let node = processor.call(context);
824                new_states.insert(id.clone(), state_data);
825                (node, true)
826            }
827        };
828        let (mount, change, unmount) = life_cycle.unwrap();
829        if mounted {
830            if !mount.is_empty() {
831                if let Some(state) = new_states.get(&id) {
832                    for mut closure in mount {
833                        let state = State::new(state, StateUpdate::new(state_sender.clone()));
834                        let messenger = Messenger::new(message_sender.clone(), &messages_list);
835                        let signals = SignalSender::new(id.clone(), signal_sender.clone());
836                        let animator = Animator::new(
837                            self.animators.get(&id).unwrap_or(&default_animator_state),
838                            AnimationUpdate::new(animation_sender.clone()),
839                        );
840                        let view_models = ViewModelCollectionView::new(&id, &mut self.view_models);
841                        let context = WidgetMountOrChangeContext {
842                            id: &id,
843                            props: &props,
844                            shared_props: &shared_props,
845                            state,
846                            messenger,
847                            signals,
848                            animator,
849                            view_models,
850                        };
851                        (closure)(context);
852                    }
853                }
854            }
855        } else if !change.is_empty() {
856            if let Some(state) = states.get(&id) {
857                for mut closure in change {
858                    let state = State::new(state, StateUpdate::new(state_sender.clone()));
859                    let messenger = Messenger::new(message_sender.clone(), &messages_list);
860                    let signals = SignalSender::new(id.clone(), signal_sender.clone());
861                    let animator = Animator::new(
862                        self.animators.get(&id).unwrap_or(&default_animator_state),
863                        AnimationUpdate::new(animation_sender.clone()),
864                    );
865                    let view_models = ViewModelCollectionView::new(&id, &mut self.view_models);
866                    let context = WidgetMountOrChangeContext {
867                        id: &id,
868                        props: &props,
869                        shared_props: &shared_props,
870                        state,
871                        messenger,
872                        signals,
873                        animator,
874                        view_models,
875                    };
876                    (closure)(context);
877                }
878            }
879        }
880        if !unmount.is_empty() {
881            self.unmount_closures.insert(id.clone(), unmount);
882        }
883        while let Ok((name, data)) = animation_receiver.try_recv() {
884            if let Some(states) = self.animators.get_mut(&id) {
885                states.change(name, data);
886            } else if let Some(data) = data {
887                self.animators
888                    .insert(id.to_owned(), AnimatorStates::new(name, data));
889            }
890        }
891        let new_node = self.process_node(
892            new_node,
893            states,
894            path,
895            messages,
896            new_states,
897            used_ids,
898            possible_key,
899            Some(shared_props),
900            message_sender,
901            signal_sender,
902        );
903        while let Ok(data) = state_receiver.try_recv() {
904            self.state_changes
905                .entry(id.to_owned())
906                .or_default()
907                .push(data);
908        }
909        new_node
910    }
911
912    #[allow(clippy::too_many_arguments)]
913    fn process_node_unit(
914        &mut self,
915        mut unit: WidgetUnitNode,
916        states: &HashMap<WidgetId, Props>,
917        path: Vec<Cow<'static, str>>,
918        messages: &mut HashMap<WidgetId, Messages>,
919        new_states: &mut HashMap<WidgetId, Props>,
920        used_ids: &mut HashSet<WidgetId>,
921        master_shared_props: Option<Props>,
922        message_sender: &MessageSender,
923        signal_sender: &Sender<Signal>,
924    ) -> WidgetNode {
925        match &mut unit {
926            WidgetUnitNode::None | WidgetUnitNode::ImageBox(_) | WidgetUnitNode::TextBox(_) => {}
927            WidgetUnitNode::AreaBox(unit) => {
928                let slot = *std::mem::take(&mut unit.slot);
929                unit.slot = Box::new(self.process_node(
930                    slot,
931                    states,
932                    path,
933                    messages,
934                    new_states,
935                    used_ids,
936                    ".".to_owned(),
937                    master_shared_props,
938                    message_sender,
939                    signal_sender,
940                ));
941            }
942            WidgetUnitNode::PortalBox(unit) => match &mut *unit.slot {
943                PortalBoxSlotNode::Slot(data) => {
944                    let slot = std::mem::take(data);
945                    *data = self.process_node(
946                        slot,
947                        states,
948                        path,
949                        messages,
950                        new_states,
951                        used_ids,
952                        ".".to_owned(),
953                        master_shared_props,
954                        message_sender,
955                        signal_sender,
956                    )
957                }
958                PortalBoxSlotNode::ContentItem(item) => {
959                    let slot = std::mem::take(&mut item.slot);
960                    item.slot = self.process_node(
961                        slot,
962                        states,
963                        path,
964                        messages,
965                        new_states,
966                        used_ids,
967                        ".".to_owned(),
968                        master_shared_props,
969                        message_sender,
970                        signal_sender,
971                    )
972                }
973                PortalBoxSlotNode::FlexItem(item) => {
974                    let slot = std::mem::take(&mut item.slot);
975                    item.slot = self.process_node(
976                        slot,
977                        states,
978                        path,
979                        messages,
980                        new_states,
981                        used_ids,
982                        ".".to_owned(),
983                        master_shared_props,
984                        message_sender,
985                        signal_sender,
986                    )
987                }
988                PortalBoxSlotNode::GridItem(item) => {
989                    let slot = std::mem::take(&mut item.slot);
990                    item.slot = self.process_node(
991                        slot,
992                        states,
993                        path,
994                        messages,
995                        new_states,
996                        used_ids,
997                        ".".to_owned(),
998                        master_shared_props,
999                        message_sender,
1000                        signal_sender,
1001                    )
1002                }
1003            },
1004            WidgetUnitNode::ContentBox(unit) => {
1005                let items = std::mem::take(&mut unit.items);
1006                unit.items = items
1007                    .into_iter()
1008                    .enumerate()
1009                    .map(|(i, mut node)| {
1010                        let slot = std::mem::take(&mut node.slot);
1011                        node.slot = self.process_node(
1012                            slot,
1013                            states,
1014                            path.clone(),
1015                            messages,
1016                            new_states,
1017                            used_ids,
1018                            format!("<{}>", i),
1019                            master_shared_props.clone(),
1020                            message_sender,
1021                            signal_sender,
1022                        );
1023                        node
1024                    })
1025                    .collect::<Vec<_>>();
1026            }
1027            WidgetUnitNode::FlexBox(unit) => {
1028                let items = std::mem::take(&mut unit.items);
1029                unit.items = items
1030                    .into_iter()
1031                    .enumerate()
1032                    .map(|(i, mut node)| {
1033                        let slot = std::mem::take(&mut node.slot);
1034                        node.slot = self.process_node(
1035                            slot,
1036                            states,
1037                            path.clone(),
1038                            messages,
1039                            new_states,
1040                            used_ids,
1041                            format!("<{}>", i),
1042                            master_shared_props.clone(),
1043                            message_sender,
1044                            signal_sender,
1045                        );
1046                        node
1047                    })
1048                    .collect::<Vec<_>>();
1049            }
1050            WidgetUnitNode::GridBox(unit) => {
1051                let items = std::mem::take(&mut unit.items);
1052                unit.items = items
1053                    .into_iter()
1054                    .enumerate()
1055                    .map(|(i, mut node)| {
1056                        let slot = std::mem::take(&mut node.slot);
1057                        node.slot = self.process_node(
1058                            slot,
1059                            states,
1060                            path.clone(),
1061                            messages,
1062                            new_states,
1063                            used_ids,
1064                            format!("<{}>", i),
1065                            master_shared_props.clone(),
1066                            message_sender,
1067                            signal_sender,
1068                        );
1069                        node
1070                    })
1071                    .collect::<Vec<_>>();
1072            }
1073            WidgetUnitNode::SizeBox(unit) => {
1074                let slot = *std::mem::take(&mut unit.slot);
1075                unit.slot = Box::new(self.process_node(
1076                    slot,
1077                    states,
1078                    path,
1079                    messages,
1080                    new_states,
1081                    used_ids,
1082                    ".".to_owned(),
1083                    master_shared_props,
1084                    message_sender,
1085                    signal_sender,
1086                ));
1087            }
1088        }
1089        unit.into()
1090    }
1091
1092    fn teleport_portals(mut root: WidgetUnit) -> WidgetUnit {
1093        let count = Self::estimate_portals(&root);
1094        if count == 0 {
1095            return root;
1096        }
1097        let mut portals = Vec::with_capacity(count);
1098        Self::consume_portals(&mut root, &mut portals);
1099        Self::inject_portals(&mut root, &mut portals);
1100        root
1101    }
1102
1103    fn estimate_portals(unit: &WidgetUnit) -> usize {
1104        let mut count = 0;
1105        match unit {
1106            WidgetUnit::None | WidgetUnit::ImageBox(_) | WidgetUnit::TextBox(_) => {}
1107            WidgetUnit::AreaBox(b) => count += Self::estimate_portals(&b.slot),
1108            WidgetUnit::PortalBox(b) => {
1109                count += Self::estimate_portals(match &*b.slot {
1110                    PortalBoxSlot::Slot(slot) => slot,
1111                    PortalBoxSlot::ContentItem(item) => &item.slot,
1112                    PortalBoxSlot::FlexItem(item) => &item.slot,
1113                    PortalBoxSlot::GridItem(item) => &item.slot,
1114                }) + 1
1115            }
1116            WidgetUnit::ContentBox(b) => {
1117                for item in &b.items {
1118                    count += Self::estimate_portals(&item.slot);
1119                }
1120            }
1121            WidgetUnit::FlexBox(b) => {
1122                for item in &b.items {
1123                    count += Self::estimate_portals(&item.slot);
1124                }
1125            }
1126            WidgetUnit::GridBox(b) => {
1127                for item in &b.items {
1128                    count += Self::estimate_portals(&item.slot);
1129                }
1130            }
1131            WidgetUnit::SizeBox(b) => count += Self::estimate_portals(&b.slot),
1132        }
1133        count
1134    }
1135
1136    fn consume_portals(unit: &mut WidgetUnit, bucket: &mut Vec<(WidgetId, PortalBoxSlot)>) {
1137        match unit {
1138            WidgetUnit::None | WidgetUnit::ImageBox(_) | WidgetUnit::TextBox(_) => {}
1139            WidgetUnit::AreaBox(b) => Self::consume_portals(&mut b.slot, bucket),
1140            WidgetUnit::PortalBox(b) => {
1141                let PortalBox {
1142                    owner, mut slot, ..
1143                } = std::mem::take(b);
1144                Self::consume_portals(
1145                    match &mut *slot {
1146                        PortalBoxSlot::Slot(slot) => slot,
1147                        PortalBoxSlot::ContentItem(item) => &mut item.slot,
1148                        PortalBoxSlot::FlexItem(item) => &mut item.slot,
1149                        PortalBoxSlot::GridItem(item) => &mut item.slot,
1150                    },
1151                    bucket,
1152                );
1153                bucket.push((owner, *slot));
1154            }
1155            WidgetUnit::ContentBox(b) => {
1156                for item in &mut b.items {
1157                    Self::consume_portals(&mut item.slot, bucket);
1158                }
1159            }
1160            WidgetUnit::FlexBox(b) => {
1161                for item in &mut b.items {
1162                    Self::consume_portals(&mut item.slot, bucket);
1163                }
1164            }
1165            WidgetUnit::GridBox(b) => {
1166                for item in &mut b.items {
1167                    Self::consume_portals(&mut item.slot, bucket);
1168                }
1169            }
1170            WidgetUnit::SizeBox(b) => Self::consume_portals(&mut b.slot, bucket),
1171        }
1172    }
1173
1174    fn inject_portals(unit: &mut WidgetUnit, portals: &mut Vec<(WidgetId, PortalBoxSlot)>) -> bool {
1175        if portals.is_empty() {
1176            return false;
1177        }
1178        while let Some(data) = unit.as_data() {
1179            let found = portals.iter().position(|(id, _)| data.id() == id);
1180            if let Some(index) = found {
1181                let slot = portals.swap_remove(index).1;
1182                match unit {
1183                    WidgetUnit::None
1184                    | WidgetUnit::PortalBox(_)
1185                    | WidgetUnit::ImageBox(_)
1186                    | WidgetUnit::TextBox(_) => {}
1187                    WidgetUnit::AreaBox(b) => {
1188                        match slot {
1189                            PortalBoxSlot::Slot(slot) => b.slot = Box::new(slot),
1190                            PortalBoxSlot::ContentItem(item) => b.slot = Box::new(item.slot),
1191                            PortalBoxSlot::FlexItem(item) => b.slot = Box::new(item.slot),
1192                            PortalBoxSlot::GridItem(item) => b.slot = Box::new(item.slot),
1193                        }
1194                        if !Self::inject_portals(&mut b.slot, portals) {
1195                            return false;
1196                        }
1197                    }
1198                    WidgetUnit::ContentBox(b) => {
1199                        b.items.push(match slot {
1200                            PortalBoxSlot::Slot(slot) => ContentBoxItem {
1201                                slot,
1202                                ..Default::default()
1203                            },
1204                            PortalBoxSlot::ContentItem(item) => item,
1205                            PortalBoxSlot::FlexItem(item) => ContentBoxItem {
1206                                slot: item.slot,
1207                                ..Default::default()
1208                            },
1209                            PortalBoxSlot::GridItem(item) => ContentBoxItem {
1210                                slot: item.slot,
1211                                ..Default::default()
1212                            },
1213                        });
1214                        for item in &mut b.items {
1215                            if !Self::inject_portals(&mut item.slot, portals) {
1216                                return false;
1217                            }
1218                        }
1219                    }
1220                    WidgetUnit::FlexBox(b) => {
1221                        b.items.push(match slot {
1222                            PortalBoxSlot::Slot(slot) => FlexBoxItem {
1223                                slot,
1224                                ..Default::default()
1225                            },
1226                            PortalBoxSlot::ContentItem(item) => FlexBoxItem {
1227                                slot: item.slot,
1228                                ..Default::default()
1229                            },
1230                            PortalBoxSlot::FlexItem(item) => item,
1231                            PortalBoxSlot::GridItem(item) => FlexBoxItem {
1232                                slot: item.slot,
1233                                ..Default::default()
1234                            },
1235                        });
1236                        for item in &mut b.items {
1237                            if !Self::inject_portals(&mut item.slot, portals) {
1238                                return false;
1239                            }
1240                        }
1241                    }
1242                    WidgetUnit::GridBox(b) => {
1243                        b.items.push(match slot {
1244                            PortalBoxSlot::Slot(slot) => GridBoxItem {
1245                                slot,
1246                                ..Default::default()
1247                            },
1248                            PortalBoxSlot::ContentItem(item) => GridBoxItem {
1249                                slot: item.slot,
1250                                ..Default::default()
1251                            },
1252                            PortalBoxSlot::FlexItem(item) => GridBoxItem {
1253                                slot: item.slot,
1254                                ..Default::default()
1255                            },
1256                            PortalBoxSlot::GridItem(item) => item,
1257                        });
1258                        for item in &mut b.items {
1259                            if !Self::inject_portals(&mut item.slot, portals) {
1260                                return false;
1261                            }
1262                        }
1263                    }
1264                    WidgetUnit::SizeBox(b) => {
1265                        match slot {
1266                            PortalBoxSlot::Slot(slot) => b.slot = Box::new(slot),
1267                            PortalBoxSlot::ContentItem(item) => b.slot = Box::new(item.slot),
1268                            PortalBoxSlot::FlexItem(item) => b.slot = Box::new(item.slot),
1269                            PortalBoxSlot::GridItem(item) => b.slot = Box::new(item.slot),
1270                        }
1271                        if !Self::inject_portals(&mut b.slot, portals) {
1272                            return false;
1273                        }
1274                    }
1275                }
1276            } else {
1277                break;
1278            }
1279        }
1280        true
1281    }
1282
1283    fn node_to_prefab(&self, data: &WidgetNode) -> Result<WidgetNodePrefab, ApplicationError> {
1284        Ok(match data {
1285            WidgetNode::None => WidgetNodePrefab::None,
1286            WidgetNode::Component(data) => {
1287                WidgetNodePrefab::Component(self.component_to_prefab(data)?)
1288            }
1289            WidgetNode::Unit(data) => WidgetNodePrefab::Unit(self.unit_to_prefab(data)?),
1290            WidgetNode::Tuple(data) => WidgetNodePrefab::Tuple(self.tuple_to_prefab(data)?),
1291        })
1292    }
1293
1294    fn component_to_prefab(
1295        &self,
1296        data: &WidgetComponent,
1297    ) -> Result<WidgetComponentPrefab, ApplicationError> {
1298        if self.component_mappings.contains_key(&data.type_name) {
1299            Ok(WidgetComponentPrefab {
1300                type_name: data.type_name.to_owned(),
1301                key: data.key.clone(),
1302                props: self.props_registry.serialize(&data.props)?,
1303                shared_props: match &data.shared_props {
1304                    Some(p) => Some(self.props_registry.serialize(p)?),
1305                    None => None,
1306                },
1307                listed_slots: data
1308                    .listed_slots
1309                    .iter()
1310                    .map(|v| self.node_to_prefab(v))
1311                    .collect::<Result<_, _>>()?,
1312                named_slots: data
1313                    .named_slots
1314                    .iter()
1315                    .map(|(k, v)| Ok((k.to_owned(), self.node_to_prefab(v)?)))
1316                    .collect::<Result<_, ApplicationError>>()?,
1317            })
1318        } else {
1319            Err(ApplicationError::ComponentMappingNotFound(
1320                data.type_name.to_owned(),
1321            ))
1322        }
1323    }
1324
1325    fn unit_to_prefab(
1326        &self,
1327        data: &WidgetUnitNode,
1328    ) -> Result<WidgetUnitNodePrefab, ApplicationError> {
1329        Ok(match data {
1330            WidgetUnitNode::None => WidgetUnitNodePrefab::None,
1331            WidgetUnitNode::AreaBox(data) => {
1332                WidgetUnitNodePrefab::AreaBox(self.area_box_to_prefab(data)?)
1333            }
1334            WidgetUnitNode::PortalBox(data) => {
1335                WidgetUnitNodePrefab::PortalBox(self.portal_box_to_prefab(data)?)
1336            }
1337            WidgetUnitNode::ContentBox(data) => {
1338                WidgetUnitNodePrefab::ContentBox(self.content_box_to_prefab(data)?)
1339            }
1340            WidgetUnitNode::FlexBox(data) => {
1341                WidgetUnitNodePrefab::FlexBox(self.flex_box_to_prefab(data)?)
1342            }
1343            WidgetUnitNode::GridBox(data) => {
1344                WidgetUnitNodePrefab::GridBox(self.grid_box_to_prefab(data)?)
1345            }
1346            WidgetUnitNode::SizeBox(data) => {
1347                WidgetUnitNodePrefab::SizeBox(self.size_box_to_prefab(data)?)
1348            }
1349            WidgetUnitNode::ImageBox(data) => {
1350                WidgetUnitNodePrefab::ImageBox(self.image_box_to_prefab(data)?)
1351            }
1352            WidgetUnitNode::TextBox(data) => {
1353                WidgetUnitNodePrefab::TextBox(self.text_box_to_prefab(data)?)
1354            }
1355        })
1356    }
1357
1358    fn tuple_to_prefab(
1359        &self,
1360        data: &[WidgetNode],
1361    ) -> Result<Vec<WidgetNodePrefab>, ApplicationError> {
1362        data.iter()
1363            .map(|node| self.node_to_prefab(node))
1364            .collect::<Result<_, _>>()
1365    }
1366
1367    fn area_box_to_prefab(
1368        &self,
1369        data: &AreaBoxNode,
1370    ) -> Result<AreaBoxNodePrefab, ApplicationError> {
1371        Ok(AreaBoxNodePrefab {
1372            id: data.id.to_owned(),
1373            slot: Box::new(self.node_to_prefab(&data.slot)?),
1374        })
1375    }
1376
1377    fn portal_box_to_prefab(
1378        &self,
1379        data: &PortalBoxNode,
1380    ) -> Result<PortalBoxNodePrefab, ApplicationError> {
1381        Ok(PortalBoxNodePrefab {
1382            id: data.id.to_owned(),
1383            slot: Box::new(match &*data.slot {
1384                PortalBoxSlotNode::Slot(slot) => {
1385                    PortalBoxSlotNodePrefab::Slot(self.node_to_prefab(slot)?)
1386                }
1387                PortalBoxSlotNode::ContentItem(item) => {
1388                    PortalBoxSlotNodePrefab::ContentItem(ContentBoxItemNodePrefab {
1389                        slot: self.node_to_prefab(&item.slot)?,
1390                        layout: item.layout.clone(),
1391                    })
1392                }
1393                PortalBoxSlotNode::FlexItem(item) => {
1394                    PortalBoxSlotNodePrefab::FlexItem(FlexBoxItemNodePrefab {
1395                        slot: self.node_to_prefab(&item.slot)?,
1396                        layout: item.layout.clone(),
1397                    })
1398                }
1399                PortalBoxSlotNode::GridItem(item) => {
1400                    PortalBoxSlotNodePrefab::GridItem(GridBoxItemNodePrefab {
1401                        slot: self.node_to_prefab(&item.slot)?,
1402                        layout: item.layout.clone(),
1403                    })
1404                }
1405            }),
1406            owner: data.owner.to_owned(),
1407        })
1408    }
1409
1410    fn content_box_to_prefab(
1411        &self,
1412        data: &ContentBoxNode,
1413    ) -> Result<ContentBoxNodePrefab, ApplicationError> {
1414        Ok(ContentBoxNodePrefab {
1415            id: data.id.to_owned(),
1416            props: self.props_registry.serialize(&data.props)?,
1417            items: data
1418                .items
1419                .iter()
1420                .map(|v| {
1421                    Ok(ContentBoxItemNodePrefab {
1422                        slot: self.node_to_prefab(&v.slot)?,
1423                        layout: v.layout.clone(),
1424                    })
1425                })
1426                .collect::<Result<_, ApplicationError>>()?,
1427            clipping: data.clipping,
1428            transform: data.transform,
1429        })
1430    }
1431
1432    fn flex_box_to_prefab(
1433        &self,
1434        data: &FlexBoxNode,
1435    ) -> Result<FlexBoxNodePrefab, ApplicationError> {
1436        Ok(FlexBoxNodePrefab {
1437            id: data.id.to_owned(),
1438            props: self.props_registry.serialize(&data.props)?,
1439            items: data
1440                .items
1441                .iter()
1442                .map(|v| {
1443                    Ok(FlexBoxItemNodePrefab {
1444                        slot: self.node_to_prefab(&v.slot)?,
1445                        layout: v.layout.clone(),
1446                    })
1447                })
1448                .collect::<Result<_, ApplicationError>>()?,
1449            direction: data.direction,
1450            separation: data.separation,
1451            wrap: data.wrap,
1452            transform: data.transform,
1453        })
1454    }
1455
1456    fn grid_box_to_prefab(
1457        &self,
1458        data: &GridBoxNode,
1459    ) -> Result<GridBoxNodePrefab, ApplicationError> {
1460        Ok(GridBoxNodePrefab {
1461            id: data.id.to_owned(),
1462            props: self.props_registry.serialize(&data.props)?,
1463            items: data
1464                .items
1465                .iter()
1466                .map(|v| {
1467                    Ok(GridBoxItemNodePrefab {
1468                        slot: self.node_to_prefab(&v.slot)?,
1469                        layout: v.layout.clone(),
1470                    })
1471                })
1472                .collect::<Result<_, ApplicationError>>()?,
1473            cols: data.cols,
1474            rows: data.rows,
1475            transform: data.transform,
1476        })
1477    }
1478
1479    fn size_box_to_prefab(
1480        &self,
1481        data: &SizeBoxNode,
1482    ) -> Result<SizeBoxNodePrefab, ApplicationError> {
1483        Ok(SizeBoxNodePrefab {
1484            id: data.id.to_owned(),
1485            props: self.props_registry.serialize(&data.props)?,
1486            slot: Box::new(self.node_to_prefab(&data.slot)?),
1487            width: data.width,
1488            height: data.height,
1489            margin: data.margin,
1490            keep_aspect_ratio: data.keep_aspect_ratio,
1491            transform: data.transform,
1492        })
1493    }
1494
1495    fn image_box_to_prefab(
1496        &self,
1497        data: &ImageBoxNode,
1498    ) -> Result<ImageBoxNodePrefab, ApplicationError> {
1499        Ok(ImageBoxNodePrefab {
1500            id: data.id.to_owned(),
1501            props: self.props_registry.serialize(&data.props)?,
1502            width: data.width,
1503            height: data.height,
1504            content_keep_aspect_ratio: data.content_keep_aspect_ratio,
1505            material: data.material.clone(),
1506            transform: data.transform,
1507        })
1508    }
1509
1510    fn text_box_to_prefab(
1511        &self,
1512        data: &TextBoxNode,
1513    ) -> Result<TextBoxNodePrefab, ApplicationError> {
1514        Ok(TextBoxNodePrefab {
1515            id: data.id.to_owned(),
1516            props: self.props_registry.serialize(&data.props)?,
1517            text: data.text.clone(),
1518            width: data.width,
1519            height: data.height,
1520            horizontal_align: data.horizontal_align,
1521            vertical_align: data.vertical_align,
1522            direction: data.direction,
1523            font: data.font.clone(),
1524            color: data.color,
1525            transform: data.transform,
1526        })
1527    }
1528
1529    fn node_from_prefab(&self, data: WidgetNodePrefab) -> Result<WidgetNode, ApplicationError> {
1530        Ok(match data {
1531            WidgetNodePrefab::None => WidgetNode::None,
1532            WidgetNodePrefab::Component(data) => {
1533                WidgetNode::Component(self.component_from_prefab(data)?)
1534            }
1535            WidgetNodePrefab::Unit(data) => WidgetNode::Unit(self.unit_from_prefab(data)?),
1536            WidgetNodePrefab::Tuple(data) => WidgetNode::Tuple(self.tuple_from_prefab(data)?),
1537        })
1538    }
1539
1540    fn component_from_prefab(
1541        &self,
1542        data: WidgetComponentPrefab,
1543    ) -> Result<WidgetComponent, ApplicationError> {
1544        if let Some(processor) = self.component_mappings.get(&data.type_name) {
1545            Ok(WidgetComponent {
1546                processor: processor.clone(),
1547                type_name: data.type_name,
1548                key: data.key,
1549                idref: Default::default(),
1550                props: self.deserialize_props(data.props)?,
1551                shared_props: match data.shared_props {
1552                    Some(p) => Some(self.deserialize_props(p)?),
1553                    None => None,
1554                },
1555                listed_slots: data
1556                    .listed_slots
1557                    .into_iter()
1558                    .map(|v| self.node_from_prefab(v))
1559                    .collect::<Result<_, ApplicationError>>()?,
1560                named_slots: data
1561                    .named_slots
1562                    .into_iter()
1563                    .map(|(k, v)| Ok((k, self.node_from_prefab(v)?)))
1564                    .collect::<Result<_, ApplicationError>>()?,
1565            })
1566        } else {
1567            Err(ApplicationError::ComponentMappingNotFound(
1568                data.type_name.clone(),
1569            ))
1570        }
1571    }
1572
1573    fn unit_from_prefab(
1574        &self,
1575        data: WidgetUnitNodePrefab,
1576    ) -> Result<WidgetUnitNode, ApplicationError> {
1577        Ok(match data {
1578            WidgetUnitNodePrefab::None => WidgetUnitNode::None,
1579            WidgetUnitNodePrefab::AreaBox(data) => {
1580                WidgetUnitNode::AreaBox(self.area_box_from_prefab(data)?)
1581            }
1582            WidgetUnitNodePrefab::PortalBox(data) => {
1583                WidgetUnitNode::PortalBox(self.portal_box_from_prefab(data)?)
1584            }
1585            WidgetUnitNodePrefab::ContentBox(data) => {
1586                WidgetUnitNode::ContentBox(self.content_box_from_prefab(data)?)
1587            }
1588            WidgetUnitNodePrefab::FlexBox(data) => {
1589                WidgetUnitNode::FlexBox(self.flex_box_from_prefab(data)?)
1590            }
1591            WidgetUnitNodePrefab::GridBox(data) => {
1592                WidgetUnitNode::GridBox(self.grid_box_from_prefab(data)?)
1593            }
1594            WidgetUnitNodePrefab::SizeBox(data) => {
1595                WidgetUnitNode::SizeBox(self.size_box_from_prefab(data)?)
1596            }
1597            WidgetUnitNodePrefab::ImageBox(data) => {
1598                WidgetUnitNode::ImageBox(self.image_box_from_prefab(data)?)
1599            }
1600            WidgetUnitNodePrefab::TextBox(data) => {
1601                WidgetUnitNode::TextBox(self.text_box_from_prefab(data)?)
1602            }
1603        })
1604    }
1605
1606    fn tuple_from_prefab(
1607        &self,
1608        data: Vec<WidgetNodePrefab>,
1609    ) -> Result<Vec<WidgetNode>, ApplicationError> {
1610        data.into_iter()
1611            .map(|data| self.node_from_prefab(data))
1612            .collect::<Result<_, _>>()
1613    }
1614
1615    fn area_box_from_prefab(
1616        &self,
1617        data: AreaBoxNodePrefab,
1618    ) -> Result<AreaBoxNode, ApplicationError> {
1619        Ok(AreaBoxNode {
1620            id: data.id,
1621            slot: Box::new(self.node_from_prefab(*data.slot)?),
1622        })
1623    }
1624
1625    fn portal_box_from_prefab(
1626        &self,
1627        data: PortalBoxNodePrefab,
1628    ) -> Result<PortalBoxNode, ApplicationError> {
1629        Ok(PortalBoxNode {
1630            id: data.id,
1631            slot: Box::new(match *data.slot {
1632                PortalBoxSlotNodePrefab::Slot(slot) => {
1633                    PortalBoxSlotNode::Slot(self.node_from_prefab(slot)?)
1634                }
1635                PortalBoxSlotNodePrefab::ContentItem(item) => {
1636                    PortalBoxSlotNode::ContentItem(ContentBoxItemNode {
1637                        slot: self.node_from_prefab(item.slot)?,
1638                        layout: item.layout,
1639                    })
1640                }
1641                PortalBoxSlotNodePrefab::FlexItem(item) => {
1642                    PortalBoxSlotNode::FlexItem(FlexBoxItemNode {
1643                        slot: self.node_from_prefab(item.slot)?,
1644                        layout: item.layout,
1645                    })
1646                }
1647                PortalBoxSlotNodePrefab::GridItem(item) => {
1648                    PortalBoxSlotNode::GridItem(GridBoxItemNode {
1649                        slot: self.node_from_prefab(item.slot)?,
1650                        layout: item.layout,
1651                    })
1652                }
1653            }),
1654            owner: data.owner,
1655        })
1656    }
1657
1658    fn content_box_from_prefab(
1659        &self,
1660        data: ContentBoxNodePrefab,
1661    ) -> Result<ContentBoxNode, ApplicationError> {
1662        Ok(ContentBoxNode {
1663            id: data.id,
1664            props: self.props_registry.deserialize(data.props)?,
1665            items: data
1666                .items
1667                .into_iter()
1668                .map(|v| {
1669                    Ok(ContentBoxItemNode {
1670                        slot: self.node_from_prefab(v.slot)?,
1671                        layout: v.layout,
1672                    })
1673                })
1674                .collect::<Result<_, ApplicationError>>()?,
1675            clipping: data.clipping,
1676            transform: data.transform,
1677        })
1678    }
1679
1680    fn flex_box_from_prefab(
1681        &self,
1682        data: FlexBoxNodePrefab,
1683    ) -> Result<FlexBoxNode, ApplicationError> {
1684        Ok(FlexBoxNode {
1685            id: data.id,
1686            props: self.props_registry.deserialize(data.props)?,
1687            items: data
1688                .items
1689                .into_iter()
1690                .map(|v| {
1691                    Ok(FlexBoxItemNode {
1692                        slot: self.node_from_prefab(v.slot)?,
1693                        layout: v.layout,
1694                    })
1695                })
1696                .collect::<Result<_, ApplicationError>>()?,
1697            direction: data.direction,
1698            separation: data.separation,
1699            wrap: data.wrap,
1700            transform: data.transform,
1701        })
1702    }
1703
1704    fn grid_box_from_prefab(
1705        &self,
1706        data: GridBoxNodePrefab,
1707    ) -> Result<GridBoxNode, ApplicationError> {
1708        Ok(GridBoxNode {
1709            id: data.id,
1710            props: self.props_registry.deserialize(data.props)?,
1711            items: data
1712                .items
1713                .into_iter()
1714                .map(|v| {
1715                    Ok(GridBoxItemNode {
1716                        slot: self.node_from_prefab(v.slot)?,
1717                        layout: v.layout,
1718                    })
1719                })
1720                .collect::<Result<_, ApplicationError>>()?,
1721            cols: data.cols,
1722            rows: data.rows,
1723            transform: data.transform,
1724        })
1725    }
1726
1727    fn size_box_from_prefab(
1728        &self,
1729        data: SizeBoxNodePrefab,
1730    ) -> Result<SizeBoxNode, ApplicationError> {
1731        Ok(SizeBoxNode {
1732            id: data.id,
1733            props: self.props_registry.deserialize(data.props)?,
1734            slot: Box::new(self.node_from_prefab(*data.slot)?),
1735            width: data.width,
1736            height: data.height,
1737            margin: data.margin,
1738            keep_aspect_ratio: data.keep_aspect_ratio,
1739            transform: data.transform,
1740        })
1741    }
1742
1743    fn image_box_from_prefab(
1744        &self,
1745        data: ImageBoxNodePrefab,
1746    ) -> Result<ImageBoxNode, ApplicationError> {
1747        Ok(ImageBoxNode {
1748            id: data.id,
1749            props: self.props_registry.deserialize(data.props)?,
1750            width: data.width,
1751            height: data.height,
1752            content_keep_aspect_ratio: data.content_keep_aspect_ratio,
1753            material: data.material,
1754            transform: data.transform,
1755        })
1756    }
1757
1758    fn text_box_from_prefab(
1759        &self,
1760        data: TextBoxNodePrefab,
1761    ) -> Result<TextBoxNode, ApplicationError> {
1762        Ok(TextBoxNode {
1763            id: data.id,
1764            props: self.props_registry.deserialize(data.props)?,
1765            text: data.text,
1766            width: data.width,
1767            height: data.height,
1768            horizontal_align: data.horizontal_align,
1769            vertical_align: data.vertical_align,
1770            direction: data.direction,
1771            font: data.font,
1772            color: data.color,
1773            transform: data.transform,
1774        })
1775    }
1776}