1use crate::api::TextInput;
2use crate::node_interface::NodeLocal;
3use pax_language::Computable;
4use pax_runtime_api::pax_value::{ImplToFromPaxAny, PaxAny, ToFromPaxAny};
5use pax_runtime_api::{
6 borrow, borrow_mut, use_RefCell, AsyncError, AsyncScope, Focus, Interpolatable, Layer,
7 LocalProperty, NativeLiquidGlassScope, OpacityScope, PaxValue, Percent, SelectStart, Variable,
8};
9
10use crate::api::math::Point2;
11use crate::constants::{
12 ACCEL_HANDLERS, BUTTON_CLICK_HANDLERS, CHECKBOX_CHANGE_HANDLERS, CLICK_HANDLERS,
13 CONTEXT_MENU_HANDLERS, DOUBLE_CLICK_HANDLERS, DROP_HANDLERS, FOCUSED_HANDLERS, GYRO_HANDLERS,
14 KEY_DOWN_HANDLERS, KEY_PRESS_HANDLERS, KEY_UP_HANDLERS, MOUSE_DOWN_HANDLERS,
15 MOUSE_MOVE_HANDLERS, MOUSE_OUT_HANDLERS, MOUSE_OVER_HANDLERS, MOUSE_UP_HANDLERS,
16 PHOTO_PICKER_CHANGE_HANDLERS, SCROLL_HANDLERS, SELECT_START_HANDLERS, SLIDER_CHANGE_HANDLERS,
17 TAP_HANDLERS, TEXTBOX_CHANGE_HANDLERS, TEXTBOX_INPUT_HANDLERS, TEXT_INPUT_HANDLERS,
18 TOUCH_CANCEL_HANDLERS, TOUCH_END_HANDLERS, TOUCH_MOVE_HANDLERS, TOUCH_START_HANDLERS,
19 WHEEL_HANDLERS,
20};
21use_RefCell!();
22use crate::cartridge::evaluate_timeline_duration;
23use crate::{ExpandedNodeIdentifier, Globals, LayoutHull, LayoutProperties, TransformAndBounds};
24use core::fmt;
25use std::cell::Cell;
26use std::collections::{BTreeMap, HashMap, HashSet};
27use std::rc::{Rc, Weak};
28
29#[derive(Clone)]
32struct ParentBindingSources {
33 parent: ExpandedNodeIdentifier,
34 properties: Vec<pax_runtime_api::properties::UntypedProperty>,
35}
36
37impl ParentBindingSources {
38 fn same_as(&self, other: &Self) -> bool {
39 self.parent == other.parent
40 && self.properties.len() == other.properties.len()
41 && self
42 .properties
43 .iter()
44 .zip(&other.properties)
45 .all(|(a, b)| a.get_id() == b.get_id())
46 }
47}
48
49#[derive(PartialEq)]
50struct ChildStructure {
51 binding_generation: u64,
52 rendered: Vec<ExpandedNodeIdentifier>,
53 active: Vec<ExpandedNodeIdentifier>,
54 exiting: Vec<ExpandedNodeIdentifier>,
55}
56
57use crate::api::{
58 Accel, Axis, ButtonClick, CheckboxChange, Click, CommonProperties, ContextMenu, DoubleClick,
59 Drop, Event, Gyro, KeyDown, KeyPress, KeyUp, LayoutRole, MouseDown, MouseMove, MouseOut,
60 MouseOver, MouseUp, NodeContext, PhotoPickerChange, RenderContext, Scroll, Size, SliderChange,
61 TextboxChange, TextboxInput, TouchCancel, TouchEnd, TouchMove, TouchStart, Wheel, Window,
62};
63use pax_manifest::cartridge_generation::{
64 TRANSITION_GENERATION_SYMBOL, TRANSITION_PHASE_ENTER, TRANSITION_PHASE_EXIT,
65 TRANSITION_PHASE_IDLE, TRANSITION_PHASE_SYMBOL, TRANSITION_PLAYHEAD_MILLIS_SYMBOL,
66 TRANSITION_PLAYHEAD_SYMBOL, TRANSITION_TAKEOVER_SYMBOL,
67};
68use pax_manifest::{
69 ExpressionInfo, LocationInfo, SelectorExpr, SettingsBlockElement, TypeId,
70 UniqueTemplateNodeIdentifier, ValueDefinition,
71};
72
73use crate::{
74 add_symmetric_padding_to_content_layout_hull, apply_container_frame, apply_padding_frame,
75 compute_tab, ComponentInstance, ContainerFrame, HandlerLocation, InstanceNode, InstanceNodePtr,
76 ReceivedChildrenSource, RuntimeContext, RuntimePropertiesStackFrame,
77};
78
79#[derive(Clone)]
80pub struct RuntimeSettingsLayer {
81 pub provider_id: ExpandedNodeIdentifier,
82 pub provider_type_id: TypeId,
83 pub provider_stack: Rc<RuntimePropertiesStackFrame>,
87 pub settings: Vec<SettingsBlockElement>,
88 pub transition: Option<LocalProperty<Option<crate::SettingsTransitionConfig>>>,
89}
90
91impl fmt::Debug for RuntimeSettingsLayer {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 f.debug_struct("RuntimeSettingsLayer")
94 .field("provider_id", &self.provider_id)
95 .field("provider_type_id", &self.provider_type_id)
96 .field("provider_stack", &"<runtime scope>")
97 .field("settings", &self.settings)
98 .finish()
99 }
100}
101
102#[derive(Clone, Debug, PartialEq, Eq)]
103pub struct RuntimeSettingsSignatureEntry {
104 pub transition_enabled: bool,
105 pub provider_id: ExpandedNodeIdentifier,
106 pub provider_type_id: TypeId,
107}
108
109struct DiscoveredSettingsProvider {
112 node: Rc<ExpandedNode>,
113 transition: Option<LocalProperty<Option<crate::SettingsTransitionConfig>>>,
114}
115
116impl DiscoveredSettingsProvider {
117 fn signature(&self) -> RuntimeSettingsSignatureEntry {
118 RuntimeSettingsSignatureEntry {
119 transition_enabled: self
120 .transition
121 .as_ref()
122 .and_then(|policy| policy.get())
123 .is_some(),
124 provider_id: self.node.id,
125 provider_type_id: borrow!(self.node.instance_node)
126 .base()
127 .template_node_type_id
128 .clone()
129 .unwrap_or_default(),
130 }
131 }
132
133 fn materialize(self) -> RuntimeSettingsLayer {
134 let instance = borrow!(self.node.instance_node);
135 #[cfg(test)]
136 self.node
137 .settings_layer_materializations
138 .set(self.node.settings_layer_materializations.get() + 1);
139 RuntimeSettingsLayer {
140 provider_id: self.node.id,
141 provider_type_id: instance
142 .base()
143 .template_node_type_id
144 .clone()
145 .unwrap_or_default(),
146 provider_stack: self
147 .node
148 .stack
149 .push(borrow!(self.node.properties_scope).clone()),
150 settings: instance
151 .base()
152 .component_settings
153 .as_ref()
154 .expect("discovered provider has settings")
155 .clone(),
156 transition: self.transition,
157 }
158 }
159}
160
161#[derive(Clone, Debug, PartialEq, Eq)]
162pub enum RuntimeSettingsSource {
163 ComponentSettings,
164 ImportedLayer {
165 provider_id: ExpandedNodeIdentifier,
166 provider_type_id: TypeId,
167 },
168 Inline,
169}
170
171#[derive(Clone, Debug, Default)]
172pub struct RuntimeSettingsCondition {
173 pub positive: Vec<ExpressionInfo>,
174 pub negative: Vec<ExpressionInfo>,
175}
176
177#[derive(Clone)]
178pub struct RuntimeResolvedPropertyEntry {
179 pub transition: Option<LocalProperty<Option<crate::SettingsTransitionConfig>>>,
180 pub source: RuntimeSettingsSource,
181 pub selector: Option<SelectorExpr>,
182 pub source_location: Option<LocationInfo>,
183 pub source_stack: Option<Rc<RuntimePropertiesStackFrame>>,
186 pub value: ValueDefinition,
187 pub condition: Option<RuntimeSettingsCondition>,
188 pub axis_index: Option<usize>,
189}
190
191impl fmt::Debug for RuntimeResolvedPropertyEntry {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 f.debug_struct("RuntimeResolvedPropertyEntry")
194 .field("source", &self.source)
195 .field("selector", &self.selector)
196 .field("source_location", &self.source_location)
197 .field(
198 "source_stack",
199 &self.source_stack.as_ref().map(|_| "<runtime scope>"),
200 )
201 .field("value", &self.value)
202 .field("condition", &self.condition)
203 .field("axis_index", &self.axis_index)
204 .finish()
205 }
206}
207
208pub type RuntimeResolvedPropertyColumns = BTreeMap<String, Vec<RuntimeResolvedPropertyEntry>>;
209
210#[derive(Clone, Copy)]
211enum PointerActivationSource {
212 Mouse,
213 Touch,
214}
215
216#[derive(Clone, Copy, Debug, Default)]
217pub(crate) struct FilteredRenderStats {
218 pub path_nodes_visited: usize,
219 pub dirty_nodes_rendered: usize,
220 pub skipped_subtrees: usize,
221}
222
223#[derive(Clone)]
231pub struct ExpandedNode {
232 property_binding_scope: RefCell<pax_runtime_api::properties::BindingScope>,
233 async_scope: RefCell<Option<AsyncScope>>,
234 #[allow(dead_code)]
235 pub id: ExpandedNodeIdentifier,
238
239 pub instance_node: RefCell<InstanceNodePtr>,
241
242 pub render_parent: RefCell<Weak<ExpandedNode>>,
245
246 pub template_parent: Weak<ExpandedNode>,
249
250 pub parent_frame: LocalProperty<Option<ExpandedNodeIdentifier>>,
255
256 pub liquid_glass_scope: LocalProperty<Option<NativeLiquidGlassScope>>,
258
259 pub containing_component: Weak<ExpandedNode>,
264
265 pub stack: Rc<RuntimePropertiesStackFrame>,
269 pub(crate) stores: crate::store::NodeStores,
270
271 pub children: LocalProperty<Vec<Rc<ExpandedNode>>>,
274
275 pub mounted_children: RefCell<Vec<Rc<ExpandedNode>>>,
281
282 pub active_children: RefCell<Vec<Rc<ExpandedNode>>>,
287 pub active_children_view: LocalProperty<Vec<Rc<ExpandedNode>>>,
290
291 pub exiting_children: RefCell<Vec<Rc<ExpandedNode>>>,
296 exiting_child_generations: RefCell<HashMap<ExpandedNodeIdentifier, u64>>,
299 pub exiting_children_view: LocalProperty<Vec<Rc<ExpandedNode>>>,
301
302 pub sidecar_children: RefCell<Vec<Rc<ExpandedNode>>>,
305
306 render_source_owner: Cell<Option<ExpandedNodeIdentifier>>,
309
310 pub properties: RefCell<Rc<RefCell<PaxAny>>>,
312
313 pub common_properties: RefCell<Rc<RefCell<CommonProperties>>>,
315 pub measured_size: LocalProperty<Option<(f64, f64)>>,
318 pub selector_metadata: RefCell<RuntimeSelectorMetadata>,
320
321 pub transform_and_bounds: LocalProperty<TransformAndBounds<NodeLocal, Window>>,
324 pub subtree_layout_hull: LocalProperty<LayoutHull>,
326 pub container_frame: LocalProperty<Option<ContainerFrame>>,
328
329 pub computed_opacity: LocalProperty<f64>,
332 pub computed_opacity_scopes: LocalProperty<Vec<OpacityScope>>,
335
336 pub expanded_projected_children: RefCell<Option<Vec<Rc<ExpandedNode>>>>,
343 pub expanded_and_flattened_projected_children: LocalProperty<Vec<Rc<ExpandedNode>>>,
349 pub flattened_projected_children_count: LocalProperty<usize>,
351
352 pub attached: Cell<u32>,
359
360 pub occlusion: LocalProperty<Occlusion>,
365
366 pub native_mask_hash: Cell<u64>,
368
369 pub browser_content_layer_id: Cell<Option<u32>>,
371
372 pub presentation_cache_hash: Cell<u64>,
374
375 pub properties_scope: RefCell<HashMap<String, Variable>>,
378
379 pub slot_index: LocalProperty<Option<usize>>,
382
383 pub suspended: LocalProperty<bool>,
385
386 pub changed_listener: LocalProperty<()>,
389 selector_classes_listener: LocalProperty<()>,
390
391 pub occlusion_listener: LocalProperty<()>,
393
394 pub children_listener: LocalProperty<()>,
396 pub subtree_layout_hull_listener: LocalProperty<()>,
398 parent_binding_sources: RefCell<Option<ParentBindingSources>>,
399 layout_binding_generation: Cell<u64>,
400 #[cfg(test)]
401 pub(crate) parent_binding_rebuilds: Cell<usize>,
402 #[cfg(test)]
403 pub(crate) layout_hull_rebuilds: Cell<usize>,
404 #[cfg(test)]
405 pub(crate) structural_children_updates: Cell<usize>,
406 pub content_measurement_listener: LocalProperty<()>,
408 pub content_measurement_rebind_listener: LocalProperty<()>,
410 pub content_measurement_bound: Cell<bool>,
412 #[cfg(feature = "shaders")]
413 pub(crate) shader_effect_bound: Cell<bool>,
414 subtree_requires_non_reactive_update: Cell<bool>,
418
419 pub projected_children_changed: LocalProperty<()>,
421
422 pub slot_projection_changed: LocalProperty<()>,
425
426 pub subscriptions: RefCell<Vec<LocalProperty<()>>>,
428
429 pub transition_phase: LocalProperty<u64>,
431 pub transition_generation: LocalProperty<u64>,
433 pub transition_takeover: LocalProperty<bool>,
435 pub transition_selector_classes: RefCell<Option<Vec<String>>>,
439 exit_retention_generation: Cell<u64>,
441 pub transition_origin_frame: LocalProperty<u64>,
443 pub transition_origin_millis: LocalProperty<u64>,
445 pub transition_playhead: LocalProperty<f64>,
447 pub transition_playhead_millis: LocalProperty<f64>,
449 pub enter_cleanup_listener: LocalProperty<()>,
451 pub enter_cleanup_active: Cell<bool>,
453 pub exit_started_millis: Cell<Option<u128>>,
455 exit_timeout_warning_emitted: Cell<bool>,
457 pub exit_cleanup_listener: LocalProperty<()>,
459 pub exit_cleanup_active: Cell<bool>,
461 pub imported_settings_layers: RefCell<Vec<RuntimeSettingsLayer>>,
463 #[cfg(test)]
464 pub(crate) settings_layer_materializations: Cell<usize>,
465 pub import_settings_transition:
466 RefCell<Option<LocalProperty<Option<crate::SettingsTransitionConfig>>>>,
467 pub(crate) settings_motion: Rc<RefCell<HashMap<String, Box<dyn std::any::Any>>>>,
468 pub(crate) settings_birth_frame: u64,
469 pub imported_settings_signature: RefCell<Vec<RuntimeSettingsSignatureEntry>>,
471 pub resolved_property_columns: RefCell<RuntimeResolvedPropertyColumns>,
473 pub resolved_property_provenance: RefCell<BTreeMap<String, RuntimeResolvedPropertyEntry>>,
475 pub reset_removed_runtime_properties: Cell<bool>,
477}
478
479#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
480pub struct Occlusion {
481 pub render_layer_id: usize,
483 pub z_index: i32,
484 pub parent_frame: Option<u32>,
488}
489
490impl Interpolatable for Occlusion {}
491
492impl ImplToFromPaxAny for ExpandedNode {}
493impl Interpolatable for ExpandedNode {}
494
495#[derive(Clone)]
496pub struct RuntimeSelectorMetadata {
498 pub type_id: TypeId,
500 pub id: LocalProperty<Option<String>>,
502 pub classes: LocalProperty<Vec<String>>,
504}
505
506impl RuntimeSelectorMetadata {
507 fn from_base(
508 base: &crate::rendering::BaseInstance,
509 common_properties: &Rc<RefCell<CommonProperties>>,
510 stack: &Rc<RuntimePropertiesStackFrame>,
511 ) -> Self {
512 let classes = base
513 .template_node_selector_info
514 .as_ref()
515 .and_then(|info| {
516 info.class_binding.as_ref().map(|binding| {
517 build_selector_classes_property(binding, info.source_location.as_ref(), stack)
518 })
519 })
520 .unwrap_or_else(|| LocalProperty::new_with_name(Vec::new(), "selector classes"));
521
522 Self {
523 type_id: base.template_node_type_id.clone().unwrap_or_default(),
524 id: common_properties.borrow().id.clone(),
525 classes,
526 }
527 }
528
529 pub fn matches(&self, selector: &SelectorExpr) -> bool {
530 match selector {
531 SelectorExpr::Id(id) => self.id.get().as_deref() == Some(id.as_str()),
532 SelectorExpr::Class(class_name) => self
533 .classes
534 .get()
535 .into_iter()
536 .any(|node_class| node_class == *class_name),
537 SelectorExpr::Type(type_name) => {
538 self.type_id.to_string() == *type_name
539 || self.type_id.get_pascal_identifier().as_deref() == Some(type_name.as_str())
540 }
541 }
542 }
543}
544
545fn warn_selector_class_once(
546 warnings: &Rc<RefCell<HashSet<String>>>,
547 key: String,
548 message: impl FnOnce() -> String,
549) {
550 if warnings.borrow_mut().insert(key) {
551 log::warn!("{}", message());
552 }
553}
554
555fn normalize_selector_classes(
556 value: PaxValue,
557 warnings: &Rc<RefCell<HashSet<String>>>,
558 source_location: Option<&LocationInfo>,
559) -> Vec<String> {
560 let location = source_location
561 .map(|location| {
562 format!(
563 " at {}:{}",
564 location.start_line_col.0, location.start_line_col.1
565 )
566 })
567 .unwrap_or_default();
568 let candidates = match value {
569 PaxValue::String(value) => vec![value],
570 PaxValue::Vec(values) => values
571 .into_iter()
572 .filter_map(|value| match value {
573 PaxValue::String(value) => Some(value),
574 other => {
575 warn_selector_class_once(warnings, format!("list-type:{other}"), || {
576 format!("ignoring non-string value in class list{location}: {other}")
577 });
578 None
579 }
580 })
581 .collect(),
582 other => {
583 warn_selector_class_once(warnings, format!("binding-type:{other}"), || {
584 format!(
585 "class binding{location} must evaluate to String or Vec<String>; received {other}"
586 )
587 });
588 return Vec::new();
589 }
590 };
591
592 let mut seen = HashSet::new();
593 let mut normalized = candidates
594 .into_iter()
595 .rev()
596 .filter(|class_name| {
597 if class_name.is_empty() {
598 return false;
599 }
600 if !matches!(
601 SelectorExpr::parse(&format!(".{class_name}")),
602 Ok(SelectorExpr::Class(_))
603 ) {
604 warn_selector_class_once(warnings, format!("invalid-name:{class_name}"), || {
605 format!("ignoring invalid class name '{class_name}'{location}")
606 });
607 return false;
608 }
609 seen.insert(class_name.clone())
610 })
611 .collect::<Vec<_>>();
612 normalized.reverse();
613 normalized
614}
615
616fn build_selector_classes_property(
617 binding: &ValueDefinition,
618 source_location: Option<&LocationInfo>,
619 stack: &Rc<RuntimePropertiesStackFrame>,
620) -> LocalProperty<Vec<String>> {
621 let warnings = Rc::new(RefCell::new(HashSet::new()));
622 let source_location = source_location.cloned();
623 match binding {
624 ValueDefinition::LiteralValue(value) => LocalProperty::new_with_name(
625 normalize_selector_classes(value.clone(), &warnings, source_location.as_ref()),
626 "selector classes",
627 ),
628 ValueDefinition::Expression(info) => {
629 let dependencies = info
630 .dependencies
631 .iter()
632 .filter_map(|dependency| {
633 stack
634 .resolve_symbol_as_erased_property(dependency)
635 .or_else(|| {
636 log::warn!("failed to resolve class binding symbol {dependency}");
637 None
638 })
639 })
640 .collect::<Vec<_>>();
641 let expression = info.expression.clone();
642 let expression_label = expression.to_string();
643 let stack = Rc::clone(stack);
644 let warnings = Rc::clone(&warnings);
645 LocalProperty::computed_with_name(
646 move || match expression.compute(stack.clone()) {
647 Ok(value) => {
648 normalize_selector_classes(value, &warnings, source_location.as_ref())
649 }
650 Err(error) => {
651 warn_selector_class_once(
652 &warnings,
653 format!("evaluation:{expression_label}:{error:?}"),
654 || {
655 let location = source_location
656 .as_ref()
657 .map(|location| {
658 format!(
659 " at {}:{}",
660 location.start_line_col.0, location.start_line_col.1
661 )
662 })
663 .unwrap_or_default();
664 format!(
665 "failed to evaluate class binding `{expression_label}`{location}: {error:?}"
666 )
667 },
668 );
669 Vec::new()
670 }
671 },
672 &dependencies,
673 "selector classes",
674 )
675 }
676 other => {
677 log::warn!("unsupported class binding definition: {other}");
678 LocalProperty::new_with_name(Vec::new(), "selector classes")
679 }
680 }
681}
682
683#[cfg(test)]
684mod selector_class_tests {
685 use super::{build_selector_classes_property, normalize_selector_classes};
686 use crate::RuntimePropertiesStackFrame;
687 use pax_language::parse_pax_expression;
688 use pax_manifest::{ExpressionInfo, ValueDefinition};
689 use pax_runtime_api::{LocalProperty, PaxValue, Variable};
690 use std::cell::RefCell;
691 use std::collections::{HashMap, HashSet};
692 use std::rc::Rc;
693
694 #[test]
695 fn normalizes_order_duplicates_and_invalid_names() {
696 let warnings = Rc::new(RefCell::new(HashSet::new()));
697 let classes = normalize_selector_classes(
698 PaxValue::Vec(vec![
699 PaxValue::String("base".to_string()),
700 PaxValue::String("bad name".to_string()),
701 PaxValue::String("active".to_string()),
702 PaxValue::String("base".to_string()),
703 PaxValue::Numeric(1.into()),
704 ]),
705 &warnings,
706 None,
707 );
708
709 assert_eq!(classes, vec!["active", "base"]);
710 assert_eq!(warnings.borrow().len(), 2);
711 }
712
713 #[test]
714 fn expression_backed_classes_track_string_and_vector_properties() {
715 let constant_binding = ValueDefinition::Expression(ExpressionInfo::new(
716 parse_pax_expression(r#""temporary""#).unwrap(),
717 ));
718 let constant_classes = build_selector_classes_property(
719 &constant_binding,
720 None,
721 &RuntimePropertiesStackFrame::new(HashMap::new()),
722 );
723 assert_eq!(constant_classes.get(), vec!["temporary"]);
724
725 let scalar = LocalProperty::new("base".to_string());
726 let stack = RuntimePropertiesStackFrame::new(HashMap::from([(
727 "current_class".to_string(),
728 Variable::new_from_typed_property(scalar.clone()),
729 )]));
730 let scalar_binding = ValueDefinition::Expression(ExpressionInfo::new(
731 parse_pax_expression("current_class").unwrap(),
732 ));
733 let scalar_classes = build_selector_classes_property(&scalar_binding, None, &stack);
734 assert_eq!(scalar_classes.get(), vec!["base"]);
735 scalar.set("active".to_string());
736 assert_eq!(scalar_classes.get(), vec!["active"]);
737
738 let vector = LocalProperty::new(vec!["base".to_string(), "selected".to_string()]);
739 let stack = RuntimePropertiesStackFrame::new(HashMap::from([(
740 "current_classes".to_string(),
741 Variable::new_from_typed_property(vector.clone()),
742 )]));
743 let vector_binding = ValueDefinition::Expression(ExpressionInfo::new(
744 parse_pax_expression("current_classes").unwrap(),
745 ));
746 let vector_classes = build_selector_classes_property(&vector_binding, None, &stack);
747 assert_eq!(vector_classes.get(), vec!["base", "selected"]);
748 vector.set(Vec::new());
749 assert!(vector_classes.get().is_empty());
750 }
751}
752
753macro_rules! dispatch_event_handler {
754 ($fn_name:ident, $arg_type:ty, $handler_key:ident, $recurse:expr) => {
755 pub fn $fn_name(
756 self: &Rc<Self>,
757 event: Event<$arg_type>,
758 globals: &Globals,
759 ctx: &Rc<RuntimeContext>,
760 ) -> bool {
761 if self.is_render_source() {
762 return event.cancelled();
763 }
764 self.run_event_handlers_for_key($handler_key, &event, ctx);
765
766 if $recurse {
767 if let Some(parent) = self.template_parent.upgrade() {
768 return parent.$fn_name(event, globals, ctx);
769 }
770 }
771 event.cancelled()
772 }
773 };
774}
775
776impl ExpandedNode {
777 pub fn is_render_source(&self) -> bool {
779 self.render_source_owner.get().is_some()
780 }
781
782 pub fn mount_as_render_source(self: &Rc<Self>, owner: &Rc<Self>, context: &Rc<RuntimeContext>) {
785 assert_eq!(
786 self.attached.get(),
787 0,
788 "a mask cannot borrow a mounted scene node"
789 );
790 self.render_source_owner.set(Some(owner.id));
791 self.recurse_mount(context);
792 }
793
794 pub(crate) fn render_source_owner(&self) -> Option<ExpandedNodeIdentifier> {
795 self.render_source_owner.get()
796 }
797
798 fn suppresses_native_presentation(&self) -> bool {
799 self.is_render_source() && borrow!(self.instance_node).base().flags().layer == Layer::Native
800 }
801
802 pub fn initialize_root(template: Rc<ComponentInstance>, ctx: &Rc<RuntimeContext>) -> Rc<Self> {
803 let root_node = Self::new(
804 template,
805 ctx.globals().stack_frame(),
806 ctx,
807 Weak::new(),
808 Weak::new(),
809 );
810 root_node.bind_to_parent_bounds(ctx);
811 Rc::clone(&root_node).recurse_mount(ctx);
812 root_node
813 }
814
815 fn has_event_handlers(&self, handler_key: &str) -> bool {
816 borrow!(self.instance_node)
817 .base()
818 .get_handler_registry()
819 .is_some_and(|registry| {
820 borrow!(*registry)
821 .handlers
822 .get(handler_key)
823 .is_some_and(|handlers| !handlers.is_empty())
824 })
825 }
826
827 fn handler_owner(self: &Rc<Self>, location: &HandlerLocation) -> Rc<Self> {
828 match location {
831 HandlerLocation::Component => self.clone(),
832 HandlerLocation::Inline => self
833 .containing_component
834 .upgrade()
835 .unwrap_or_else(|| self.clone()),
836 }
837 }
838
839 fn run_event_handlers_for_key<T: Clone + 'static>(
840 self: &Rc<Self>,
841 handler_key: &str,
842 event: &Event<T>,
843 ctx: &Rc<RuntimeContext>,
844 ) {
845 if self.is_render_source() {
846 return;
847 }
848 if self.attached.get() == 0 {
851 return;
852 }
853 let _graph = ctx.property_graph.enter();
854 if let Some(registry) = borrow!(self.instance_node).base().get_handler_registry() {
855 let borrowed_registry = &borrow!(*registry);
856 if let Some(handlers) = borrowed_registry.handlers.get(handler_key) {
857 let context = self.get_node_context(ctx);
858 for handler in handlers {
859 let owner = self.handler_owner(&handler.location);
860 if owner.attached.get() == 0 {
861 continue;
862 }
863 let _scope = owner.property_binding_scope.borrow().enter();
867 let properties = Rc::clone(&*borrow!(owner.properties));
868 (handler.function)(properties, &context, Some(event.clone().to_pax_any()));
869 }
870 }
871 }
872 }
873
874 fn run_lifecycle_handlers_for_key(
875 self: &Rc<Self>,
876 handler_key: &str,
877 context: &Rc<RuntimeContext>,
878 ) {
879 if let Some(registry) = borrow!(self.instance_node).base().get_handler_registry() {
880 if let Some(handlers) = borrow!(registry).handlers.get(handler_key) {
881 for handler in handlers {
882 let target_context = self.get_node_context(context);
885 let owner = self.handler_owner(&handler.location);
886 let _scope = owner.property_binding_scope.borrow().enter();
887 (handler.function)(
890 Rc::clone(&*borrow!(owner.properties)),
891 &target_context,
892 None,
893 );
894 }
895 }
896 }
897 }
898
899 fn run_viewport_event_handlers<T: Clone + 'static>(
900 self: &Rc<Self>,
901 key: &str,
902 event: Event<T>,
903 ctx: &Rc<RuntimeContext>,
904 ) {
905 let handlers: Vec<_> = borrow!(self.instance_node)
906 .base()
907 .get_handler_registry()
908 .map(|registry| {
909 borrow!(registry)
910 .handlers
911 .get(key)
912 .map(|handlers| {
913 handlers
914 .iter()
915 .map(|h| (h.function, matches!(h.location, HandlerLocation::Component)))
916 .collect()
917 })
918 .unwrap_or_default()
919 })
920 .unwrap_or_default();
921 for (function, component) in handlers {
922 if self.attached.get() == 0 || self.suspended.get() {
923 break;
924 }
925 let properties = if component {
926 Rc::clone(&borrow!(self.properties))
927 } else {
928 self.containing_component
929 .upgrade()
930 .map(|owner| Rc::clone(&borrow!(owner.properties)))
931 .unwrap_or_else(|| Rc::clone(&borrow!(self.properties)))
932 };
933 function(
934 properties,
935 &self.get_node_context(ctx),
936 Some(event.clone().to_pax_any()),
937 );
938 }
939 }
940
941 pub(crate) fn dispatch_viewport_proximity_enter(
942 self: &Rc<Self>,
943 event: Event<crate::api::ViewportProximityEnter>,
944 ctx: &Rc<RuntimeContext>,
945 ) {
946 self.run_viewport_event_handlers(
947 crate::constants::VIEWPORT_PROXIMITY_ENTER_HANDLERS,
948 event,
949 ctx,
950 );
951 }
952 pub(crate) fn dispatch_viewport_proximity_change(
953 self: &Rc<Self>,
954 event: Event<crate::api::ViewportProximityChange>,
955 ctx: &Rc<RuntimeContext>,
956 ) {
957 self.run_viewport_event_handlers(
958 crate::constants::VIEWPORT_PROXIMITY_CHANGE_HANDLERS,
959 event,
960 ctx,
961 );
962 }
963 pub(crate) fn dispatch_viewport_proximity_exit(
964 self: &Rc<Self>,
965 event: Event<crate::api::ViewportProximityExit>,
966 ctx: &Rc<RuntimeContext>,
967 ) {
968 self.run_viewport_event_handlers(
969 crate::constants::VIEWPORT_PROXIMITY_EXIT_HANDLERS,
970 event,
971 ctx,
972 );
973 }
974
975 fn new(
976 template: Rc<dyn InstanceNode>,
977 env: Rc<RuntimePropertiesStackFrame>,
978 context: &Rc<RuntimeContext>,
979 containing_component: Weak<ExpandedNode>,
980 parent: Weak<ExpandedNode>,
981 ) -> Rc<Self> {
982 let _graph = context.property_graph.enter();
983 let property_binding_scope = context.property_graph.binding_scope();
984 let _scope = property_binding_scope.enter();
985 let transition_config = template.base().transition_config().clone();
986 let has_transition_bindings = transition_config.has_enter || transition_config.has_exit;
987 let transition_phase =
988 LocalProperty::new_with_name(TRANSITION_PHASE_IDLE, "transition phase");
989 let transition_generation = LocalProperty::new_with_name(0, "transition generation");
990 let transition_takeover = LocalProperty::new_with_name(false, "transition takeover");
991 let transition_origin_frame = LocalProperty::new_with_name(
992 context.globals().elapsed_frames.get(),
993 "transition origin",
994 );
995 let transition_origin_millis = LocalProperty::new_with_name(
996 context.globals().elapsed_millis.get(),
997 "transition origin millis",
998 );
999 let transition_playhead = LocalProperty::new_with_name(0.0, "transition playhead");
1000 let transition_playhead_millis =
1001 LocalProperty::new_with_name(0.0, "transition playhead millis");
1002
1003 let env = if has_transition_bindings {
1004 env.push(
1005 vec![
1006 (
1007 TRANSITION_PHASE_SYMBOL.to_string(),
1008 Variable::new_from_typed_property(transition_phase.clone()),
1009 ),
1010 (
1011 TRANSITION_GENERATION_SYMBOL.to_string(),
1012 Variable::new_from_typed_property(transition_generation.clone()),
1013 ),
1014 (
1015 TRANSITION_TAKEOVER_SYMBOL.to_string(),
1016 Variable::new_from_typed_property(transition_takeover.clone()),
1017 ),
1018 (
1019 TRANSITION_PLAYHEAD_SYMBOL.to_string(),
1020 Variable::new_from_typed_property(transition_playhead.clone()),
1021 ),
1022 (
1023 TRANSITION_PLAYHEAD_MILLIS_SYMBOL.to_string(),
1024 Variable::new_from_typed_property(transition_playhead_millis.clone()),
1025 ),
1026 ]
1027 .into_iter()
1028 .collect(),
1029 )
1030 } else {
1031 env
1032 };
1033
1034 let legacy_initialization = template.base().requires_pre_node_binding();
1039 let properties_init = &template.base().instance_prototypical_properties;
1040 let common_init = &template.base().instance_prototypical_common_properties;
1041 let properties = if legacy_initialization {
1042 properties_init.materialize(env.clone(), None).unwrap()
1043 } else {
1044 properties_init.allocate(env.clone())
1045 };
1046 let common_properties = if legacy_initialization {
1047 common_init.materialize(env.clone(), None).unwrap()
1048 } else {
1049 common_init.allocate(env.clone())
1050 };
1051 let selector_metadata =
1052 RuntimeSelectorMetadata::from_base(template.base(), &common_properties, &env);
1053
1054 let property_scope = if legacy_initialization {
1055 let mut scope = borrow!(*common_properties).retrieve_property_scope();
1056 scope.extend(template.base().properties_scope.build(properties.clone()));
1057 scope
1058 } else {
1059 HashMap::new()
1062 };
1063
1064 let id = context.gen_uid();
1065 let render_source_owner = parent
1066 .upgrade()
1067 .and_then(|parent| parent.render_source_owner.get());
1068 let res = Rc::new(ExpandedNode {
1069 property_binding_scope: RefCell::new(property_binding_scope),
1070 async_scope: RefCell::new(None),
1071 id,
1072 stores: crate::store::NodeStores::new(parent.upgrade().as_deref()),
1073 stack: env,
1074 instance_node: RefCell::new(Rc::clone(&template)),
1075 attached: Cell::new(0),
1076 properties: RefCell::new(properties),
1077 common_properties: RefCell::new(common_properties),
1078 measured_size: LocalProperty::default(),
1079 selector_metadata: RefCell::new(selector_metadata),
1080
1081 render_parent: Default::default(),
1084 parent_frame: Default::default(),
1085 liquid_glass_scope: Default::default(),
1086 template_parent: parent,
1087
1088 containing_component,
1089 children: LocalProperty::new_with_name(
1090 Vec::new(),
1091 &format!("node children (node id: {})", id.0),
1092 ),
1093 mounted_children: RefCell::new(Vec::new()),
1094 active_children: RefCell::new(Vec::new()),
1095 active_children_view: LocalProperty::new(Vec::new()),
1096 exiting_children: RefCell::new(Vec::new()),
1097 exiting_child_generations: RefCell::new(HashMap::new()),
1098 exiting_children_view: LocalProperty::new(Vec::new()),
1099 sidecar_children: RefCell::new(Vec::new()),
1100 render_source_owner: Cell::new(render_source_owner),
1101 transform_and_bounds: LocalProperty::new(TransformAndBounds::default()),
1102 subtree_layout_hull: LocalProperty::new(LayoutHull::default()),
1103 container_frame: LocalProperty::new(None),
1104 computed_opacity: LocalProperty::new(1.0),
1105 computed_opacity_scopes: LocalProperty::new(Vec::new()),
1106 expanded_projected_children: Default::default(),
1107 expanded_and_flattened_projected_children: Default::default(),
1108 flattened_projected_children_count: LocalProperty::new(0),
1109 occlusion: LocalProperty::new(Occlusion::default()),
1110 native_mask_hash: Cell::new(0),
1111 browser_content_layer_id: Cell::new(None),
1112 presentation_cache_hash: Cell::new(0),
1113 properties_scope: RefCell::new(property_scope),
1114 slot_index: LocalProperty::default(),
1115 suspended: LocalProperty::new(false),
1116 changed_listener: LocalProperty::default(),
1117 selector_classes_listener: LocalProperty::default(),
1118 occlusion_listener: LocalProperty::default(),
1119 children_listener: LocalProperty::default(),
1120 subtree_layout_hull_listener: LocalProperty::default(),
1121 parent_binding_sources: RefCell::new(None),
1122 layout_binding_generation: Cell::new(0),
1123 #[cfg(test)]
1124 parent_binding_rebuilds: Cell::new(0),
1125 #[cfg(test)]
1126 layout_hull_rebuilds: Cell::new(0),
1127 #[cfg(test)]
1128 structural_children_updates: Cell::new(0),
1129 content_measurement_listener: LocalProperty::default(),
1130 content_measurement_rebind_listener: LocalProperty::default(),
1131 content_measurement_bound: Cell::new(false),
1132 #[cfg(feature = "shaders")]
1133 shader_effect_bound: Cell::new(false),
1134 subtree_requires_non_reactive_update: Cell::new(true),
1135 projected_children_changed: LocalProperty::default(),
1136 slot_projection_changed: LocalProperty::default(),
1137 subscriptions: Default::default(),
1138 transition_phase,
1139 transition_generation,
1140 transition_takeover,
1141 transition_selector_classes: RefCell::new(None),
1142 exit_retention_generation: Cell::new(0),
1143 transition_origin_frame,
1144 transition_origin_millis,
1145 transition_playhead,
1146 transition_playhead_millis,
1147 enter_cleanup_listener: LocalProperty::default(),
1148 enter_cleanup_active: Cell::new(false),
1149 exit_started_millis: Cell::new(None),
1150 exit_timeout_warning_emitted: Cell::new(false),
1151 exit_cleanup_listener: LocalProperty::default(),
1152 exit_cleanup_active: Cell::new(false),
1153 imported_settings_layers: RefCell::new(Vec::new()),
1154 #[cfg(test)]
1155 settings_layer_materializations: Cell::new(0),
1156 import_settings_transition: RefCell::new(None),
1157 settings_motion: Rc::new(RefCell::new(HashMap::new())),
1158 settings_birth_frame: context.globals().elapsed_frames.get(),
1159 imported_settings_signature: RefCell::new(Vec::new()),
1160 resolved_property_columns: RefCell::new(BTreeMap::new()),
1161 resolved_property_provenance: RefCell::new(BTreeMap::new()),
1162 reset_removed_runtime_properties: Cell::new(false),
1163 });
1164 template.base().bind_properties(&res);
1165 if legacy_initialization {
1166 let common_properties = Rc::clone(&*borrow!(res.common_properties));
1167 *res.selector_metadata.borrow_mut() =
1168 RuntimeSelectorMetadata::from_base(template.base(), &common_properties, &res.stack);
1169 }
1170 res.refresh_properties_scope(&template);
1171 res.bind_selector_classes_listener(context);
1172 res.bind_occlusion_listener(context);
1173 res.bind_children_listener(context);
1174 res.bind_subtree_layout_hull(context);
1175 res
1176 }
1177
1178 pub fn recreate_with_new_data(
1179 self: &Rc<Self>,
1180 template: Rc<dyn InstanceNode>,
1181 context: &Rc<RuntimeContext>,
1182 ) {
1183 let _graph = context.property_graph.enter();
1184 let _scope = self.property_binding_scope.borrow().enter();
1185 context.unregister_viewport_proximity(self.id.to_u32());
1188 let previous = self.stores.has_providers().then(|| {
1189 borrow!(self.properties_scope)
1190 .iter()
1191 .filter(|(name, _)| !name.starts_with('$'))
1192 .map(|(name, value)| (name.clone(), value.get_untyped_property().get_id()))
1193 .collect::<HashMap<_, _>>()
1194 });
1195 *borrow_mut!(self.instance_node) = Rc::clone(&template);
1196 template.base().bind_properties(self);
1197 self.refresh_properties_scope(&template);
1198 if let Some(previous) = previous {
1199 let current: HashMap<_, _> = borrow!(self.properties_scope)
1202 .iter()
1203 .filter(|(name, _)| !name.starts_with('$'))
1204 .map(|(name, value)| (name.clone(), value.get_untyped_property().get_id()))
1205 .collect();
1206 if current != previous {
1207 self.fully_recreate_with_new_data(template, context);
1208 return;
1209 }
1210 }
1211 let common_properties = Rc::clone(&*borrow!(self.common_properties));
1212 *self.selector_metadata.borrow_mut() =
1213 RuntimeSelectorMetadata::from_base(template.base(), &common_properties, &self.stack);
1214 self.refresh_properties_scope(&template);
1215 self.bind_selector_classes_listener(context);
1216 self.reapply_runtime_settings(context);
1217 self.bind_to_parent_bounds(context);
1218 self.bind_occlusion_listener(context);
1219 self.mark_non_reactive_update_subtree_dirty();
1220 context.mark_occlusion_dirty();
1221 context.set_canvas_dirty(self.occlusion.get().render_layer_id);
1222 if self.attached.get() > 0 {
1223 context.refresh_scene_lighting_node(self);
1224 context.viewport_structure_changed();
1225 context.register_viewport_proximity(self);
1226 context.invalidate_scene_geometry(self.id.to_u32());
1227 }
1228 }
1229
1230 pub fn fully_recreate_with_new_data(
1231 self: &Rc<Self>,
1232 template: Rc<dyn InstanceNode>,
1233 context: &Rc<RuntimeContext>,
1234 ) {
1235 let _graph = context.property_graph.enter();
1236 let _scope = self.property_binding_scope.borrow().enter();
1237 Rc::clone(self).recurse_unmount(context);
1238 *borrow_mut!(self.instance_node) = template;
1239 self.prepare_remount(context);
1240 let _scope = self.property_binding_scope.borrow().enter();
1241
1242 self.bind_to_parent_bounds(context);
1243 self.mark_non_reactive_update_subtree_dirty();
1244 context.mark_occlusion_dirty();
1245 Rc::clone(self).recurse_mount(context);
1246 context.drain_node_effects();
1247 self.recurse_emit_mount_updates();
1248 }
1249
1250 fn prepare_remount(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1251 let template = Rc::clone(&*borrow!(self.instance_node));
1252 let new_expanded_node = Self::new(
1253 template.clone(),
1254 Rc::clone(&self.stack),
1255 context,
1256 Weak::clone(&self.containing_component),
1257 Weak::clone(&self.template_parent),
1258 );
1259 *self.property_binding_scope.borrow_mut() =
1260 new_expanded_node.property_binding_scope.borrow().clone();
1261 let _scope = self.property_binding_scope.borrow().enter();
1262 *borrow_mut!(self.instance_node) = Rc::clone(&*borrow!(new_expanded_node.instance_node));
1263 *borrow_mut!(self.properties) = Rc::clone(&*borrow!(new_expanded_node.properties));
1264 *borrow_mut!(self.properties_scope) = borrow!(new_expanded_node.properties_scope).clone();
1265 *borrow_mut!(self.common_properties) =
1266 Rc::clone(&*borrow!(new_expanded_node.common_properties));
1267 *self.selector_metadata.borrow_mut() = new_expanded_node.selector_metadata.borrow().clone();
1268 self.occlusion.set(Default::default());
1269
1270 self.bind_to_parent_bounds(context);
1271 self.bind_selector_classes_listener(context);
1272 self.bind_occlusion_listener(context);
1273 self.bind_children_listener(context);
1274 self.bind_subtree_layout_hull(context);
1275 self.stores.mount(self.template_parent.upgrade().as_deref());
1276 }
1277
1278 fn recurse_emit_mount_updates(self: &Rc<Self>) {
1279 self.changed_listener.get();
1283 self.occlusion_listener.get();
1284 for child in self.children.get().iter() {
1285 child.recurse_emit_mount_updates();
1286 }
1287 for child in borrow!(self.sidecar_children).iter() {
1288 child.recurse_emit_mount_updates();
1289 }
1290 }
1291
1292 fn refresh_properties_scope(self: &Rc<Self>, template: &Rc<dyn InstanceNode>) {
1293 let common_properties = Rc::clone(&*borrow!(self.common_properties));
1294 let mut refreshed_scope = borrow!(*common_properties).retrieve_property_scope();
1295 refreshed_scope.extend(
1296 template
1297 .base()
1298 .properties_scope
1299 .build(Rc::clone(&*borrow!(self.properties))),
1300 );
1301 *borrow_mut!(self.properties_scope) = refreshed_scope;
1302 }
1303
1304 pub fn is_descendant_of(&self, other_expanded_node_id: &ExpandedNodeIdentifier) -> bool {
1308 if let Some(parent) = borrow!(self.render_parent).upgrade() {
1309 if parent.id.eq(other_expanded_node_id) {
1311 true
1312 } else {
1313 parent.is_descendant_of(other_expanded_node_id)
1314 }
1315 } else {
1316 false
1317 }
1318 }
1319
1320 pub fn create_children_detached(
1321 self: &Rc<Self>,
1322 templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
1323 context: &Rc<RuntimeContext>,
1324 template_parent: &Weak<ExpandedNode>,
1325 ) -> Vec<Rc<ExpandedNode>> {
1326 let containing_component = if borrow!(self.instance_node).base().flags().is_component {
1327 Rc::downgrade(&self)
1328 } else {
1329 Weak::clone(&self.containing_component)
1330 };
1331
1332 let mut children = Vec::new();
1333
1334 for (template, env) in templates {
1335 children.push(Self::new(
1336 template,
1337 env,
1338 context,
1339 Weak::clone(&containing_component),
1340 Weak::clone(&template_parent),
1341 ));
1342 }
1343 children
1344 }
1345
1346 pub(crate) fn current_attached_children(&self) -> Vec<Rc<ExpandedNode>> {
1347 borrow!(self.mounted_children).clone()
1348 }
1349
1350 pub(crate) fn rescue_exiting_child_matching(
1351 self: &Rc<Self>,
1352 predicate: impl Fn(&Rc<ExpandedNode>) -> bool,
1353 ) -> Option<Rc<ExpandedNode>> {
1354 let position = borrow!(self.exiting_children).iter().position(predicate)?;
1355 let child = borrow_mut!(self.exiting_children).remove(position);
1356 child
1357 .exit_retention_generation
1358 .set(child.exit_retention_generation.get().wrapping_add(1));
1359 borrow_mut!(self.exiting_child_generations).remove(&child.id);
1360 if borrow!(self.exiting_children).is_empty() {
1361 self.exit_cleanup_active.set(false);
1362 self.exit_cleanup_listener
1363 .replace_with(LocalProperty::new_with_name((), "exit transition cleanup"));
1364 }
1365 Some(child)
1366 }
1367
1368 fn sync_mounted_children_from_active_and_exiting(&self) -> Vec<Rc<ExpandedNode>> {
1369 let active_children = borrow!(self.active_children).clone();
1370 let exiting_children = borrow!(self.exiting_children).clone();
1371 if !self
1372 .active_children_view
1373 .read(|old| Self::same_children(old, &active_children))
1374 {
1375 self.active_children_view.set(active_children.clone());
1376 }
1377 if !self
1378 .exiting_children_view
1379 .read(|old| Self::same_children(old, &exiting_children))
1380 {
1381 self.exiting_children_view.set(exiting_children.clone());
1382 }
1383 let mut combined = exiting_children;
1384 combined.extend(active_children);
1385 *borrow_mut!(self.mounted_children) = combined.clone();
1386 combined
1387 }
1388
1389 fn has_child(children: &[Rc<ExpandedNode>], target: &Rc<ExpandedNode>) -> bool {
1390 children.iter().any(|child| Rc::ptr_eq(child, target))
1391 }
1392
1393 fn same_children(a: &[Rc<ExpandedNode>], b: &[Rc<ExpandedNode>]) -> bool {
1394 a.len() == b.len() && a.iter().zip(b).all(|(a, b)| Rc::ptr_eq(a, b))
1395 }
1396
1397 fn detach_child_for_reparent(&self, target: &Rc<ExpandedNode>) {
1398 let mut removed = false;
1399 {
1400 let active_children = &mut *borrow_mut!(self.active_children);
1401 let old_len = active_children.len();
1402 active_children.retain(|child| !Rc::ptr_eq(child, target));
1403 removed |= active_children.len() != old_len;
1404 }
1405 {
1406 let exiting_children = &mut *borrow_mut!(self.exiting_children);
1407 let old_len = exiting_children.len();
1408 exiting_children.retain(|child| !Rc::ptr_eq(child, target));
1409 removed |= exiting_children.len() != old_len;
1410 }
1411 if removed {
1412 self.sync_mounted_children_from_active_and_exiting();
1413 self.mark_non_reactive_update_subtree_dirty();
1414 }
1415 }
1416
1417 fn template_node_identifier(&self) -> Option<UniqueTemplateNodeIdentifier> {
1418 borrow!(self.instance_node)
1419 .base()
1420 .template_node_identifier
1421 .clone()
1422 }
1423
1424 fn transition_source_matches(
1425 sources: &[UniqueTemplateNodeIdentifier],
1426 own_identifier: Option<&UniqueTemplateNodeIdentifier>,
1427 requested_source: Option<&UniqueTemplateNodeIdentifier>,
1428 ) -> bool {
1429 if let Some(source) = requested_source {
1430 return sources.contains(source);
1431 }
1432 if sources.is_empty() {
1433 return true;
1434 }
1435 own_identifier
1436 .map(|identifier| sources.contains(identifier))
1437 .unwrap_or(false)
1438 }
1439
1440 fn has_exit_transition_for_source(
1441 &self,
1442 requested_source: Option<&UniqueTemplateNodeIdentifier>,
1443 ) -> bool {
1444 let instance_node = borrow!(self.instance_node);
1445 let transition_config = instance_node.base().transition_config();
1446 transition_config.has_exit
1447 && Self::transition_source_matches(
1448 &transition_config.exit_sources,
1449 instance_node.base().template_node_identifier.as_ref(),
1450 requested_source,
1451 )
1452 }
1453
1454 fn mark_non_reactive_update_subtree_dirty(&self) {
1455 if self.subtree_requires_non_reactive_update.replace(true) {
1456 return;
1457 }
1458 if let Some(parent) = borrow!(self.render_parent).upgrade() {
1459 parent.mark_non_reactive_update_subtree_dirty();
1460 }
1461 }
1462
1463 fn start_self_enter_transition_for_source(
1464 self: &Rc<Self>,
1465 context: &Rc<RuntimeContext>,
1466 requested_source: Option<&UniqueTemplateNodeIdentifier>,
1467 ) -> bool {
1468 let instance_node = borrow!(self.instance_node);
1469 let transition_config = instance_node.base().transition_config();
1470 if !transition_config.has_enter
1471 || !Self::transition_source_matches(
1472 &transition_config.enter_sources,
1473 instance_node.base().template_node_identifier.as_ref(),
1474 requested_source,
1475 )
1476 {
1477 return false;
1478 }
1479 drop(instance_node);
1480 self.begin_transition_run(context, TRANSITION_PHASE_ENTER);
1481 self.exit_started_millis.set(None);
1482 self.exit_timeout_warning_emitted.set(false);
1483 self.enable_enter_cleanup_listener(context);
1484 true
1485 }
1486
1487 fn start_self_enter_transition(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1488 let started = self.start_self_enter_transition_for_source(context, None);
1489 if started {
1490 if let Some(source) = self.template_node_identifier() {
1491 self.start_bound_enter_transitions(context, &source);
1492 }
1493 }
1494 }
1495
1496 fn start_self_exit_transition_for_source(
1497 self: &Rc<Self>,
1498 context: &Rc<RuntimeContext>,
1499 requested_source: Option<&UniqueTemplateNodeIdentifier>,
1500 ) -> bool {
1501 if !self.has_exit_transition_for_source(requested_source) {
1502 return false;
1503 }
1504 self.begin_transition_run(context, TRANSITION_PHASE_EXIT);
1505 self.disable_enter_cleanup_listener();
1506 self.exit_started_millis
1507 .set(Some((context.globals().get_elapsed_millis)()));
1508 self.exit_timeout_warning_emitted.set(false);
1509 true
1510 }
1511
1512 fn start_self_exit_transition(self: &Rc<Self>, context: &Rc<RuntimeContext>) -> bool {
1513 let started = self.start_self_exit_transition_for_source(context, None);
1514 if started {
1515 if let Some(source) = self.template_node_identifier() {
1516 self.start_bound_exit_transitions(context, &source);
1517 }
1518 }
1519 started
1520 }
1521
1522 fn begin_transition_run(self: &Rc<Self>, context: &Rc<RuntimeContext>, phase: u64) {
1523 let previous_phase = self.transition_phase.get();
1524 let is_takeover = matches!(
1525 (previous_phase, phase),
1526 (TRANSITION_PHASE_ENTER, TRANSITION_PHASE_EXIT)
1527 | (TRANSITION_PHASE_EXIT, TRANSITION_PHASE_ENTER)
1528 );
1529 self.transition_origin_frame
1530 .set(context.globals().elapsed_frames.get());
1531 self.transition_origin_millis
1532 .set(context.globals().elapsed_millis.get());
1533 self.transition_takeover.set(is_takeover);
1534 self.transition_generation
1535 .set(self.transition_generation.get().wrapping_add(1));
1536 self.activate_transition_clock(context);
1537 self.transition_phase.set(phase);
1538 *self.transition_selector_classes.borrow_mut() =
1539 Some(self.selector_metadata.borrow().classes.get());
1540 self.reset_removed_runtime_properties.set(true);
1541 self.reapply_runtime_settings(context);
1542 }
1543
1544 fn activate_transition_clock(&self, context: &Rc<RuntimeContext>) {
1545 let elapsed_frames = context.globals().elapsed_frames.clone();
1546 let origin_frame = self.transition_origin_frame.clone();
1547 let elapsed_frames_for_playhead = elapsed_frames.clone();
1548 let origin_frame_for_playhead = origin_frame.clone();
1549 self.transition_playhead
1550 .replace_with(LocalProperty::computed_with_name(
1551 move || {
1552 elapsed_frames_for_playhead
1553 .get()
1554 .saturating_sub(origin_frame_for_playhead.get()) as f64
1555 },
1556 &[elapsed_frames.untyped(), origin_frame.untyped()],
1557 "transition playhead",
1558 ));
1559
1560 let elapsed_millis = context.globals().elapsed_millis.clone();
1561 let origin_millis = self.transition_origin_millis.clone();
1562 let elapsed_millis_for_playhead = elapsed_millis.clone();
1563 let origin_millis_for_playhead = origin_millis.clone();
1564 self.transition_playhead_millis
1565 .replace_with(LocalProperty::computed_with_name(
1566 move || {
1567 elapsed_millis_for_playhead
1568 .get()
1569 .saturating_sub(origin_millis_for_playhead.get()) as f64
1570 },
1571 &[elapsed_millis.untyped(), origin_millis.untyped()],
1572 "transition playhead millis",
1573 ));
1574 }
1575
1576 fn deactivate_transition_clock(&self) {
1577 self.transition_playhead
1578 .replace_with(LocalProperty::new_with_name(0.0, "transition playhead"));
1579 self.transition_playhead_millis
1580 .replace_with(LocalProperty::new_with_name(
1581 0.0,
1582 "transition playhead millis",
1583 ));
1584 }
1585
1586 fn start_bound_enter_transitions(
1587 self: &Rc<Self>,
1588 context: &Rc<RuntimeContext>,
1589 source: &UniqueTemplateNodeIdentifier,
1590 ) {
1591 if let Some(containing_component) = self.containing_component.upgrade() {
1592 containing_component.start_descendant_enter_transitions_for_source(context, source);
1593 }
1594 }
1595
1596 fn start_bound_exit_transitions(
1597 self: &Rc<Self>,
1598 context: &Rc<RuntimeContext>,
1599 source: &UniqueTemplateNodeIdentifier,
1600 ) {
1601 if let Some(containing_component) = self.containing_component.upgrade() {
1602 containing_component.start_descendant_exit_transitions_for_source(context, source);
1603 }
1604 }
1605
1606 fn start_descendant_enter_transitions_for_source(
1607 self: &Rc<Self>,
1608 context: &Rc<RuntimeContext>,
1609 source: &UniqueTemplateNodeIdentifier,
1610 ) {
1611 for child in borrow!(self.mounted_children).iter() {
1612 child.start_enter_transition_for_source_recursive(context, source);
1613 }
1614 for child in borrow!(self.sidecar_children).iter() {
1615 child.start_enter_transition_for_source_recursive(context, source);
1616 }
1617 }
1618
1619 fn start_descendant_exit_transitions_for_source(
1620 self: &Rc<Self>,
1621 context: &Rc<RuntimeContext>,
1622 source: &UniqueTemplateNodeIdentifier,
1623 ) {
1624 for child in borrow!(self.mounted_children).iter() {
1625 child.start_exit_transition_for_source_recursive(context, source);
1626 }
1627 for child in borrow!(self.sidecar_children).iter() {
1628 child.start_exit_transition_for_source_recursive(context, source);
1629 }
1630 }
1631
1632 fn start_enter_transition_for_source_recursive(
1633 self: &Rc<Self>,
1634 context: &Rc<RuntimeContext>,
1635 source: &UniqueTemplateNodeIdentifier,
1636 ) {
1637 if self.template_node_identifier().as_ref() != Some(source) {
1638 self.start_self_enter_transition_for_source(context, Some(source));
1639 }
1640 for child in borrow!(self.mounted_children).iter() {
1641 child.start_enter_transition_for_source_recursive(context, source);
1642 }
1643 for child in borrow!(self.sidecar_children).iter() {
1644 child.start_enter_transition_for_source_recursive(context, source);
1645 }
1646 }
1647
1648 fn trigger_bound_enter_transitions_for_mounted_sources_recursive(
1649 self: &Rc<Self>,
1650 context: &Rc<RuntimeContext>,
1651 ) {
1652 if let Some(source) = self.template_node_identifier() {
1653 let should_trigger = {
1654 let instance_node = borrow!(self.instance_node);
1655 instance_node
1656 .base()
1657 .transition_config()
1658 .enter_sources
1659 .contains(&source)
1660 };
1661 if should_trigger {
1662 self.start_bound_enter_transitions(context, &source);
1663 }
1664 }
1665 for child in borrow!(self.mounted_children).iter() {
1666 child.trigger_bound_enter_transitions_for_mounted_sources_recursive(context);
1667 }
1668 for child in borrow!(self.sidecar_children).iter() {
1669 child.trigger_bound_enter_transitions_for_mounted_sources_recursive(context);
1670 }
1671 }
1672
1673 fn start_exit_transition_for_source_recursive(
1674 self: &Rc<Self>,
1675 context: &Rc<RuntimeContext>,
1676 source: &UniqueTemplateNodeIdentifier,
1677 ) {
1678 if self.template_node_identifier().as_ref() != Some(source) {
1679 self.start_self_exit_transition_for_source(context, Some(source));
1680 }
1681 for child in borrow!(self.mounted_children).iter() {
1682 child.start_exit_transition_for_source_recursive(context, source);
1683 }
1684 for child in borrow!(self.sidecar_children).iter() {
1685 child.start_exit_transition_for_source_recursive(context, source);
1686 }
1687 }
1688
1689 fn start_exit_transition_tree(self: &Rc<Self>, context: &Rc<RuntimeContext>) -> bool {
1690 let mut started = self.start_self_exit_transition(context);
1691 for child in borrow!(self.mounted_children).iter() {
1692 started |= child.start_exit_transition_tree(context);
1693 }
1694 started
1695 }
1696
1697 fn start_enter_transition_tree(self: &Rc<Self>, context: &Rc<RuntimeContext>) -> bool {
1698 let was_exiting = self.transition_phase.get() == TRANSITION_PHASE_EXIT;
1699 let started_here = self.start_self_enter_transition_for_source(context, None);
1700 if was_exiting && !started_here {
1701 self.transition_phase.set(TRANSITION_PHASE_IDLE);
1702 self.transition_takeover.set(false);
1703 self.deactivate_transition_clock();
1704 self.exit_started_millis.set(None);
1705 self.transition_selector_classes.borrow_mut().take();
1706 self.reset_removed_runtime_properties.set(true);
1707 self.reapply_runtime_settings(context);
1708 }
1709 let mut started = started_here;
1710 for child in borrow!(self.mounted_children).iter() {
1711 started |= child.start_enter_transition_tree(context);
1712 }
1713 if started_here {
1714 if let Some(source) = self.template_node_identifier() {
1715 self.start_bound_enter_transitions(context, &source);
1716 }
1717 }
1718 started
1719 }
1720
1721 fn retain_exiting_child(
1722 self: &Rc<Self>,
1723 child: Rc<ExpandedNode>,
1724 context: &Rc<RuntimeContext>,
1725 ) {
1726 if Self::has_child(&borrow!(self.exiting_children), &child) {
1727 return;
1728 }
1729 let generation = child.exit_retention_generation.get().wrapping_add(1);
1730 child.exit_retention_generation.set(generation);
1731 borrow_mut!(self.exiting_child_generations).insert(child.id, generation);
1732 borrow_mut!(self.exiting_children).push(child);
1733 self.enable_exit_cleanup_listener(context);
1734 }
1735
1736 fn self_exit_transition_complete(&self, context: &Rc<RuntimeContext>) -> bool {
1737 if self.transition_phase.get() != TRANSITION_PHASE_EXIT {
1738 return true;
1739 }
1740 let instance_node = borrow!(self.instance_node);
1741 let base = instance_node.base();
1742 let transition_config = base.transition_config().clone();
1743 let template_node_identifier = base.template_node_identifier.clone();
1744 let template_node_type_id = base.template_node_type_id.clone();
1745 drop(instance_node);
1746 let duration_complete = self.transition_duration_complete(
1747 transition_config.exit_frame_count,
1748 transition_config.exit_millis_count,
1749 &transition_config.exit_dynamic_durations,
1750 );
1751 let timeout_complete = self
1752 .exit_started_millis
1753 .get()
1754 .map(|started| {
1755 (context.globals().get_elapsed_millis)().saturating_sub(started)
1756 >= transition_config.timeout_ms as u128
1757 })
1758 .unwrap_or(false);
1759 if timeout_complete
1760 && !duration_complete
1761 && !self.exit_timeout_warning_emitted.replace(true)
1762 {
1763 log::warn!(
1764 "Exit transition timed out after {}ms and will be truncated for node {:?}{}{}. Check @out/@transition duration, percent keyframes, and dynamic duration expressions.",
1765 transition_config.timeout_ms,
1766 self.id,
1767 template_node_identifier
1768 .as_ref()
1769 .map(|identifier| format!(" template={}", identifier))
1770 .unwrap_or_default(),
1771 template_node_type_id
1772 .as_ref()
1773 .map(|type_id| format!(" type={}", type_id))
1774 .unwrap_or_default(),
1775 );
1776 }
1777 duration_complete || timeout_complete
1778 }
1779
1780 fn transition_duration_complete(
1781 &self,
1782 frame_count: u64,
1783 millis_count: Option<u64>,
1784 dynamic_durations: &[ValueDefinition],
1785 ) -> bool {
1786 let mut frame_count = frame_count as f64;
1787 let mut millis_count = millis_count.map(|value| value as f64).unwrap_or_default();
1788 let mut unresolved_dynamic_duration = false;
1789
1790 for duration_definition in dynamic_durations {
1791 match evaluate_timeline_duration(duration_definition, &self.stack) {
1792 Some(duration) if duration.is_frame_based() => {
1793 frame_count = frame_count.max(duration.as_frames_f64().max(0.0).ceil());
1794 }
1795 Some(duration) => {
1796 millis_count = millis_count.max(duration.as_milliseconds_f64().max(0.0).ceil());
1797 }
1798 None => unresolved_dynamic_duration = true,
1799 }
1800 }
1801
1802 if unresolved_dynamic_duration
1803 && frame_count <= f64::EPSILON
1804 && millis_count <= f64::EPSILON
1805 {
1806 frame_count = 100.0;
1807 }
1808
1809 let frame_complete =
1810 frame_count <= f64::EPSILON || self.transition_playhead.get() >= frame_count;
1811 let millis_complete =
1812 millis_count <= f64::EPSILON || self.transition_playhead_millis.get() >= millis_count;
1813 frame_complete && millis_complete
1814 }
1815
1816 fn self_enter_transition_complete(&self) -> bool {
1817 if self.transition_phase.get() != TRANSITION_PHASE_ENTER {
1818 return true;
1819 }
1820 let instance_node = borrow!(self.instance_node);
1821 let transition_config = instance_node.base().transition_config().clone();
1822 drop(instance_node);
1823 self.transition_duration_complete(
1824 transition_config.enter_frame_count,
1825 transition_config.enter_millis_count,
1826 &transition_config.enter_dynamic_durations,
1827 )
1828 }
1829
1830 fn enable_enter_cleanup_listener(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1831 if self.enter_cleanup_active.get() {
1832 return;
1833 }
1834 self.enter_cleanup_active.set(true);
1835 let weak_self = Rc::downgrade(self);
1836 let runtime_context = Rc::clone(context);
1837 let elapsed_frames = context.globals().elapsed_frames.clone();
1838 let elapsed_frames_dep = elapsed_frames.untyped();
1839 self.enter_cleanup_listener
1840 .replace_with(LocalProperty::computed_with_name(
1841 move || {
1842 let _ = elapsed_frames.get();
1843 if let Some(node) = weak_self.upgrade() {
1844 node.complete_self_enter_transition_if_needed(&runtime_context);
1845 }
1846 },
1847 &[elapsed_frames_dep],
1848 "enter transition cleanup",
1849 ));
1850 context.register_expanded_node_effect_property_named(
1851 self,
1852 &self.enter_cleanup_listener,
1853 "enter transition cleanup",
1854 );
1855 }
1856
1857 fn complete_self_enter_transition_if_needed(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1858 if !self.enter_cleanup_active.get() {
1859 return;
1860 }
1861 if self.transition_phase.get() != TRANSITION_PHASE_ENTER {
1862 self.disable_enter_cleanup_listener();
1863 return;
1864 }
1865 if self.self_enter_transition_complete() {
1866 self.transition_phase.set(TRANSITION_PHASE_IDLE);
1867 self.deactivate_transition_clock();
1868 self.disable_enter_cleanup_listener();
1869 self.transition_selector_classes.borrow_mut().take();
1870 self.reset_removed_runtime_properties.set(true);
1871 self.reapply_runtime_settings(context);
1872 }
1873 }
1874
1875 fn disable_enter_cleanup_listener(&self) {
1876 self.enter_cleanup_active.set(false);
1877 self.enter_cleanup_listener
1878 .replace_with(LocalProperty::new_with_name((), "enter transition cleanup"));
1879 }
1880
1881 fn exit_transition_tree_complete(self: &Rc<Self>, context: &Rc<RuntimeContext>) -> bool {
1882 self.self_exit_transition_complete(context)
1883 && borrow!(self.mounted_children)
1884 .iter()
1885 .all(|child| child.exit_transition_tree_complete(context))
1886 }
1887
1888 fn enable_exit_cleanup_listener(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1889 if self.exit_cleanup_active.get() {
1890 return;
1891 }
1892 self.exit_cleanup_active.set(true);
1893 let weak_self = Rc::downgrade(self);
1894 let cloned_context = Rc::clone(context);
1895 let elapsed_frames = context.globals().elapsed_frames.clone();
1896 let elapsed_frames_dep = elapsed_frames.untyped();
1897 self.exit_cleanup_listener
1898 .replace_with(LocalProperty::computed_with_name(
1899 move || {
1900 let _ = elapsed_frames.get();
1901 if let Some(node) = weak_self.upgrade() {
1902 node.prune_completed_exit_children(&cloned_context);
1903 }
1904 },
1905 &[elapsed_frames_dep],
1906 "exit transition cleanup",
1907 ));
1908 context.register_expanded_node_effect_property_named(
1909 self,
1910 &self.exit_cleanup_listener,
1911 "exit transition cleanup",
1912 );
1913 }
1914
1915 fn prune_completed_exit_children(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1916 let exiting = std::mem::take(&mut *borrow_mut!(self.exiting_children));
1917 if exiting.is_empty() {
1918 self.exit_cleanup_active.set(false);
1919 self.exit_cleanup_listener
1920 .replace_with(LocalProperty::new_with_name((), "exit transition cleanup"));
1921 return;
1922 }
1923
1924 let mut retained = Vec::new();
1925 let active = borrow!(self.active_children).clone();
1926 for child in exiting {
1927 let expected_generation = borrow_mut!(self.exiting_child_generations).remove(&child.id);
1928 if Self::has_child(&active, &child)
1929 || expected_generation != Some(child.exit_retention_generation.get())
1930 {
1931 continue;
1932 }
1933 if child.exit_transition_tree_complete(context) {
1934 child.recurse_unmount(context);
1935 } else {
1936 borrow_mut!(self.exiting_child_generations)
1937 .insert(child.id, child.exit_retention_generation.get());
1938 retained.push(child);
1939 }
1940 }
1941 *borrow_mut!(self.exiting_children) = retained;
1942 self.sync_mounted_children_from_active_and_exiting();
1943 self.children.invalidate();
1947 context.mark_occlusion_dirty();
1948
1949 if borrow!(self.exiting_children).is_empty() {
1950 self.exit_cleanup_active.set(false);
1951 self.exit_cleanup_listener
1952 .replace_with(LocalProperty::new_with_name((), "exit transition cleanup"));
1953 }
1954 }
1955
1956 pub fn attach_children(
1957 self: &Rc<Self>,
1958 new_children: Vec<Rc<ExpandedNode>>,
1959 context: &Rc<RuntimeContext>,
1960 parent_frame: &LocalProperty<Option<ExpandedNodeIdentifier>>,
1961 ) -> Vec<Rc<ExpandedNode>> {
1962 self.attach_children_with_liquid_glass_scope(
1963 new_children,
1964 context,
1965 parent_frame,
1966 &self.liquid_glass_scope,
1967 )
1968 }
1969
1970 pub fn attach_children_with_liquid_glass_scope(
1971 self: &Rc<Self>,
1972 new_children: Vec<Rc<ExpandedNode>>,
1973 context: &Rc<RuntimeContext>,
1974 parent_frame: &LocalProperty<Option<ExpandedNodeIdentifier>>,
1975 liquid_glass_scope: &LocalProperty<Option<NativeLiquidGlassScope>>,
1976 ) -> Vec<Rc<ExpandedNode>> {
1977 let mut bindings_changed = false;
1978 for child in new_children.iter() {
1979 let previous_parent = borrow!(child.render_parent).upgrade();
1980 let is_reparented = child.attached.get() > 0
1981 && previous_parent
1982 .as_ref()
1983 .is_some_and(|parent| parent.id != self.id);
1984 if is_reparented {
1985 child.mark_canvas_subtree_dirty_for_reparent(context, true);
1986 if let Some(previous_parent) = previous_parent.as_ref() {
1987 previous_parent.detach_child_for_reparent(child);
1988 }
1989 }
1990
1991 bindings_changed |=
1992 child.ensure_parent_bindings(self, context, parent_frame, liquid_glass_scope);
1993 if is_reparented {
1994 child.rebind_parent_bounds_for_mounted_subtree(context);
1995 child.mark_canvas_subtree_dirty_for_reparent(context, false);
1996 }
1997 }
1998 if Self::same_children(&borrow!(self.active_children), &new_children)
2001 && (self.attached.get() == 0
2002 || new_children.iter().all(|child| child.attached.get() > 0))
2003 {
2004 if bindings_changed {
2005 self.mark_non_reactive_update_subtree_dirty();
2006 }
2007 return self.current_attached_children();
2008 }
2009 let mut newly_mounted_children = Vec::new();
2010 if self.attached.get() > 0 {
2011 let old_active_children = borrow!(self.active_children).clone();
2012 for child in old_active_children.iter() {
2013 if Self::has_child(&new_children, child) {
2014 continue;
2015 }
2016 if child.transition_phase.get() == TRANSITION_PHASE_EXIT {
2017 if child.exit_transition_tree_complete(context) {
2018 Rc::clone(child).recurse_unmount(context);
2019 } else {
2020 self.retain_exiting_child(Rc::clone(child), context);
2021 }
2022 continue;
2023 }
2024 if child.start_exit_transition_tree(context) {
2025 self.retain_exiting_child(Rc::clone(child), context);
2026 } else {
2027 Rc::clone(child).recurse_unmount(context);
2028 }
2029 }
2030 for child in new_children.iter() {
2031 if !Self::has_child(&old_active_children, child) || child.attached.get() == 0 {
2032 if child.attached.get() > 0 {
2033 child.start_enter_transition_tree(context);
2034 } else {
2035 Rc::clone(child).recurse_mount(context);
2036 newly_mounted_children.push(Rc::clone(child));
2037 }
2038 }
2039 }
2040 }
2041 *borrow_mut!(self.active_children) = new_children;
2042 let mounted = self.sync_mounted_children_from_active_and_exiting();
2043 for child in newly_mounted_children {
2044 child.trigger_bound_enter_transitions_for_mounted_sources_recursive(context);
2045 }
2046 self.mark_non_reactive_update_subtree_dirty();
2047 mounted
2048 }
2049
2050 pub fn attach_sidecar_children(
2051 self: &Rc<Self>,
2052 new_children: Vec<Rc<ExpandedNode>>,
2053 context: &Rc<RuntimeContext>,
2054 parent_frame: &LocalProperty<Option<ExpandedNodeIdentifier>>,
2055 ) -> Vec<Rc<ExpandedNode>> {
2056 for child in new_children.iter() {
2057 child.ensure_parent_bindings(self, context, parent_frame, &self.liquid_glass_scope);
2058 }
2059 *borrow_mut!(self.sidecar_children) = new_children.clone();
2060 self.mark_non_reactive_update_subtree_dirty();
2061 new_children
2062 }
2063
2064 fn ensure_parent_bindings(
2065 self: &Rc<Self>,
2066 parent: &Rc<Self>,
2067 context: &Rc<RuntimeContext>,
2068 frame: &LocalProperty<Option<ExpandedNodeIdentifier>>,
2069 glass: &LocalProperty<Option<NativeLiquidGlassScope>>,
2070 ) -> bool {
2071 if let Some(owner) = parent.render_source_owner.get() {
2072 self.render_source_owner.set(Some(owner));
2073 }
2074 let properties = {
2075 let parent_cp = parent.get_common_properties();
2076 let parent_cp = borrow!(parent_cp);
2077 let cp = self.get_common_properties();
2078 let cp = borrow!(cp);
2079 vec![
2080 frame.untyped(),
2081 glass.untyped(),
2082 parent.suspended.untyped(),
2083 parent.transform_and_bounds.untyped(),
2084 parent.computed_opacity.untyped(),
2085 parent.computed_opacity_scopes.untyped(),
2086 parent_cp.padding_x.untyped(),
2087 parent_cp.padding_y.untyped(),
2088 self.container_frame.untyped(),
2089 self.measured_size.untyped(),
2090 cp._suspended.untyped(),
2091 cp.opacity.untyped(),
2092 cp.transform.untyped(),
2093 cp.width.untyped(),
2094 cp.height.untyped(),
2095 cp.x.untyped(),
2096 cp.y.untyped(),
2097 cp.anchor_x.untyped(),
2098 cp.anchor_y.untyped(),
2099 cp.scale_x.untyped(),
2100 cp.scale_y.untyped(),
2101 cp.skew_x.untyped(),
2102 cp.skew_y.untyped(),
2103 cp.rotate.untyped(),
2104 ]
2105 };
2106 let sources = ParentBindingSources {
2107 parent: parent.id,
2108 properties,
2109 };
2110 let same_parent = borrow!(self.render_parent)
2111 .upgrade()
2112 .is_some_and(|old| Rc::ptr_eq(&old, parent));
2113 if same_parent
2114 && borrow!(self.parent_binding_sources)
2115 .as_ref()
2116 .is_some_and(|old| old.same_as(&sources))
2117 {
2118 return false;
2119 }
2120 if self.attached.get() > 0 && context.viewport_dependency(self.id.to_u32()) {
2121 context.viewport_structure_changed();
2122 }
2123 *borrow_mut!(self.render_parent) = Rc::downgrade(parent);
2124 let frame = frame.clone();
2125 let deps = [frame.untyped()];
2126 self.parent_frame
2127 .replace_with(LocalProperty::computed(move || frame.get(), &deps));
2128 let glass = glass.clone();
2129 let deps = [glass.untyped()];
2130 self.liquid_glass_scope
2131 .replace_with(LocalProperty::computed(move || glass.get(), &deps));
2132 self.inherit_suspend(parent);
2133 self.bind_to_parent_bounds(context);
2134 *borrow_mut!(self.parent_binding_sources) = Some(sources);
2135 true
2136 }
2137
2138 fn rebind_parent_bounds_for_mounted_subtree(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2139 self.bind_to_parent_bounds(context);
2140 self.bind_occlusion_listener(context);
2141 let mounted_children = borrow!(self.mounted_children).clone();
2142 for child in mounted_children {
2143 child.rebind_parent_bounds_for_mounted_subtree(context);
2144 }
2145 let sidecar_children = borrow!(self.sidecar_children).clone();
2146 for child in sidecar_children {
2147 child.rebind_parent_bounds_for_mounted_subtree(context);
2148 }
2149 }
2150
2151 fn mark_canvas_subtree_dirty_for_reparent(
2152 self: &Rc<Self>,
2153 context: &Rc<RuntimeContext>,
2154 queue_retained_removal: bool,
2155 ) {
2156 let layer = self.occlusion.get().render_layer_id;
2157 context.set_canvas_dirty(layer);
2158 if borrow!(self.instance_node).base().flags().layer == Layer::Canvas {
2159 if queue_retained_removal {
2160 context.enqueue_canvas_node_removal(layer, self.id.to_u32());
2161 }
2162 context.mark_canvas_node_dirty(self.id);
2163 }
2164 context.mark_occlusion_dirty();
2165
2166 let mounted_children = borrow!(self.mounted_children).clone();
2167 for child in mounted_children {
2168 child.mark_canvas_subtree_dirty_for_reparent(context, queue_retained_removal);
2169 }
2170 let sidecar_children = borrow!(self.sidecar_children).clone();
2171 for child in sidecar_children {
2172 child.mark_canvas_subtree_dirty_for_reparent(context, queue_retained_removal);
2173 }
2174 }
2175
2176 fn bind_to_parent_bounds(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) {
2177 self.parent_binding_sources.borrow_mut().take();
2179 self.layout_binding_generation
2180 .set(self.layout_binding_generation.get().wrapping_add(1));
2181 self.subtree_layout_hull_listener.invalidate();
2182 self.children_listener.invalidate();
2183 #[cfg(test)]
2184 self.parent_binding_rebuilds
2185 .set(self.parent_binding_rebuilds.get() + 1);
2186 let render_parent = borrow!(self.render_parent).upgrade();
2187 let parent_transform_and_bounds = render_parent
2188 .as_ref()
2189 .map(|n| n.transform_and_bounds.clone())
2190 .unwrap_or_else(|| ctx.globals().viewport);
2191 let (parent_padding_x, parent_padding_y) = render_parent
2192 .as_ref()
2193 .map(|n| {
2194 let common_props = n.get_common_properties();
2195 let common_props = borrow!(common_props);
2196 (
2197 common_props.padding_x.clone(),
2198 common_props.padding_y.clone(),
2199 )
2200 })
2201 .unwrap_or_default();
2202 let container_frame = self.container_frame.clone();
2203 let deps = [
2204 parent_transform_and_bounds.untyped(),
2205 parent_padding_x.untyped(),
2206 parent_padding_y.untyped(),
2207 container_frame.untyped(),
2208 ];
2209 let effective_parent_transform_and_bounds = LocalProperty::computed(
2210 move || {
2211 apply_container_frame(
2212 apply_padding_frame(
2213 parent_transform_and_bounds.get(),
2214 parent_padding_x.get(),
2215 parent_padding_y.get(),
2216 ),
2217 container_frame.get(),
2218 )
2219 },
2220 &deps,
2221 );
2222 let common_props = borrow!(self.common_properties);
2223 let extra_transform = borrow!(common_props).transform.clone();
2224
2225 let transform_and_bounds = compute_tab(
2226 self.layout_properties(),
2227 extra_transform,
2228 effective_parent_transform_and_bounds,
2229 );
2230 self.transform_and_bounds.replace_with(transform_and_bounds);
2231
2232 let parent_opacity = borrow!(self.render_parent)
2233 .upgrade()
2234 .map(|n| n.computed_opacity.clone())
2235 .unwrap_or_else(|| LocalProperty::new(1.0));
2236 let common_props = self.get_common_properties();
2237 let self_opacity = borrow!(common_props).opacity.clone();
2238 let parent_scopes = render_parent
2239 .as_ref()
2240 .map(|n| n.computed_opacity_scopes.clone())
2241 .unwrap_or_else(|| LocalProperty::new(Vec::new()));
2242 let local_opacity = self_opacity.clone();
2243 let node_id = self.id.to_u32();
2244 let scope_deps = [parent_scopes.untyped(), local_opacity.untyped()];
2245 self.computed_opacity_scopes
2246 .replace_with(LocalProperty::computed(
2247 move || {
2248 let mut scopes = parent_scopes.get();
2249 if let Some(opacity) = local_opacity.get() {
2250 scopes.push(OpacityScope {
2251 node_id,
2252 opacity: opacity.to_float_0_1().clamp(0.0, 1.0) as f32,
2253 });
2254 }
2255 scopes
2256 },
2257 &scope_deps,
2258 ));
2259 let deps = [parent_opacity.untyped(), self_opacity.untyped()];
2260 self.computed_opacity.replace_with(LocalProperty::computed(
2261 move || {
2262 let parent = parent_opacity.get().clamp(0.0, 1.0);
2263 let local = self_opacity
2264 .get()
2265 .unwrap_or_default()
2266 .to_float_0_1()
2267 .clamp(0.0, 1.0);
2268 (parent * local).clamp(0.0, 1.0)
2269 },
2270 &deps,
2271 ));
2272 }
2273
2274 fn bind_occlusion_listener(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) {
2275 let instance_node = borrow!(self.instance_node);
2276 let mut deps: Vec<_> = borrow!(self.properties_scope)
2277 .iter()
2278 .filter(|(name, _)| instance_node.property_requires_occlusion_recompute(name))
2279 .map(|(_, v)| v.get_untyped_property().clone())
2280 .collect();
2281 drop(instance_node);
2282
2283 deps.extend([
2286 self.transform_and_bounds.untyped(),
2287 self.computed_opacity.untyped(),
2288 self.liquid_glass_scope.untyped(),
2289 self.get_common_properties().borrow().unclippable.untyped(),
2290 ]);
2291
2292 let context = Rc::clone(ctx);
2293 let id = self.id.to_u32();
2294 self.occlusion_listener
2295 .replace_with(LocalProperty::computed_with_name(
2296 move || {
2297 context.mark_node_occlusion_dirty(id);
2298 },
2299 &deps,
2300 "occlusion listener",
2301 ));
2302 ctx.register_expanded_node_effect_property_named(
2303 self,
2304 &self.occlusion_listener,
2305 "occlusion listener",
2306 );
2307 }
2308
2309 fn reapply_runtime_settings(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2310 let instance = Rc::clone(&*borrow!(self.instance_node));
2311 instance.base().bind_properties(self);
2312 self.reset_removed_runtime_properties.set(false);
2313 self.refresh_properties_scope(&instance);
2314 self.mark_non_reactive_update_subtree_dirty();
2315 context.mark_occlusion_dirty();
2316 context.set_canvas_dirty(self.occlusion.get().render_layer_id);
2317 }
2318
2319 fn bind_selector_classes_listener(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2320 let classes = self.selector_metadata.borrow().classes.clone();
2321 let previous = Rc::new(RefCell::new(classes.get()));
2322 let weak_self = Rc::downgrade(self);
2323 let runtime_context = Rc::clone(context);
2324 self.selector_classes_listener
2325 .replace_with(LocalProperty::computed_with_name(
2326 move || {
2327 let current = classes.get();
2328 if *previous.borrow() == current {
2329 return;
2330 }
2331 *previous.borrow_mut() = current;
2332 let Some(node) = weak_self.upgrade() else {
2333 return;
2334 };
2335 node.reset_removed_runtime_properties.set(true);
2336 node.reapply_runtime_settings(&runtime_context);
2337 },
2338 &[self.selector_metadata.borrow().classes.untyped()],
2339 "selector classes listener",
2340 ));
2341 context.register_expanded_node_effect_property_named(
2342 self,
2343 &self.selector_classes_listener,
2344 "selector classes listener",
2345 );
2346 }
2347
2348 fn bind_children_listener(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) {
2349 let deps = [self.children.untyped()];
2350 let weak_self = Rc::downgrade(self);
2351 let context = Rc::clone(ctx);
2352 let previous = RefCell::new(None);
2353 self.children_listener
2354 .replace_with(LocalProperty::computed_with_name(
2355 move || {
2356 let Some(node) = weak_self.upgrade() else {
2357 return;
2358 };
2359 if !node.child_structure_changed(&previous) {
2360 return;
2361 }
2362 #[cfg(test)]
2363 node.structural_children_updates
2364 .set(node.structural_children_updates.get() + 1);
2365 if borrow!(node.instance_node).base().flags().is_component
2366 || borrow!(node.expanded_projected_children).is_some()
2367 {
2368 node.compute_flattened_projected_children();
2369 }
2370 if borrow!(node.instance_node).base().flags().invisible_to_slot {
2374 let mut ancestor = node.template_parent.upgrade();
2375 while let Some(parent) = ancestor {
2376 if borrow!(parent.expanded_projected_children).is_some() {
2377 parent.compute_flattened_projected_children();
2378 break;
2379 }
2380 if !borrow!(parent.instance_node)
2381 .base()
2382 .flags()
2383 .invisible_to_slot
2384 {
2385 break;
2386 }
2387 ancestor = parent.template_parent.upgrade();
2388 }
2389 }
2390 context.mark_occlusion_dirty();
2391
2392 let (is_slot, is_component) = {
2393 let instance = borrow!(node.instance_node);
2394 let flags = instance.base().flags();
2395 (flags.is_slot, flags.is_component)
2396 };
2397 if !is_slot {
2398 if is_component {
2399 node.slot_projection_changed.set(());
2400 } else if let Some(containing_component) =
2401 node.containing_component.upgrade()
2402 {
2403 containing_component.slot_projection_changed.set(());
2404 }
2405 }
2406 },
2407 &deps,
2408 "children listener",
2409 ));
2410 ctx.register_expanded_node_effect_property_named(
2411 self,
2412 &self.children_listener,
2413 "children listener",
2414 );
2415 }
2416
2417 fn bind_subtree_layout_hull(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) {
2418 self.rebind_subtree_layout_hull();
2419 let deps = [self.children.untyped()];
2420 let weak_self = Rc::downgrade(self);
2421 let previous = RefCell::new(None);
2422 self.subtree_layout_hull_listener
2423 .replace_with(LocalProperty::computed_with_name(
2424 move || {
2425 let Some(node) = weak_self.upgrade() else {
2426 return;
2427 };
2428 if node.child_structure_changed(&previous) {
2429 node.rebind_subtree_layout_hull();
2430 }
2431 },
2432 &deps,
2433 "subtree layout hull listener",
2434 ));
2435 ctx.register_expanded_node_effect_property_named(
2436 self,
2437 &self.subtree_layout_hull_listener,
2438 "subtree layout hull listener",
2439 );
2440 }
2441 fn child_structure_changed(&self, previous: &RefCell<Option<ChildStructure>>) -> bool {
2442 let children = self.children.get();
2445 let ids = |children: &[Rc<ExpandedNode>]| {
2446 children.iter().map(|child| child.id).collect::<Vec<_>>()
2447 };
2448 let current = ChildStructure {
2451 binding_generation: self.layout_binding_generation.get(),
2452 rendered: ids(&children),
2453 active: ids(&borrow!(self.active_children)),
2454 exiting: ids(&borrow!(self.exiting_children)),
2455 };
2456 let mut previous = previous.borrow_mut();
2457 if previous.as_ref() == Some(¤t) {
2458 return false;
2459 }
2460 *previous = Some(current);
2461 true
2462 }
2463
2464 fn rebind_subtree_layout_hull(self: &Rc<Self>) {
2465 #[cfg(test)]
2466 self.layout_hull_rebuilds
2467 .set(self.layout_hull_rebuilds.get() + 1);
2468 let self_transform_and_bounds = self.transform_and_bounds.clone();
2469 let layout_properties = self.layout_properties();
2470 let common_props = self.get_common_properties();
2471 let (width, height, x, y, padding_x, padding_y) = {
2472 let common_props = borrow!(common_props);
2473 (
2474 common_props.width.clone(),
2475 common_props.height.clone(),
2476 common_props.x.clone(),
2477 common_props.y.clone(),
2478 common_props.padding_x.clone(),
2479 common_props.padding_y.clone(),
2480 )
2481 };
2482 let children = self.children.get();
2483 let has_children = !children.is_empty();
2484 let bounds_name = format!("layout bounds (node id: {})", self.id.0);
2485 let bounds_transform_and_bounds = self_transform_and_bounds.clone();
2486 let bounds = LocalProperty::computed_with_cutoff_and_name(
2487 move || bounds_transform_and_bounds.get().bounds,
2488 &[self_transform_and_bounds.untyped()],
2489 <(f64, f64)>::eq,
2490 &bounds_name,
2491 );
2492 let mut own_deps = vec![bounds.untyped()];
2493 if has_children {
2494 own_deps.extend([width.untyped(), height.untyped(), x.untyped(), y.untyped()]);
2495 } else {
2496 own_deps.push(layout_properties.untyped());
2497 }
2498
2499 let own_hull_name = format!("own layout hull (node id: {})", self.id.0);
2500 let own_hull = LocalProperty::computed_with_cutoff_and_name(
2501 move || {
2502 let bounds = bounds.get();
2503 let (contributes_x, contributes_y) = if has_children {
2504 (
2505 layout_axis_can_contribute_from_parts(width.get(), x.get()),
2506 layout_axis_can_contribute_from_parts(height.get(), y.get()),
2507 )
2508 } else {
2509 let layout_properties = layout_properties.get();
2510 (
2511 layout_axis_can_contribute(&layout_properties, Axis::X),
2512 layout_axis_can_contribute(&layout_properties, Axis::Y),
2513 )
2514 };
2515 LayoutHull::from_axis_ranges(
2516 contributes_x.then_some((0.0, bounds.0)),
2517 contributes_y.then_some((0.0, bounds.1)),
2518 )
2519 },
2520 &own_deps,
2521 crate::layout_hulls_equivalent,
2522 &own_hull_name,
2523 );
2524
2525 if !has_children {
2526 self.subtree_layout_hull.replace_with(own_hull);
2527 return;
2528 }
2529
2530 let mut projected_child_hulls = Vec::with_capacity(children.len());
2531 for child in children.iter() {
2532 let child_cp = child.get_common_properties();
2533 let child_layout_role = borrow!(child_cp).layout_role.clone();
2534 let projection_name = format!(
2535 "projected child layout hull (parent id: {}, child id: {})",
2536 self.id.0, child.id.0
2537 );
2538 projected_child_hulls.push(crate::projected_child_layout_hull_property(
2539 self_transform_and_bounds.clone(),
2540 padding_x.clone(),
2541 padding_y.clone(),
2542 child.transform_and_bounds.clone(),
2543 child.subtree_layout_hull.clone(),
2544 child_layout_role,
2545 &projection_name,
2546 ));
2547 }
2548
2549 let mut deps = vec![own_hull.untyped(), padding_x.untyped(), padding_y.untyped()];
2550 deps.extend(projected_child_hulls.iter().map(LocalProperty::untyped));
2551
2552 let property_name = format!("subtree layout hull (node id: {})", self.id.0);
2553 self.subtree_layout_hull
2554 .replace_with(LocalProperty::computed_with_cutoff_and_name(
2555 move || {
2556 let mut hull = own_hull.get();
2557 let mut children_hull = LayoutHull::default();
2558 for projected_hull in &projected_child_hulls {
2559 children_hull = children_hull.union(projected_hull.get());
2560 }
2561 hull = hull.union(add_symmetric_padding_to_content_layout_hull(
2562 children_hull,
2563 padding_x.get(),
2564 padding_y.get(),
2565 ));
2566
2567 hull
2568 },
2569 &deps,
2570 crate::layout_hulls_equivalent,
2571 &property_name,
2572 ));
2573 }
2574
2575 pub fn inherit_suspend(self: &Rc<Self>, node: &Rc<Self>) {
2576 self.parent_binding_sources.borrow_mut().take();
2577 let cp = self.get_common_properties();
2578 let self_suspended = borrow!(cp)._suspended.clone();
2579 let parent_suspended = node.suspended.clone();
2580 let deps = [parent_suspended.untyped(), self_suspended.untyped()];
2581 self.suspended.replace_with(LocalProperty::computed(
2582 move || {
2583 self_suspended
2584 .get()
2585 .unwrap_or_else(|| parent_suspended.get())
2586 },
2587 &deps,
2588 ));
2589 }
2590
2591 pub fn generate_children(
2592 self: &Rc<Self>,
2593 templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
2594 context: &Rc<RuntimeContext>,
2595 parent_frame: &LocalProperty<Option<ExpandedNodeIdentifier>>,
2596 is_mount: bool,
2597 ) -> Vec<Rc<ExpandedNode>> {
2598 self.generate_children_with_liquid_glass_scope(
2599 templates,
2600 context,
2601 parent_frame,
2602 &self.liquid_glass_scope,
2603 is_mount,
2604 )
2605 }
2606
2607 pub fn generate_children_with_liquid_glass_scope(
2608 self: &Rc<Self>,
2609 templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
2610 context: &Rc<RuntimeContext>,
2611 parent_frame: &LocalProperty<Option<ExpandedNodeIdentifier>>,
2612 liquid_glass_scope: &LocalProperty<Option<NativeLiquidGlassScope>>,
2613 is_mount: bool,
2614 ) -> Vec<Rc<ExpandedNode>> {
2615 let new_children = self.create_children_detached(templates, context, &Rc::downgrade(&self));
2616 let res = if is_mount {
2617 self.attach_children_with_liquid_glass_scope(
2618 new_children,
2619 context,
2620 parent_frame,
2621 liquid_glass_scope,
2622 )
2623 } else {
2624 for child in new_children.iter() {
2625 child.recurse_control_flow_expansion(context);
2626 }
2627
2628 new_children
2629 };
2630 res
2631 }
2632
2633 pub fn run_lifecycle_handlers(
2636 self: &Rc<Self>,
2637 handler_key: &str,
2638 context: &Rc<RuntimeContext>,
2639 ) {
2640 if self.attached.get() == 0 || self.suspended.get() {
2641 return;
2642 }
2643 let _graph = context.property_graph.enter();
2644 let _scope = self.property_binding_scope.borrow().enter();
2645 self.run_lifecycle_handlers_for_key(handler_key, context);
2646 }
2647
2648 pub fn recurse_update(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2649 context.drain_node_effects();
2652 self.recurse_update_mounted(context);
2653 context.drain_node_effects();
2654 }
2655
2656 fn recurse_update_mounted(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2657 if self.attached.get() == 0 || !self.subtree_requires_non_reactive_update.get() {
2658 return;
2659 }
2660 let _graph = context.property_graph.enter();
2661 let _scope = self.property_binding_scope.borrow().enter();
2662 let instance_node = Rc::clone(&*borrow!(self.instance_node));
2663 if !self.suppresses_native_presentation()
2664 && instance_node.requires_non_reactive_update(self)
2665 {
2666 instance_node.update(&self, context);
2667 }
2668 let mut subtree_requires_non_reactive_update = !self.suppresses_native_presentation()
2669 && Rc::clone(&*borrow!(self.instance_node)).requires_non_reactive_update(self);
2670 let children = borrow!(self.mounted_children).clone();
2671 for child in children.iter() {
2672 child.recurse_update_mounted(context);
2673 subtree_requires_non_reactive_update |=
2674 child.subtree_requires_non_reactive_update.get();
2675 }
2676 let sidecar_children = borrow!(self.sidecar_children).clone();
2677 for child in sidecar_children.iter() {
2678 child.recurse_update_mounted(context);
2679 subtree_requires_non_reactive_update |=
2680 child.subtree_requires_non_reactive_update.get();
2681 }
2682 self.subtree_requires_non_reactive_update
2683 .set(subtree_requires_non_reactive_update);
2684 }
2685
2686 pub fn recurse_sync_import_settings(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2687 if borrow!(self.instance_node).base().flags().is_component {
2688 self.sync_imported_settings(context);
2689 }
2690 for child in self.children.get().iter() {
2691 child.recurse_sync_import_settings(context);
2692 }
2693 for child in borrow!(self.sidecar_children).iter() {
2694 child.recurse_sync_import_settings(context);
2695 }
2696 }
2697
2698 fn recurse_reapply_runtime_settings_for_component_owner(
2699 self: &Rc<Self>,
2700 owner_component_id: ExpandedNodeIdentifier,
2701 context: &Rc<RuntimeContext>,
2702 ) {
2703 if self.is_import_settings_node() {
2704 return;
2705 }
2706
2707 let owned_by_component = self
2708 .containing_component
2709 .upgrade()
2710 .map(|component| component.id == owner_component_id)
2711 .unwrap_or(false);
2712
2713 if owned_by_component {
2714 self.reset_removed_runtime_properties.set(true);
2715 let instance = borrow!(self.instance_node).clone();
2716 self.recreate_with_new_data(instance, context);
2717 }
2718
2719 let children = self.children.get();
2720 for child in children.iter() {
2721 child.recurse_reapply_runtime_settings_for_component_owner(owner_component_id, context);
2722 }
2723
2724 let sidecar_children = borrow!(self.sidecar_children).clone();
2725 for child in sidecar_children.iter() {
2726 child.recurse_reapply_runtime_settings_for_component_owner(owner_component_id, context);
2727 }
2728 }
2729
2730 pub fn recurse_control_flow_expansion(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2731 borrow!(self.instance_node)
2732 .clone()
2733 .handle_control_flow_node_expansion(&self, context);
2734 }
2735
2736 pub fn recurse_mount(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2737 let _graph = context.property_graph.enter();
2738 if self.attached.get() == 0 && self.stores.is_closed() {
2739 self.prepare_remount(context);
2740 }
2741 self.property_binding_scope.borrow().resume();
2742 let _scope = self.property_binding_scope.borrow().enter();
2743 if self.attached.get() == 0 {
2744 self.stores.mount(self.template_parent.upgrade().as_deref());
2745 let scope: Vec<_> = borrow!(self.properties_scope).values().cloned().collect();
2748 for property in scope {
2749 property.get_as_pax_value();
2750 }
2751 borrow!(self.instance_node)
2753 .clone()
2754 .handle_setup_projected_children(&self, context);
2755
2756 if let Some(projected_children) = borrow!(self.expanded_projected_children).as_ref() {
2758 for projected_child in projected_children {
2759 projected_child.recurse_control_flow_expansion(context);
2760 }
2761 self.compute_flattened_projected_children();
2763 }
2764 self.attached.set(self.attached.get() + 1);
2765 self.start_self_enter_transition(context);
2766 context.add_to_cache(&self);
2767 self.run_lifecycle_handlers_for_key("mount", context);
2768 if !self.suppresses_native_presentation() {
2772 borrow!(self.instance_node)
2773 .clone()
2774 .handle_mount(&self, context);
2775 }
2776 context.register_expanded_node_effect_property_named(
2777 self,
2778 &self.changed_listener,
2779 "changed listener",
2780 );
2781 context.register_viewport_proximity(self);
2782 }
2783 }
2784
2785 pub fn async_scope(&self, context: &Rc<RuntimeContext>) -> Result<AsyncScope, AsyncError> {
2786 if self.attached.get() == 0 {
2787 return Err(AsyncError::Closed);
2788 }
2789 if let Some(scope) = self.async_scope.borrow().as_ref() {
2790 return Ok(scope.clone());
2791 }
2792 let scope = context.application.context().async_scope()?.child()?;
2793 let binding = self.property_binding_scope.borrow().clone();
2794 scope.set_callback_context(move |callback| binding.with_graph(callback));
2795 *self.async_scope.borrow_mut() = Some(scope.clone());
2796 Ok(scope)
2797 }
2798
2799 pub fn recurse_unmount(self: Rc<Self>, context: &Rc<RuntimeContext>) {
2800 if self.attached.get() != 1 {
2801 return;
2802 }
2803 let _graph = context.property_graph.enter();
2804 let _scope = self.property_binding_scope.borrow().enter();
2805 if self.attached.get() == 1 {
2810 if let Some(scope) = self.async_scope.borrow_mut().take() {
2811 scope.cancel();
2812 }
2813 self.attached.set(self.attached.get() - 1);
2814 context.remove_from_cache(&self);
2815 for child in borrow!(self.mounted_children).iter() {
2816 Rc::clone(child).recurse_unmount(context);
2817 }
2818 if !self.suppresses_native_presentation() {
2819 borrow!(self.instance_node).handle_unmount(&self, context);
2820 }
2821 self.run_lifecycle_handlers_for_key("unmount", context);
2822
2823 if !self.is_render_source()
2824 && self.instance_node.borrow().base().flags().layer == Layer::Canvas
2825 {
2826 context.enqueue_canvas_node_removal(
2827 self.occlusion.get().render_layer_id,
2828 self.id.to_u32(),
2829 );
2830 context.set_canvas_dirty(self.occlusion.get().render_layer_id);
2831 }
2832
2833 self.occlusion.set(Default::default());
2835 self.browser_content_layer_id.set(None);
2836 self.changed_listener.replace_with(LocalProperty::default());
2837 self.selector_classes_listener
2838 .replace_with(LocalProperty::default());
2839 borrow_mut!(self.subscriptions).clear();
2840 borrow_mut!(self.active_children).clear();
2841 borrow_mut!(self.exiting_children).clear();
2842 borrow_mut!(self.exiting_child_generations).clear();
2843 borrow_mut!(self.mounted_children).clear();
2844 borrow_mut!(self.settings_motion).clear();
2845 borrow_mut!(self.import_settings_transition).take();
2846 borrow_mut!(self.imported_settings_layers).clear();
2847 borrow_mut!(self.imported_settings_signature).clear();
2848 borrow_mut!(self.resolved_property_columns).clear();
2849 borrow_mut!(self.resolved_property_provenance).clear();
2850 self.reset_removed_runtime_properties.set(false);
2851 self.active_children_view.set(Vec::new());
2852 self.exiting_children_view.set(Vec::new());
2853 self.transition_phase.set(TRANSITION_PHASE_IDLE);
2854 self.transition_takeover.set(false);
2855 self.transition_selector_classes.borrow_mut().take();
2856 self.deactivate_transition_clock();
2857 self.enter_cleanup_active.set(false);
2858 self.enter_cleanup_listener
2859 .replace_with(LocalProperty::new_with_name((), "enter transition cleanup"));
2860 self.exit_cleanup_active.set(false);
2861 self.exit_cleanup_listener
2862 .replace_with(LocalProperty::default());
2863 self.stores.close();
2864 self.children.replace_with(LocalProperty::new(Vec::new()));
2868 self.children_listener
2869 .replace_with(LocalProperty::default());
2870 self.occlusion_listener
2871 .replace_with(LocalProperty::default());
2872 self.subtree_layout_hull_listener
2873 .replace_with(LocalProperty::default());
2874 self.content_measurement_listener
2875 .replace_with(LocalProperty::default());
2876 self.content_measurement_rebind_listener
2877 .replace_with(LocalProperty::default());
2878 self.content_measurement_bound.set(false);
2879 borrow_mut!(self.sidecar_children).clear();
2880 borrow_mut!(self.expanded_projected_children).take();
2881 self.expanded_and_flattened_projected_children
2882 .set(Vec::new());
2883 self.flattened_projected_children_count.set(0);
2884 self.parent_binding_sources.borrow_mut().take();
2885 self.transform_and_bounds
2886 .replace_with(LocalProperty::default());
2887 self.computed_opacity.replace_with(LocalProperty::new(1.0));
2888 self.subtree_layout_hull
2889 .replace_with(LocalProperty::default());
2890 self.measured_size.set(None);
2891 *borrow_mut!(self.common_properties) =
2892 Rc::new(RefCell::new(CommonProperties::default()));
2893 self.selector_metadata.borrow_mut().id = LocalProperty::default();
2894 self.selector_metadata.borrow_mut().classes = LocalProperty::default();
2895 borrow_mut!(self.properties_scope).clear();
2896 *borrow_mut!(self.properties) =
2897 Rc::new(RefCell::new(PaxAny::Builtin(PaxValue::default())));
2898 self.property_binding_scope.borrow().close();
2899 }
2900 }
2901
2902 pub fn is_import_settings_node(&self) -> bool {
2903 borrow!(self.instance_node)
2904 .base()
2905 .template_node_type_id
2906 .as_ref()
2907 .and_then(|type_id| type_id.get_pascal_identifier())
2908 .as_deref()
2909 == Some("ImportSettings")
2910 }
2911
2912 pub fn is_in_import_settings_subtree(&self) -> bool {
2913 let mut current = borrow!(self.render_parent).upgrade();
2914 while let Some(node) = current {
2915 if node.is_import_settings_node() {
2916 return true;
2917 }
2918 current = borrow!(node.render_parent).upgrade();
2919 }
2920 false
2921 }
2922
2923 fn collect_imported_settings_from_node(
2924 node: &Rc<Self>,
2925 in_import_settings: bool,
2926 descend_components: bool,
2927 providers: &mut Vec<DiscoveredSettingsProvider>,
2928 transition: Option<LocalProperty<Option<crate::SettingsTransitionConfig>>>,
2929 ) {
2930 if node.is_import_settings_node() {
2931 let sidecar_children = borrow!(node.sidecar_children).clone();
2932 for child in sidecar_children.iter() {
2933 Self::collect_imported_settings_from_node(
2934 child,
2935 true,
2936 true,
2937 providers,
2938 transition
2939 .clone()
2940 .or_else(|| node.import_settings_transition.borrow().clone()),
2941 );
2942 }
2943 return;
2944 }
2945
2946 if borrow!(node.instance_node).base().flags().is_component {
2947 if in_import_settings {
2948 if borrow!(node.instance_node)
2949 .base()
2950 .component_settings
2951 .as_ref()
2952 .is_some_and(|settings| !settings.is_empty())
2953 {
2954 providers.push(DiscoveredSettingsProvider {
2955 node: Rc::clone(node),
2956 transition: transition.clone(),
2957 });
2958 }
2959 let children = node.children.get();
2960 for child in children.iter() {
2961 Self::collect_imported_settings_from_node(
2962 child,
2963 false,
2964 true,
2965 providers,
2966 transition.clone(),
2967 );
2968 }
2969 let sidecar_children = borrow!(node.sidecar_children).clone();
2970 for child in sidecar_children.iter() {
2971 Self::collect_imported_settings_from_node(
2972 child,
2973 false,
2974 true,
2975 providers,
2976 transition.clone(),
2977 );
2978 }
2979 return;
2980 }
2981 if !descend_components {
2982 return;
2983 }
2984 }
2985
2986 let children = node.children.get();
2987 for child in children.iter() {
2988 Self::collect_imported_settings_from_node(
2989 child,
2990 in_import_settings,
2991 descend_components,
2992 providers,
2993 transition.clone(),
2994 );
2995 }
2996 let sidecar_children = borrow!(node.sidecar_children).clone();
2997 for child in sidecar_children.iter() {
2998 Self::collect_imported_settings_from_node(
2999 child,
3000 in_import_settings,
3001 descend_components,
3002 providers,
3003 transition.clone(),
3004 );
3005 }
3006 }
3007
3008 pub fn sync_imported_settings(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
3009 let mut providers = Vec::new();
3010 let children = self.children.get();
3011 for child in children.iter() {
3012 Self::collect_imported_settings_from_node(child, false, false, &mut providers, None);
3013 }
3014 let sidecar_children = borrow!(self.sidecar_children).clone();
3015 for child in sidecar_children.iter() {
3016 Self::collect_imported_settings_from_node(child, false, false, &mut providers, None);
3017 }
3018
3019 let signature = providers
3020 .iter()
3021 .map(DiscoveredSettingsProvider::signature)
3022 .collect::<Vec<_>>();
3023 if *borrow!(self.imported_settings_signature) == signature {
3024 return;
3025 }
3026
3027 *borrow_mut!(self.imported_settings_signature) = signature;
3028 *borrow_mut!(self.imported_settings_layers) = providers
3029 .into_iter()
3030 .map(DiscoveredSettingsProvider::materialize)
3031 .collect();
3032
3033 let root_children = self.children.get();
3034 for child in root_children.iter() {
3035 if child.is_import_settings_node() {
3036 continue;
3037 }
3038 child.recurse_reapply_runtime_settings_for_component_owner(self.id, context);
3039 child.recurse_emit_mount_updates();
3040 }
3041 }
3042
3043 pub fn recurse_render_queue(
3044 self: &Rc<Self>,
3045 ctx: &Rc<RuntimeContext>,
3046 rcs: &mut dyn RenderContext,
3047 ) {
3048 if self.is_render_source() {
3049 return;
3050 }
3051 let cp = self.get_common_properties();
3052 let cp = borrow!(cp);
3053 if cp.unclippable.get().unwrap_or(false) {
3054 ctx.queue_render(Rc::clone(&self));
3055 } else {
3056 self.recurse_render(ctx, rcs);
3057 }
3058 }
3059
3060 pub(crate) fn render_parent_node(&self) -> Option<Rc<ExpandedNode>> {
3061 borrow!(self.render_parent).upgrade()
3062 }
3063
3064 pub(crate) fn is_unclippable(&self) -> bool {
3065 let cp = self.get_common_properties();
3066 let cp = borrow!(cp);
3067 cp.unclippable.get().unwrap_or(false)
3068 }
3069
3070 pub(crate) fn recurse_render_filtered(
3071 self: &Rc<Self>,
3072 ctx: &Rc<RuntimeContext>,
3073 rcs: &mut dyn RenderContext,
3074 render_path_nodes: &HashSet<ExpandedNodeIdentifier>,
3075 dirty_nodes: &HashSet<ExpandedNodeIdentifier>,
3076 stats: &mut FilteredRenderStats,
3077 ) {
3078 if self.is_render_source() {
3079 return;
3080 }
3081 stats.path_nodes_visited += 1;
3082 #[cfg(feature = "shaders")]
3083 if self.render_shader_effect(ctx, rcs) {
3084 return;
3085 }
3086 borrow!(self.instance_node).handle_pre_render(&self, ctx, rcs);
3087 for child in self.children.get().iter().rev() {
3088 if render_path_nodes.contains(&child.id) {
3089 child.recurse_render_filtered(ctx, rcs, render_path_nodes, dirty_nodes, stats);
3090 } else {
3091 stats.skipped_subtrees += 1;
3092 }
3093 }
3094 if dirty_nodes.contains(&self.id) {
3095 borrow!(self.instance_node).render(&self, ctx, rcs);
3096 stats.dirty_nodes_rendered += 1;
3097 }
3098 borrow!(self.instance_node).handle_post_render(&self, ctx, rcs);
3099 }
3100
3101 pub fn recurse_render(self: &Rc<Self>, ctx: &Rc<RuntimeContext>, rcs: &mut dyn RenderContext) {
3102 if self.is_render_source() {
3103 return;
3104 }
3105 #[cfg(feature = "shaders")]
3106 if self.render_shader_effect(ctx, rcs) {
3107 return;
3108 }
3109 borrow!(self.instance_node).handle_pre_render(&self, ctx, rcs);
3110 for child in self.children.get().iter().rev() {
3111 child.recurse_render_queue(ctx, rcs);
3112 }
3113 borrow!(self.instance_node).render(&self, ctx, rcs);
3114 borrow!(self.instance_node).handle_post_render(&self, ctx, rcs);
3115 }
3116
3117 pub fn recurse_render_alpha_source(
3120 self: &Rc<Self>,
3121 ctx: &Rc<RuntimeContext>,
3122 rcs: &mut dyn RenderContext,
3123 ) {
3124 assert!(self.is_render_source() && rcs.alpha_source_layer().is_some());
3125 let mut deferred = Vec::new();
3126 self.render_alpha_source_inner(ctx, rcs, &mut deferred);
3127 let mut next = 0;
3128 while next < deferred.len() {
3129 let node = Rc::clone(&deferred[next]);
3130 next += 1;
3131 node.render_alpha_source_inner(ctx, rcs, &mut deferred);
3132 }
3133 }
3134
3135 fn render_alpha_source_inner(
3136 self: &Rc<Self>,
3137 ctx: &Rc<RuntimeContext>,
3138 rcs: &mut dyn RenderContext,
3139 deferred: &mut Vec<Rc<ExpandedNode>>,
3140 ) {
3141 #[cfg(feature = "shaders")]
3142 assert!(
3143 borrow!(self.instance_node).shader_effect(self).is_none(),
3144 "shader effects inside detached mask sources are unsupported"
3145 );
3146 let supported = borrow!(self.instance_node).supports_alpha_source_render();
3147 let structural = {
3148 let instance = borrow!(self.instance_node);
3149 let flags = instance.base().flags();
3150 flags.layer == Layer::DontCare || flags.is_component
3151 };
3152 if !supported && !structural {
3153 return;
3154 }
3155 if supported {
3156 borrow!(self.instance_node).handle_pre_render(self, ctx, rcs);
3157 }
3158 for child in self.children.get().iter().rev() {
3159 if child.is_unclippable() {
3160 deferred.push(Rc::clone(child));
3161 } else {
3162 child.render_alpha_source_inner(ctx, rcs, deferred);
3163 }
3164 }
3165 if supported {
3166 borrow!(self.instance_node).render(self, ctx, rcs);
3167 borrow!(self.instance_node).handle_post_render(self, ctx, rcs);
3168 }
3169 }
3170
3171 pub fn with_properties_unwrapped<T: ToFromPaxAny, R>(
3177 &self,
3178 callback: impl FnOnce(&mut T) -> R,
3179 ) -> R {
3180 self.try_with_properties_unwrapped(callback)
3181 .expect("properties not of expected type")
3182 }
3183
3184 pub fn try_with_properties_unwrapped<T: ToFromPaxAny, R>(
3185 &self,
3186 callback: impl FnOnce(&mut T) -> R,
3187 ) -> Option<R> {
3188 let properties = borrow_mut!(self.properties);
3190 let mut borrowed = borrow_mut!(properties);
3191 let Ok(mut val) = T::mut_from_pax_any(&mut *borrowed) else {
3193 return None;
3194 };
3195 self.property_binding_scope
3196 .borrow()
3197 .with_graph(|| Some(callback(&mut val)))
3198 }
3199
3200 pub fn recurse_visit_postorder(self: &Rc<Self>, func: &mut impl FnMut(&Rc<Self>)) {
3201 self.compute_flattened_projected_children();
3204 for child in self.children.get().iter().rev() {
3205 child.recurse_visit_postorder(func)
3206 }
3207 func(self);
3208 }
3209
3210 pub fn get_node_context(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) -> NodeContext {
3211 let globals = ctx.globals();
3212 let viewport = super::viewport_info_property(&globals.viewport);
3213 let target = LocalProperty::new(globals.target);
3214 let t_and_b = self.transform_and_bounds.clone();
3215 let deps = [t_and_b.untyped()];
3216 let bounds_name = format!("node context bounds (node id: {})", self.id.0);
3217 let bounds_self = LocalProperty::computed_with_cutoff_and_name(
3218 move || t_and_b.get().bounds,
3219 &deps,
3220 <(f64, f64)>::eq,
3221 &bounds_name,
3222 );
3223 let render_parent = borrow!(self.render_parent).upgrade();
3224 let t_and_b_parent = render_parent
3225 .as_ref()
3226 .map(|parent| parent.transform_and_bounds.clone())
3227 .unwrap_or_else(|| globals.viewport.clone());
3228 let (parent_padding_x, parent_padding_y) = render_parent
3229 .as_ref()
3230 .map(|parent| {
3231 let common_props = parent.get_common_properties();
3232 let common_props = borrow!(common_props);
3233 (
3234 common_props.padding_x.clone(),
3235 common_props.padding_y.clone(),
3236 )
3237 })
3238 .unwrap_or_default();
3239 let deps = [
3240 t_and_b_parent.untyped(),
3241 parent_padding_x.untyped(),
3242 parent_padding_y.untyped(),
3243 ];
3244 let bounds_parent = LocalProperty::computed(
3245 move || {
3246 apply_padding_frame(
3247 t_and_b_parent.get(),
3248 parent_padding_x.get(),
3249 parent_padding_y.get(),
3250 )
3251 .bounds
3252 },
3253 &deps,
3254 );
3255
3256 let owns_projected_children = borrow!(self.instance_node).base().flags().is_component
3257 || borrow!(self.expanded_projected_children).is_some();
3258
3259 let projected_children_count = if owns_projected_children {
3260 self.flattened_projected_children_count.clone()
3261 } else {
3262 self.containing_component
3263 .upgrade()
3264 .map(|v| v.flattened_projected_children_count.clone())
3265 .unwrap_or_default()
3266 };
3267
3268 let projected_children = if owns_projected_children {
3269 self.expanded_and_flattened_projected_children.clone()
3270 } else {
3271 self.containing_component
3272 .upgrade()
3273 .map(|v| v.expanded_and_flattened_projected_children.clone())
3274 .unwrap_or_default()
3275 };
3276
3277 let projected_children_changed = if owns_projected_children {
3278 self.projected_children_changed.clone()
3279 } else {
3280 self.containing_component
3281 .upgrade()
3282 .map(|v| v.projected_children_changed.clone())
3283 .unwrap_or_default()
3284 };
3285 let received_children = match borrow!(self.instance_node).received_children_source() {
3289 ReceivedChildrenSource::Owned => self.active_children_view.clone(),
3290 ReceivedChildrenSource::Projected => {
3291 if owns_projected_children {
3292 self.expanded_and_flattened_projected_children.clone()
3293 } else {
3294 self.containing_component
3295 .upgrade()
3296 .map(|v| v.expanded_and_flattened_projected_children.clone())
3297 .unwrap_or_default()
3298 }
3299 }
3300 };
3301 let received_children_count_source = received_children.clone();
3302 let received_children_count = LocalProperty::computed(
3303 move || received_children_count_source.get().len(),
3304 &[received_children.untyped()],
3305 );
3306 let received_children_signal = received_children.clone();
3307 let received_children_changed = LocalProperty::computed(
3308 move || {
3309 let _ = received_children_signal.get();
3310 },
3311 &[received_children.untyped()],
3312 );
3313 let retained_received_children =
3317 match borrow!(self.instance_node).received_children_source() {
3318 ReceivedChildrenSource::Owned => self.exiting_children_view.clone(),
3319 ReceivedChildrenSource::Projected
3320 if borrow!(self.expanded_projected_children).is_some() =>
3321 {
3322 self.exiting_children_view.clone()
3323 }
3324 ReceivedChildrenSource::Projected => LocalProperty::default(),
3325 };
3326 let retained_received_children_signal = retained_received_children.clone();
3327 let retained_received_children_changed = LocalProperty::computed(
3328 move || {
3329 let _ = retained_received_children_signal.get();
3330 },
3331 &[retained_received_children.untyped()],
3332 );
3333
3334 let last_frame = Rc::new(RefCell::new(globals.elapsed_frames.get()));
3335 let suspended = self.suspended.clone();
3336 let elapsed_frames = globals.elapsed_frames.clone();
3337 let elapsed_millis = globals.elapsed_millis.clone();
3338 let deps = [elapsed_frames.untyped(), suspended.untyped()];
3339 let elapsed_frames_frozen_if_suspended = LocalProperty::computed(
3343 move || {
3344 if suspended.get() {
3345 *borrow!(last_frame)
3346 } else {
3347 let val = elapsed_frames.get();
3348 *borrow_mut!(last_frame) = val;
3349 val
3350 }
3351 },
3352 &deps,
3353 );
3354 NodeContext {
3358 slot_index: self.slot_index.clone(),
3359 expression_stack: Rc::clone(&self.stack),
3360 store_generation: self.stores.generation(),
3361 expanded_node: Rc::downgrade(&self),
3362 containing_component: Weak::clone(&self.containing_component),
3363 elapsed_frames: elapsed_frames_frozen_if_suspended,
3364 elapsed_millis,
3365 gyro: globals.gyro.clone(),
3366 accel: globals.accel.clone(),
3367 bounds_self,
3368 bounds_parent,
3369 measured_size: self.measured_size.clone(),
3370 subtree_layout_hull: self.subtree_layout_hull.clone(),
3371 runtime_context: ctx.clone(),
3372 platform: globals.platform.clone(),
3373 os: globals.os.clone(),
3374 target,
3375 viewport,
3376 get_elapsed_millis: globals.get_elapsed_millis,
3377 projected_children_count,
3378 projected_children,
3379 node_transform_and_bounds: self.transform_and_bounds.get(),
3380 projected_children_changed,
3381 received_children,
3382 received_children_count,
3383 received_children_changed,
3384 retained_received_children,
3385 retained_received_children_changed,
3386 #[cfg(feature = "designtime")]
3387 designtime: globals.designtime.clone(),
3388 }
3389 }
3390
3391 pub fn get_common_properties(&self) -> Rc<RefCell<CommonProperties>> {
3392 Rc::clone(&*borrow!(self.common_properties))
3393 }
3394
3395 pub fn is_layout_breakout(&self) -> bool {
3396 let common_props = self.get_common_properties();
3397 let is_breakout = borrow!(common_props).layout_role.get() == Some(LayoutRole::Breakout);
3398 is_breakout
3399 }
3400
3401 pub fn ray_cast_test(&self, ray: Point2<Window>) -> bool {
3404 if self.is_render_source() {
3405 return false;
3406 }
3407 let cp = borrow!(self.common_properties);
3408
3409 if !borrow!(&*cp)._raycastable.get().unwrap_or(true) {
3411 return false;
3412 }
3413 drop(cp);
3414 borrow!(self.instance_node).ray_cast_test(self, ray)
3415 }
3416
3417 pub fn compute_flattened_projected_children(&self) {
3418 if let Some(projected_children) = borrow!(self.expanded_projected_children).as_ref() {
3423 let new_flattened = flatten_expanded_nodes_for_projection(projected_children);
3424 let old_and_new_filtered_same =
3425 self.expanded_and_flattened_projected_children
3426 .read(|flattened| {
3427 flattened
3428 .iter()
3429 .map(|n| n.id)
3430 .eq(new_flattened.iter().map(|n| n.id))
3431 });
3432
3433 if !old_and_new_filtered_same {
3434 self.flattened_projected_children_count
3435 .set(new_flattened.len());
3436 self.expanded_and_flattened_projected_children
3437 .set(new_flattened);
3438 for (i, projected_child) in self
3439 .expanded_and_flattened_projected_children
3440 .get()
3441 .iter()
3442 .enumerate()
3443 {
3444 if projected_child.slot_index.get() != Some(i) {
3445 projected_child.slot_index.set(Some(i));
3446 };
3447 }
3448 }
3449 }
3450 }
3451
3452 dispatch_event_handler!(dispatch_scroll, Scroll, SCROLL_HANDLERS, true);
3453 dispatch_event_handler!(dispatch_touch_start, TouchStart, TOUCH_START_HANDLERS, true);
3454
3455 dispatch_event_handler!(dispatch_touch_move, TouchMove, TOUCH_MOVE_HANDLERS, true);
3456 dispatch_event_handler!(dispatch_touch_end, TouchEnd, TOUCH_END_HANDLERS, true);
3457 dispatch_event_handler!(
3458 dispatch_touch_cancel,
3459 TouchCancel,
3460 TOUCH_CANCEL_HANDLERS,
3461 true
3462 );
3463 dispatch_event_handler!(dispatch_key_down, KeyDown, KEY_DOWN_HANDLERS, false);
3464 dispatch_event_handler!(dispatch_key_up, KeyUp, KEY_UP_HANDLERS, false);
3465 dispatch_event_handler!(dispatch_key_press, KeyPress, KEY_PRESS_HANDLERS, false);
3466 dispatch_event_handler!(
3467 dispatch_checkbox_change,
3468 CheckboxChange,
3469 CHECKBOX_CHANGE_HANDLERS,
3470 true
3471 );
3472 dispatch_event_handler!(
3473 dispatch_slider_change,
3474 SliderChange,
3475 SLIDER_CHANGE_HANDLERS,
3476 true
3477 );
3478 dispatch_event_handler!(
3479 dispatch_textbox_change,
3480 TextboxChange,
3481 TEXTBOX_CHANGE_HANDLERS,
3482 true
3483 );
3484 dispatch_event_handler!(dispatch_text_input, TextInput, TEXT_INPUT_HANDLERS, true);
3485 dispatch_event_handler!(
3486 dispatch_textbox_input,
3487 TextboxInput,
3488 TEXTBOX_INPUT_HANDLERS,
3489 true
3490 );
3491 dispatch_event_handler!(
3492 dispatch_button_click,
3493 ButtonClick,
3494 BUTTON_CLICK_HANDLERS,
3495 true
3496 );
3497 dispatch_event_handler!(
3498 dispatch_photo_picker_change,
3499 PhotoPickerChange,
3500 PHOTO_PICKER_CHANGE_HANDLERS,
3501 true
3502 );
3503 dispatch_event_handler!(dispatch_mouse_down, MouseDown, MOUSE_DOWN_HANDLERS, true);
3504 dispatch_event_handler!(dispatch_mouse_up, MouseUp, MOUSE_UP_HANDLERS, true);
3505 dispatch_event_handler!(dispatch_mouse_move, MouseMove, MOUSE_MOVE_HANDLERS, true);
3506 dispatch_event_handler!(dispatch_mouse_over, MouseOver, MOUSE_OVER_HANDLERS, false);
3507 dispatch_event_handler!(dispatch_mouse_out, MouseOut, MOUSE_OUT_HANDLERS, false);
3508 dispatch_event_handler!(
3509 dispatch_double_click,
3510 DoubleClick,
3511 DOUBLE_CLICK_HANDLERS,
3512 true
3513 );
3514 dispatch_event_handler!(
3515 dispatch_context_menu,
3516 ContextMenu,
3517 CONTEXT_MENU_HANDLERS,
3518 true
3519 );
3520 fn dispatch_pointer_activation(
3521 self: &Rc<Self>,
3522 event: Event<Click>,
3523 source: PointerActivationSource,
3524 ctx: &Rc<RuntimeContext>,
3525 ) -> bool {
3526 let has_click = self.has_event_handlers(CLICK_HANDLERS);
3527 let has_tap = self.has_event_handlers(TAP_HANDLERS);
3528 let handler_key = match (has_click, has_tap, source) {
3529 (true, true, PointerActivationSource::Mouse) => Some(CLICK_HANDLERS),
3530 (true, true, PointerActivationSource::Touch) => Some(TAP_HANDLERS),
3531 (true, false, _) => Some(CLICK_HANDLERS),
3532 (false, true, _) => Some(TAP_HANDLERS),
3533 (false, false, _) => None,
3534 };
3535
3536 if let Some(handler_key) = handler_key {
3537 self.run_event_handlers_for_key(handler_key, &event, ctx);
3538 }
3539
3540 if let Some(parent) = self.template_parent.upgrade() {
3541 return parent.dispatch_pointer_activation(event, source, ctx);
3542 }
3543 event.cancelled()
3544 }
3545
3546 pub fn dispatch_click(
3547 self: &Rc<Self>,
3548 event: Event<Click>,
3549 _globals: &Globals,
3550 ctx: &Rc<RuntimeContext>,
3551 ) -> bool {
3552 self.dispatch_pointer_activation(event, PointerActivationSource::Mouse, ctx)
3553 }
3554
3555 pub fn dispatch_tap(
3556 self: &Rc<Self>,
3557 event: Event<Click>,
3558 _globals: &Globals,
3559 ctx: &Rc<RuntimeContext>,
3560 ) -> bool {
3561 self.dispatch_pointer_activation(event, PointerActivationSource::Touch, ctx)
3562 }
3563
3564 dispatch_event_handler!(dispatch_wheel, Wheel, WHEEL_HANDLERS, true);
3565 dispatch_event_handler!(dispatch_drop, Drop, DROP_HANDLERS, true);
3566 dispatch_event_handler!(dispatch_gyro, Gyro, GYRO_HANDLERS, false);
3567 dispatch_event_handler!(dispatch_accel, Accel, ACCEL_HANDLERS, false);
3568 dispatch_event_handler!(dispatch_focus, Focus, FOCUSED_HANDLERS, false);
3569 dispatch_event_handler!(
3570 dispatch_select_start,
3571 SelectStart,
3572 SELECT_START_HANDLERS,
3573 false
3574 );
3575
3576 pub fn dispatch_custom_event(
3577 self: &Rc<Self>,
3578 identifier: &str,
3579 ctx: &Rc<RuntimeContext>,
3580 ) -> Result<(), String> {
3581 if self.attached.get() == 0 {
3582 return Ok(());
3583 }
3584 let _graph = ctx.property_graph.enter();
3585 let component_origin_instance = borrow!(self.instance_node);
3586 let registry = component_origin_instance
3587 .base()
3588 .handler_registry
3589 .as_ref()
3590 .ok_or_else(|| "no registry present".to_owned())?;
3591
3592 let parent_component = self
3593 .containing_component
3594 .upgrade()
3595 .ok_or_else(|| "can't dispatch from root (has no parent)".to_owned())?;
3596 if parent_component.attached.get() == 0 {
3597 return Ok(());
3598 }
3599 let _scope = parent_component.property_binding_scope.borrow().enter();
3600 let properties = borrow!(parent_component.properties);
3601
3602 for handler in borrow!(registry)
3603 .handlers
3604 .get(identifier)
3605 .expect("presence should have been checked when added to custom_event_queue")
3606 {
3607 (handler.function)(Rc::clone(&*properties), &self.get_node_context(ctx), None)
3608 }
3609 Ok(())
3610 }
3611
3612 pub fn set_measured_size(self: &Rc<ExpandedNode>, width: f64, height: f64) {
3614 self.measured_size.set(Some((width, height)));
3615 }
3616
3617 pub fn chassis_resize_request(self: &Rc<ExpandedNode>, width: f64, height: f64) {
3619 self.set_measured_size(width, height);
3620 }
3621
3622 pub fn layout_properties(self: &Rc<ExpandedNode>) -> LocalProperty<LayoutProperties> {
3625 let common_props = self.get_common_properties();
3626 let common_props = borrow!(common_props);
3627 let cp_width = common_props.width.clone();
3628 let cp_height = common_props.height.clone();
3629 let cp_transform = common_props.transform.clone();
3630 let cp_anchor_x = common_props.anchor_x.clone();
3631 let cp_anchor_y = common_props.anchor_y.clone();
3632 let cp_scale_x = common_props.scale_x.clone();
3633 let cp_scale_y = common_props.scale_y.clone();
3634 let cp_skew_x = common_props.skew_x.clone();
3635 let cp_skew_y = common_props.skew_y.clone();
3636 let cp_rotate = common_props.rotate.clone();
3637 let cp_x = common_props.x.clone();
3638 let cp_y = common_props.y.clone();
3639 let measured_size = self.measured_size.clone();
3640 let deps = [
3641 cp_width.untyped(),
3642 cp_height.untyped(),
3643 cp_transform.untyped(),
3644 cp_anchor_x.untyped(),
3645 cp_anchor_y.untyped(),
3646 cp_scale_x.untyped(),
3647 cp_scale_y.untyped(),
3648 cp_skew_x.untyped(),
3649 cp_skew_y.untyped(),
3650 cp_rotate.untyped(),
3651 cp_x.untyped(),
3652 cp_y.untyped(),
3653 measured_size.untyped(),
3654 ];
3655
3656 LocalProperty::computed(
3657 move || {
3658 let fallback = measured_size.get();
3660 let (w_fallback, h_fallback) = match fallback {
3661 Some((wf, hf)) => (Some(wf), Some(hf)),
3662 None => (None, None),
3663 };
3664
3665 LayoutProperties {
3666 x: cp_x.get(),
3667 y: cp_y.get(),
3668 width: cp_width
3669 .get()
3670 .or(w_fallback.map(|v| Size::Pixels(v.into()))),
3671 height: cp_height
3672 .get()
3673 .or(h_fallback.map(|v| Size::Pixels(v.into()))),
3674 rotate: cp_rotate.get(),
3675 scale_x: cp_scale_x
3677 .get()
3678 .map(|v| Percent((100.0 * v.expect_percent()).into())),
3679 scale_y: cp_scale_y
3680 .get()
3681 .map(|v| Percent((100.0 * v.expect_percent()).into())),
3682 anchor_x: cp_anchor_x.get(),
3683 anchor_y: cp_anchor_y.get(),
3684 skew_x: cp_skew_x.get(),
3685 skew_y: cp_skew_y.get(),
3686 }
3687 },
3688 &deps,
3689 )
3690 }
3691}
3692
3693fn layout_axis_can_contribute(layout: &LayoutProperties, axis: Axis) -> bool {
3694 let size = match axis {
3695 Axis::X => layout.width,
3696 Axis::Y => layout.height,
3697 };
3698 let position = match axis {
3699 Axis::X => layout.x,
3700 Axis::Y => layout.y,
3701 };
3702
3703 size.is_some_and(|size| !size_depends_on_parent(size))
3704 && !position.is_some_and(size_depends_on_parent)
3705}
3706
3707fn layout_axis_can_contribute_from_parts(size: Option<Size>, position: Option<Size>) -> bool {
3708 size.is_some_and(|size| !size_depends_on_parent(size))
3709 && !position.is_some_and(size_depends_on_parent)
3710}
3711
3712fn size_depends_on_parent(size: Size) -> bool {
3713 match size {
3714 Size::Pixels(_) => false,
3715 Size::Percent(percent) => percent.to_float().abs() > f64::EPSILON,
3716 Size::Combined(_, percent) => percent.to_float().abs() > f64::EPSILON,
3717 }
3718}
3719
3720fn flatten_expanded_nodes_for_projection(nodes: &[Rc<ExpandedNode>]) -> Vec<Rc<ExpandedNode>> {
3723 let mut result: Vec<Rc<ExpandedNode>> = vec![];
3724 for node in nodes {
3725 if borrow!(node.instance_node).base().flags().invisible_to_slot {
3726 result.extend(flatten_expanded_nodes_for_projection(
3727 node.children
3728 .get()
3729 .clone()
3730 .into_iter()
3731 .collect::<Vec<_>>()
3732 .as_slice(),
3733 ));
3734 } else {
3735 result.push(Rc::clone(&node))
3736 }
3737 }
3738 result
3739}
3740
3741impl std::fmt::Debug for ExpandedNode {
3742 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3743 pub struct Fmt<F>(pub F)
3746 where
3747 F: Fn(&mut fmt::Formatter) -> fmt::Result;
3748
3749 impl<F> fmt::Debug for Fmt<F>
3750 where
3751 F: Fn(&mut fmt::Formatter) -> fmt::Result,
3752 {
3753 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3754 (self.0)(f)
3755 }
3756 }
3757
3758 f.debug_struct("ExpandedNode")
3759 .field(
3760 "instance_node",
3761 &Fmt(|f| borrow!(self.instance_node).resolve_debug(f, Some(self))),
3762 )
3763 .field("id", &self.id)
3764 .field("common_properties", &borrow!(self.common_properties))
3765 .field("transform_and_bounds", &self.transform_and_bounds)
3766 .field("children", &self.children.get().iter().collect::<Vec<_>>())
3767 .field(
3768 "projected_children",
3769 &self
3770 .expanded_and_flattened_projected_children
3771 .get()
3772 .iter()
3773 .map(|v| v.id)
3774 .collect::<Vec<_>>(),
3775 )
3776 .field("render_placement", &self.occlusion.get())
3777 .field(
3778 "containing_component",
3779 &self.containing_component.upgrade().map(|v| v.id.clone()),
3780 )
3781 .finish()
3782 }
3783}