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