1use 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#[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#[derive(Debug, Default, Clone)]
85pub enum InvalidationCause {
86 #[default]
88 None,
89 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
104pub 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 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 #[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 #[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 #[inline]
193 pub fn unregister_component(&mut self, type_name: &str) {
194 self.component_mappings.remove(type_name);
195 }
196
197 #[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 #[inline]
213 pub fn unregister_props(&mut self, name: &str) {
214 self.props_registry.unregister_factory(name);
215 }
216
217 #[inline]
219 pub fn serialize_props(&self, props: &Props) -> Result<PrefabValue, PrefabError> {
220 self.props_registry.serialize(props)
221 }
222
223 #[inline]
225 pub fn deserialize_props(&self, data: PrefabValue) -> Result<Props, PrefabError> {
226 self.props_registry.deserialize(data)
227 }
228
229 #[inline]
231 pub fn serialize_node(&self, data: &WidgetNode) -> Result<PrefabValue, ApplicationError> {
232 Ok(self.node_to_prefab(data)?.to_prefab()?)
233 }
234
235 #[inline]
237 pub fn deserialize_node(&self, data: PrefabValue) -> Result<WidgetNode, ApplicationError> {
238 self.node_from_prefab(WidgetNodePrefab::from_prefab(data)?)
239 }
240
241 #[inline]
243 pub fn last_invalidation_cause(&self) -> &InvalidationCause {
244 &self.last_invalidation_cause
245 }
246
247 #[inline]
249 pub fn dirty(&self) -> &WidgetIdCommon {
250 &self.dirty
251 }
252
253 #[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 #[inline]
266 pub fn tree(&self) -> &WidgetNode {
267 &self.tree
268 }
269
270 #[inline]
272 pub fn rendered_tree(&self) -> &WidgetUnit {
273 &self.rendered_tree
274 }
275
276 #[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 #[inline]
289 pub fn apply(&mut self, tree: impl Into<WidgetNode>) {
290 self.mark_dirty();
291 self.tree = tree.into();
292 }
293
294 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
387 pub fn signals(&self) -> &[Signal] {
388 &self.signals
389 }
390
391 #[inline]
394 pub fn consume_signals(&mut self) -> Vec<Signal> {
395 std::mem::take(&mut self.signals)
396 }
397
398 #[inline]
400 pub fn forced_process(&mut self) -> bool {
401 self.mark_dirty();
402 self.process()
403 }
404
405 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}