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