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