1use 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#[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#[derive(Debug, Default, Clone)]
160pub enum InvalidationCause {
161 #[default]
163 None,
164 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
179pub 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 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 #[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 #[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 #[inline]
295 pub fn unregister_component(&mut self, type_name: &str) {
296 self.component_mappings.remove(type_name);
297 }
298
299 #[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 #[inline]
334 pub fn unregister_props(&mut self, name: &str) {
335 self.props_registry.unregister_factory(name);
336 }
337
338 #[inline]
340 pub fn serialize_props(&self, props: &Props) -> Result<PrefabValue, PrefabError> {
341 self.props_registry.serialize(props)
342 }
343
344 #[inline]
346 pub fn deserialize_props(&self, data: PrefabValue) -> Result<Props, PrefabError> {
347 self.props_registry.deserialize(data)
348 }
349
350 #[inline]
352 pub fn serialize_node(&self, data: &WidgetNode) -> Result<PrefabValue, ApplicationError> {
353 Ok(self.node_to_prefab(data)?.to_prefab()?)
354 }
355
356 #[inline]
358 pub fn deserialize_node(&self, data: PrefabValue) -> Result<WidgetNode, ApplicationError> {
359 self.node_from_prefab(WidgetNodePrefab::from_prefab(data)?)
360 }
361
362 #[inline]
364 pub fn last_invalidation_cause(&self) -> &InvalidationCause {
365 &self.last_invalidation_cause
366 }
367
368 #[inline]
370 pub fn dirty(&self) -> &WidgetIdCommon {
371 &self.dirty
372 }
373
374 #[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 #[inline]
387 pub fn tree(&self) -> &WidgetNode {
388 &self.tree
389 }
390
391 #[inline]
393 pub fn rendered_tree(&self) -> &WidgetUnit {
394 &self.rendered_tree
395 }
396
397 #[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 #[inline]
410 pub fn apply(&mut self, tree: impl Into<WidgetNode>) {
411 self.mark_dirty();
412 self.tree = tree.into();
413 }
414
415 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
501 pub fn signals(&self) -> &[Signal] {
502 &self.signals
503 }
504
505 #[inline]
508 pub fn consume_signals(&mut self) -> Vec<Signal> {
509 std::mem::take(&mut self.signals)
510 }
511
512 #[inline]
514 pub fn forced_process(&mut self) -> bool {
515 self.mark_dirty();
516 self.process()
517 }
518
519 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}