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