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, Focus, Interpolatable, Layer, NativeLiquidGlassScope,
7 PaxValue, Percent, Property, 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}
89
90impl fmt::Debug for RuntimeSettingsLayer {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 f.debug_struct("RuntimeSettingsLayer")
93 .field("provider_id", &self.provider_id)
94 .field("provider_type_id", &self.provider_type_id)
95 .field("provider_stack", &"<runtime scope>")
96 .field("settings", &self.settings)
97 .finish()
98 }
99}
100
101#[derive(Clone, Debug, PartialEq, Eq)]
102pub struct RuntimeSettingsSignatureEntry {
103 pub provider_id: ExpandedNodeIdentifier,
104 pub provider_type_id: TypeId,
105}
106
107#[derive(Clone, Debug, PartialEq, Eq)]
108pub enum RuntimeSettingsSource {
109 ComponentSettings,
110 ImportedLayer {
111 provider_id: ExpandedNodeIdentifier,
112 provider_type_id: TypeId,
113 },
114 Inline,
115}
116
117#[derive(Clone, Debug, Default)]
118pub struct RuntimeSettingsCondition {
119 pub positive: Vec<ExpressionInfo>,
120 pub negative: Vec<ExpressionInfo>,
121}
122
123#[derive(Clone)]
124pub struct RuntimeResolvedPropertyEntry {
125 pub source: RuntimeSettingsSource,
126 pub selector: Option<SelectorExpr>,
127 pub source_location: Option<LocationInfo>,
128 pub source_stack: Option<Rc<RuntimePropertiesStackFrame>>,
131 pub value: ValueDefinition,
132 pub condition: Option<RuntimeSettingsCondition>,
133 pub axis_index: Option<usize>,
134}
135
136impl fmt::Debug for RuntimeResolvedPropertyEntry {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 f.debug_struct("RuntimeResolvedPropertyEntry")
139 .field("source", &self.source)
140 .field("selector", &self.selector)
141 .field("source_location", &self.source_location)
142 .field(
143 "source_stack",
144 &self.source_stack.as_ref().map(|_| "<runtime scope>"),
145 )
146 .field("value", &self.value)
147 .field("condition", &self.condition)
148 .field("axis_index", &self.axis_index)
149 .finish()
150 }
151}
152
153pub type RuntimeResolvedPropertyColumns = BTreeMap<String, Vec<RuntimeResolvedPropertyEntry>>;
154
155#[derive(Clone, Copy)]
156enum PointerActivationSource {
157 Mouse,
158 Touch,
159}
160
161#[derive(Clone, Copy, Debug, Default)]
162pub(crate) struct FilteredRenderStats {
163 pub path_nodes_visited: usize,
164 pub dirty_nodes_rendered: usize,
165 pub skipped_subtrees: usize,
166}
167
168#[derive(Clone)]
169pub struct ExpandedNode {
170 #[allow(dead_code)]
171 pub id: ExpandedNodeIdentifier,
174
175 pub instance_node: RefCell<InstanceNodePtr>,
177
178 pub render_parent: RefCell<Weak<ExpandedNode>>,
181
182 pub template_parent: Weak<ExpandedNode>,
185
186 pub parent_frame: Property<Option<ExpandedNodeIdentifier>>,
191
192 pub liquid_glass_scope: Property<Option<NativeLiquidGlassScope>>,
194
195 pub containing_component: Weak<ExpandedNode>,
200
201 pub stack: Rc<RuntimePropertiesStackFrame>,
205
206 pub children: Property<Vec<Rc<ExpandedNode>>>,
209
210 pub mounted_children: RefCell<Vec<Rc<ExpandedNode>>>,
216
217 pub active_children: RefCell<Vec<Rc<ExpandedNode>>>,
222 pub active_children_view: Property<Vec<Rc<ExpandedNode>>>,
225
226 pub exiting_children: RefCell<Vec<Rc<ExpandedNode>>>,
231 exiting_child_generations: RefCell<HashMap<ExpandedNodeIdentifier, u64>>,
234 pub exiting_children_view: Property<Vec<Rc<ExpandedNode>>>,
236
237 pub sidecar_children: RefCell<Vec<Rc<ExpandedNode>>>,
239
240 pub properties: RefCell<Rc<RefCell<PaxAny>>>,
242
243 pub common_properties: RefCell<Rc<RefCell<CommonProperties>>>,
245 pub measured_size: Property<Option<(f64, f64)>>,
248 pub selector_metadata: RefCell<RuntimeSelectorMetadata>,
250
251 pub transform_and_bounds: Property<TransformAndBounds<NodeLocal, Window>>,
254 pub subtree_layout_hull: Property<LayoutHull>,
256 pub container_frame: Property<Option<ContainerFrame>>,
258
259 pub computed_opacity: Property<f64>,
262
263 pub expanded_projected_children: RefCell<Option<Vec<Rc<ExpandedNode>>>>,
270 pub expanded_and_flattened_projected_children: Property<Vec<Rc<ExpandedNode>>>,
276 pub flattened_projected_children_count: Property<usize>,
278
279 pub attached: Cell<u32>,
286
287 pub occlusion: Property<Occlusion>,
292
293 pub native_mask_hash: Cell<u64>,
295
296 pub browser_content_layer_id: Cell<Option<u32>>,
298
299 pub presentation_cache_hash: Cell<u64>,
301
302 pub properties_scope: RefCell<HashMap<String, Variable>>,
305
306 pub slot_index: Property<Option<usize>>,
309
310 pub suspended: Property<bool>,
312
313 pub changed_listener: Property<()>,
316 selector_classes_listener: Property<()>,
317
318 pub occlusion_listener: Property<()>,
320
321 pub children_listener: Property<()>,
323 pub subtree_layout_hull_listener: Property<()>,
325 parent_binding_sources: RefCell<Option<ParentBindingSources>>,
326 layout_binding_generation: Cell<u64>,
327 #[cfg(test)]
328 pub(crate) parent_binding_rebuilds: Cell<usize>,
329 #[cfg(test)]
330 pub(crate) layout_hull_rebuilds: Cell<usize>,
331 #[cfg(test)]
332 pub(crate) structural_children_updates: Cell<usize>,
333 pub content_measurement_listener: Property<()>,
335 pub content_measurement_rebind_listener: Property<()>,
337 pub content_measurement_bound: Cell<bool>,
339 subtree_requires_non_reactive_update: Cell<bool>,
343
344 pub projected_children_changed: Property<()>,
346
347 pub slot_projection_changed: Property<()>,
350
351 pub subscriptions: RefCell<Vec<Property<()>>>,
353
354 pub transition_phase: Property<u64>,
356 pub transition_generation: Property<u64>,
358 pub transition_takeover: Property<bool>,
360 pub transition_selector_classes: RefCell<Option<Vec<String>>>,
364 exit_retention_generation: Cell<u64>,
366 pub transition_origin_frame: Property<u64>,
368 pub transition_origin_millis: Property<u64>,
370 pub transition_playhead: Property<f64>,
372 pub transition_playhead_millis: Property<f64>,
374 pub enter_cleanup_listener: Property<()>,
376 pub enter_cleanup_active: Cell<bool>,
378 pub exit_started_millis: Cell<Option<u128>>,
380 exit_timeout_warning_emitted: Cell<bool>,
382 pub exit_cleanup_listener: Property<()>,
384 pub exit_cleanup_active: Cell<bool>,
386 pub imported_settings_layers: RefCell<Vec<RuntimeSettingsLayer>>,
388 pub imported_settings_signature: RefCell<Vec<RuntimeSettingsSignatureEntry>>,
390 pub resolved_property_columns: RefCell<RuntimeResolvedPropertyColumns>,
392 pub resolved_property_provenance: RefCell<BTreeMap<String, RuntimeResolvedPropertyEntry>>,
394 pub reset_removed_runtime_properties: Cell<bool>,
396}
397
398#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
399pub struct Occlusion {
400 pub render_layer_id: usize,
402 pub z_index: i32,
403 pub parent_frame: Option<u32>,
407}
408
409impl Interpolatable for Occlusion {}
410
411impl ImplToFromPaxAny for ExpandedNode {}
412impl Interpolatable for ExpandedNode {}
413
414#[derive(Clone)]
415pub struct RuntimeSelectorMetadata {
417 pub type_id: TypeId,
419 pub id: Property<Option<String>>,
421 pub classes: Property<Vec<String>>,
423}
424
425impl RuntimeSelectorMetadata {
426 fn from_base(
427 base: &crate::rendering::BaseInstance,
428 common_properties: &Rc<RefCell<CommonProperties>>,
429 stack: &Rc<RuntimePropertiesStackFrame>,
430 ) -> Self {
431 let classes = base
432 .template_node_selector_info
433 .as_ref()
434 .and_then(|info| {
435 info.class_binding.as_ref().map(|binding| {
436 build_selector_classes_property(binding, info.source_location.as_ref(), stack)
437 })
438 })
439 .unwrap_or_else(|| Property::new_with_name(Vec::new(), "selector classes"));
440
441 Self {
442 type_id: base.template_node_type_id.clone().unwrap_or_default(),
443 id: common_properties.borrow().id.clone(),
444 classes,
445 }
446 }
447
448 pub fn matches(&self, selector: &SelectorExpr) -> bool {
449 match selector {
450 SelectorExpr::Id(id) => self.id.get().as_deref() == Some(id.as_str()),
451 SelectorExpr::Class(class_name) => self
452 .classes
453 .get()
454 .into_iter()
455 .any(|node_class| node_class == *class_name),
456 SelectorExpr::Type(type_name) => {
457 self.type_id.to_string() == *type_name
458 || self.type_id.get_pascal_identifier().as_deref() == Some(type_name.as_str())
459 }
460 }
461 }
462}
463
464fn warn_selector_class_once(
465 warnings: &Rc<RefCell<HashSet<String>>>,
466 key: String,
467 message: impl FnOnce() -> String,
468) {
469 if warnings.borrow_mut().insert(key) {
470 log::warn!("{}", message());
471 }
472}
473
474fn normalize_selector_classes(
475 value: PaxValue,
476 warnings: &Rc<RefCell<HashSet<String>>>,
477 source_location: Option<&LocationInfo>,
478) -> Vec<String> {
479 let location = source_location
480 .map(|location| {
481 format!(
482 " at {}:{}",
483 location.start_line_col.0, location.start_line_col.1
484 )
485 })
486 .unwrap_or_default();
487 let candidates = match value {
488 PaxValue::String(value) => vec![value],
489 PaxValue::Vec(values) => values
490 .into_iter()
491 .filter_map(|value| match value {
492 PaxValue::String(value) => Some(value),
493 other => {
494 warn_selector_class_once(warnings, format!("list-type:{other}"), || {
495 format!("ignoring non-string value in class list{location}: {other}")
496 });
497 None
498 }
499 })
500 .collect(),
501 other => {
502 warn_selector_class_once(warnings, format!("binding-type:{other}"), || {
503 format!(
504 "class binding{location} must evaluate to String or Vec<String>; received {other}"
505 )
506 });
507 return Vec::new();
508 }
509 };
510
511 let mut seen = HashSet::new();
512 let mut normalized = candidates
513 .into_iter()
514 .rev()
515 .filter(|class_name| {
516 if class_name.is_empty() {
517 return false;
518 }
519 if !matches!(
520 SelectorExpr::parse(&format!(".{class_name}")),
521 Ok(SelectorExpr::Class(_))
522 ) {
523 warn_selector_class_once(warnings, format!("invalid-name:{class_name}"), || {
524 format!("ignoring invalid class name '{class_name}'{location}")
525 });
526 return false;
527 }
528 seen.insert(class_name.clone())
529 })
530 .collect::<Vec<_>>();
531 normalized.reverse();
532 normalized
533}
534
535fn build_selector_classes_property(
536 binding: &ValueDefinition,
537 source_location: Option<&LocationInfo>,
538 stack: &Rc<RuntimePropertiesStackFrame>,
539) -> Property<Vec<String>> {
540 let warnings = Rc::new(RefCell::new(HashSet::new()));
541 let source_location = source_location.cloned();
542 match binding {
543 ValueDefinition::LiteralValue(value) => Property::new_with_name(
544 normalize_selector_classes(value.clone(), &warnings, source_location.as_ref()),
545 "selector classes",
546 ),
547 ValueDefinition::Expression(info) => {
548 let dependencies = info
549 .dependencies
550 .iter()
551 .filter_map(|dependency| {
552 stack
553 .resolve_symbol_as_erased_property(dependency)
554 .or_else(|| {
555 log::warn!("failed to resolve class binding symbol {dependency}");
556 None
557 })
558 })
559 .collect::<Vec<_>>();
560 let expression = info.expression.clone();
561 let expression_label = expression.to_string();
562 let stack = Rc::clone(stack);
563 let warnings = Rc::clone(&warnings);
564 Property::computed_with_name(
565 move || match expression.compute(stack.clone()) {
566 Ok(value) => {
567 normalize_selector_classes(value, &warnings, source_location.as_ref())
568 }
569 Err(error) => {
570 warn_selector_class_once(
571 &warnings,
572 format!("evaluation:{expression_label}:{error:?}"),
573 || {
574 let location = source_location
575 .as_ref()
576 .map(|location| {
577 format!(
578 " at {}:{}",
579 location.start_line_col.0, location.start_line_col.1
580 )
581 })
582 .unwrap_or_default();
583 format!(
584 "failed to evaluate class binding `{expression_label}`{location}: {error:?}"
585 )
586 },
587 );
588 Vec::new()
589 }
590 },
591 &dependencies,
592 "selector classes",
593 )
594 }
595 other => {
596 log::warn!("unsupported class binding definition: {other}");
597 Property::new_with_name(Vec::new(), "selector classes")
598 }
599 }
600}
601
602#[cfg(test)]
603mod selector_class_tests {
604 use super::{build_selector_classes_property, normalize_selector_classes};
605 use crate::RuntimePropertiesStackFrame;
606 use pax_language::parse_pax_expression;
607 use pax_manifest::{ExpressionInfo, ValueDefinition};
608 use pax_runtime_api::{PaxValue, Property, Variable};
609 use std::cell::RefCell;
610 use std::collections::{HashMap, HashSet};
611 use std::rc::Rc;
612
613 #[test]
614 fn normalizes_order_duplicates_and_invalid_names() {
615 let warnings = Rc::new(RefCell::new(HashSet::new()));
616 let classes = normalize_selector_classes(
617 PaxValue::Vec(vec![
618 PaxValue::String("base".to_string()),
619 PaxValue::String("bad name".to_string()),
620 PaxValue::String("active".to_string()),
621 PaxValue::String("base".to_string()),
622 PaxValue::Numeric(1.into()),
623 ]),
624 &warnings,
625 None,
626 );
627
628 assert_eq!(classes, vec!["active", "base"]);
629 assert_eq!(warnings.borrow().len(), 2);
630 }
631
632 #[test]
633 fn expression_backed_classes_track_string_and_vector_properties() {
634 let constant_binding = ValueDefinition::Expression(ExpressionInfo::new(
635 parse_pax_expression(r#""temporary""#).unwrap(),
636 ));
637 let constant_classes = build_selector_classes_property(
638 &constant_binding,
639 None,
640 &RuntimePropertiesStackFrame::new(HashMap::new()),
641 );
642 assert_eq!(constant_classes.get(), vec!["temporary"]);
643
644 let scalar = Property::new("base".to_string());
645 let stack = RuntimePropertiesStackFrame::new(HashMap::from([(
646 "current_class".to_string(),
647 Variable::new_from_typed_property(scalar.clone()),
648 )]));
649 let scalar_binding = ValueDefinition::Expression(ExpressionInfo::new(
650 parse_pax_expression("current_class").unwrap(),
651 ));
652 let scalar_classes = build_selector_classes_property(&scalar_binding, None, &stack);
653 assert_eq!(scalar_classes.get(), vec!["base"]);
654 scalar.set("active".to_string());
655 assert_eq!(scalar_classes.get(), vec!["active"]);
656
657 let vector = Property::new(vec!["base".to_string(), "selected".to_string()]);
658 let stack = RuntimePropertiesStackFrame::new(HashMap::from([(
659 "current_classes".to_string(),
660 Variable::new_from_typed_property(vector.clone()),
661 )]));
662 let vector_binding = ValueDefinition::Expression(ExpressionInfo::new(
663 parse_pax_expression("current_classes").unwrap(),
664 ));
665 let vector_classes = build_selector_classes_property(&vector_binding, None, &stack);
666 assert_eq!(vector_classes.get(), vec!["base", "selected"]);
667 vector.set(Vec::new());
668 assert!(vector_classes.get().is_empty());
669 }
670}
671
672macro_rules! dispatch_event_handler {
673 ($fn_name:ident, $arg_type:ty, $handler_key:ident, $recurse:expr) => {
674 pub fn $fn_name(
675 self: &Rc<Self>,
676 event: Event<$arg_type>,
677 globals: &Globals,
678 ctx: &Rc<RuntimeContext>,
679 ) -> bool {
680 self.run_event_handlers_for_key($handler_key, &event, ctx);
681
682 if $recurse {
683 if let Some(parent) = self.template_parent.upgrade() {
684 return parent.$fn_name(event, globals, ctx);
685 }
686 }
687 event.cancelled()
688 }
689 };
690}
691
692impl ExpandedNode {
693 pub fn initialize_root(template: Rc<ComponentInstance>, ctx: &Rc<RuntimeContext>) -> Rc<Self> {
694 let root_node = Self::new(
695 template,
696 ctx.globals().stack_frame(),
697 ctx,
698 Weak::new(),
699 Weak::new(),
700 );
701 root_node.bind_to_parent_bounds(ctx);
702 Rc::clone(&root_node).recurse_mount(ctx);
703 root_node
704 }
705
706 fn has_event_handlers(&self, handler_key: &str) -> bool {
707 borrow!(self.instance_node)
708 .base()
709 .get_handler_registry()
710 .is_some_and(|registry| {
711 borrow!(*registry)
712 .handlers
713 .get(handler_key)
714 .is_some_and(|handlers| !handlers.is_empty())
715 })
716 }
717
718 fn run_event_handlers_for_key<T: Clone + 'static>(
719 self: &Rc<Self>,
720 handler_key: &str,
721 event: &Event<T>,
722 ctx: &Rc<RuntimeContext>,
723 ) {
724 if let Some(registry) = borrow!(self.instance_node).base().get_handler_registry() {
725 let borrowed_registry = &borrow!(*registry);
726 if let Some(handlers) = borrowed_registry.handlers.get(handler_key) {
727 if !handlers.is_empty() {
728 let component_properties = if let Some(cc) = self.containing_component.upgrade()
729 {
730 Rc::clone(&*borrow!(cc.properties))
731 } else {
732 Rc::clone(&*borrow!(self.properties))
733 };
734
735 let context = self.get_node_context(ctx);
736 handlers.iter().for_each(|handler| {
737 let properties = if let HandlerLocation::Component = &handler.location {
738 Rc::clone(&*borrow!(self.properties))
739 } else {
740 Rc::clone(&component_properties)
741 };
742 (handler.function)(
743 Rc::clone(&properties),
744 &context,
745 Some(event.clone().to_pax_any()),
746 );
747 });
748 }
749 };
750 }
751 }
752
753 fn new(
754 template: Rc<dyn InstanceNode>,
755 env: Rc<RuntimePropertiesStackFrame>,
756 context: &Rc<RuntimeContext>,
757 containing_component: Weak<ExpandedNode>,
758 parent: Weak<ExpandedNode>,
759 ) -> Rc<Self> {
760 let transition_config = template.base().transition_config().clone();
761 let has_transition_bindings = transition_config.has_enter || transition_config.has_exit;
762 let transition_phase = Property::new_with_name(TRANSITION_PHASE_IDLE, "transition phase");
763 let transition_generation = Property::new_with_name(0, "transition generation");
764 let transition_takeover = Property::new_with_name(false, "transition takeover");
765 let transition_origin_frame =
766 Property::new_with_name(context.globals().elapsed_frames.get(), "transition origin");
767 let transition_origin_millis = Property::new_with_name(
768 context.globals().elapsed_millis.get(),
769 "transition origin millis",
770 );
771 let transition_playhead = Property::new_with_name(0.0, "transition playhead");
772 let transition_playhead_millis = Property::new_with_name(0.0, "transition playhead millis");
773
774 let env = if has_transition_bindings {
775 env.push(
776 vec![
777 (
778 TRANSITION_PHASE_SYMBOL.to_string(),
779 Variable::new_from_typed_property(transition_phase.clone()),
780 ),
781 (
782 TRANSITION_GENERATION_SYMBOL.to_string(),
783 Variable::new_from_typed_property(transition_generation.clone()),
784 ),
785 (
786 TRANSITION_TAKEOVER_SYMBOL.to_string(),
787 Variable::new_from_typed_property(transition_takeover.clone()),
788 ),
789 (
790 TRANSITION_PLAYHEAD_SYMBOL.to_string(),
791 Variable::new_from_typed_property(transition_playhead.clone()),
792 ),
793 (
794 TRANSITION_PLAYHEAD_MILLIS_SYMBOL.to_string(),
795 Variable::new_from_typed_property(transition_playhead_millis.clone()),
796 ),
797 ]
798 .into_iter()
799 .collect(),
800 )
801 } else {
802 env
803 };
804
805 let legacy_initialization = template.base().requires_pre_node_binding();
810 let properties_init = &template.base().instance_prototypical_properties;
811 let common_init = &template.base().instance_prototypical_common_properties;
812 let properties = if legacy_initialization {
813 properties_init.materialize(env.clone(), None).unwrap()
814 } else {
815 properties_init.allocate(env.clone())
816 };
817 let common_properties = if legacy_initialization {
818 common_init.materialize(env.clone(), None).unwrap()
819 } else {
820 common_init.allocate(env.clone())
821 };
822 let selector_metadata =
823 RuntimeSelectorMetadata::from_base(template.base(), &common_properties, &env);
824
825 let property_scope = if legacy_initialization {
826 let mut scope = borrow!(*common_properties).retrieve_property_scope();
827 scope.extend(template.base().properties_scope.build(properties.clone()));
828 scope
829 } else {
830 HashMap::new()
833 };
834
835 let id = context.gen_uid();
836 let res = Rc::new(ExpandedNode {
837 id,
838 stack: env,
839 instance_node: RefCell::new(Rc::clone(&template)),
840 attached: Cell::new(0),
841 properties: RefCell::new(properties),
842 common_properties: RefCell::new(common_properties),
843 measured_size: Property::default(),
844 selector_metadata: RefCell::new(selector_metadata),
845
846 render_parent: Default::default(),
849 parent_frame: Default::default(),
850 liquid_glass_scope: Default::default(),
851 template_parent: parent,
852
853 containing_component,
854 children: Property::new_with_name(
855 Vec::new(),
856 &format!("node children (node id: {})", id.0),
857 ),
858 mounted_children: RefCell::new(Vec::new()),
859 active_children: RefCell::new(Vec::new()),
860 active_children_view: Property::new(Vec::new()),
861 exiting_children: RefCell::new(Vec::new()),
862 exiting_child_generations: RefCell::new(HashMap::new()),
863 exiting_children_view: Property::new(Vec::new()),
864 sidecar_children: RefCell::new(Vec::new()),
865 transform_and_bounds: Property::new(TransformAndBounds::default()),
866 subtree_layout_hull: Property::new(LayoutHull::default()),
867 container_frame: Property::new(None),
868 computed_opacity: Property::new(1.0),
869 expanded_projected_children: Default::default(),
870 expanded_and_flattened_projected_children: Default::default(),
871 flattened_projected_children_count: Property::new(0),
872 occlusion: Property::new(Occlusion::default()),
873 native_mask_hash: Cell::new(0),
874 browser_content_layer_id: Cell::new(None),
875 presentation_cache_hash: Cell::new(0),
876 properties_scope: RefCell::new(property_scope),
877 slot_index: Property::default(),
878 suspended: Property::new(false),
879 changed_listener: Property::default(),
880 selector_classes_listener: Property::default(),
881 occlusion_listener: Property::default(),
882 children_listener: Property::default(),
883 subtree_layout_hull_listener: Property::default(),
884 parent_binding_sources: RefCell::new(None),
885 layout_binding_generation: Cell::new(0),
886 #[cfg(test)]
887 parent_binding_rebuilds: Cell::new(0),
888 #[cfg(test)]
889 layout_hull_rebuilds: Cell::new(0),
890 #[cfg(test)]
891 structural_children_updates: Cell::new(0),
892 content_measurement_listener: Property::default(),
893 content_measurement_rebind_listener: Property::default(),
894 content_measurement_bound: Cell::new(false),
895 subtree_requires_non_reactive_update: Cell::new(true),
896 projected_children_changed: Property::default(),
897 slot_projection_changed: Property::default(),
898 subscriptions: Default::default(),
899 transition_phase,
900 transition_generation,
901 transition_takeover,
902 transition_selector_classes: RefCell::new(None),
903 exit_retention_generation: Cell::new(0),
904 transition_origin_frame,
905 transition_origin_millis,
906 transition_playhead,
907 transition_playhead_millis,
908 enter_cleanup_listener: Property::default(),
909 enter_cleanup_active: Cell::new(false),
910 exit_started_millis: Cell::new(None),
911 exit_timeout_warning_emitted: Cell::new(false),
912 exit_cleanup_listener: Property::default(),
913 exit_cleanup_active: Cell::new(false),
914 imported_settings_layers: RefCell::new(Vec::new()),
915 imported_settings_signature: RefCell::new(Vec::new()),
916 resolved_property_columns: RefCell::new(BTreeMap::new()),
917 resolved_property_provenance: RefCell::new(BTreeMap::new()),
918 reset_removed_runtime_properties: Cell::new(false),
919 });
920 template.base().bind_properties(&res);
921 if legacy_initialization {
922 let common_properties = Rc::clone(&*borrow!(res.common_properties));
923 *res.selector_metadata.borrow_mut() =
924 RuntimeSelectorMetadata::from_base(template.base(), &common_properties, &res.stack);
925 }
926 res.refresh_properties_scope(&template);
927 res.bind_selector_classes_listener(context);
928 res.bind_occlusion_listener(context);
929 res.bind_children_listener(context);
930 res.bind_subtree_layout_hull(context);
931 res
932 }
933
934 pub fn recreate_with_new_data(
935 self: &Rc<Self>,
936 template: Rc<dyn InstanceNode>,
937 context: &Rc<RuntimeContext>,
938 ) {
939 *borrow_mut!(self.instance_node) = Rc::clone(&template);
940 template.base().bind_properties(self);
941 let common_properties = Rc::clone(&*borrow!(self.common_properties));
942 *self.selector_metadata.borrow_mut() =
943 RuntimeSelectorMetadata::from_base(template.base(), &common_properties, &self.stack);
944 self.refresh_properties_scope(&template);
945 self.bind_selector_classes_listener(context);
946 self.reapply_runtime_settings(context);
947 self.bind_to_parent_bounds(context);
948 self.bind_occlusion_listener(context);
949 self.mark_non_reactive_update_subtree_dirty();
950 context.mark_occlusion_dirty();
951 context.set_canvas_dirty(self.occlusion.get().render_layer_id);
952 }
953
954 pub fn fully_recreate_with_new_data(
955 self: &Rc<Self>,
956 template: Rc<dyn InstanceNode>,
957 context: &Rc<RuntimeContext>,
958 ) {
959 Rc::clone(self).recurse_unmount(context);
960 let new_expanded_node = Self::new(
961 template.clone(),
962 Rc::clone(&self.stack),
963 context,
964 Weak::clone(&self.containing_component),
965 Weak::clone(&self.template_parent),
966 );
967 *borrow_mut!(self.instance_node) = Rc::clone(&*borrow!(new_expanded_node.instance_node));
968 *borrow_mut!(self.properties) = Rc::clone(&*borrow!(new_expanded_node.properties));
969 *borrow_mut!(self.properties_scope) = borrow!(new_expanded_node.properties_scope).clone();
970 *borrow_mut!(self.common_properties) =
971 Rc::clone(&*borrow!(new_expanded_node.common_properties));
972 *self.selector_metadata.borrow_mut() = new_expanded_node.selector_metadata.borrow().clone();
973 self.occlusion.set(Default::default());
974
975 self.bind_to_parent_bounds(context);
976 self.bind_selector_classes_listener(context);
977 self.bind_occlusion_listener(context);
978 self.mark_non_reactive_update_subtree_dirty();
979 context.mark_occlusion_dirty();
980 Rc::clone(self).recurse_mount(context);
981 context.drain_node_effects();
982 self.recurse_emit_mount_updates();
983 }
984
985 fn recurse_emit_mount_updates(self: &Rc<Self>) {
986 self.changed_listener.get();
990 self.occlusion_listener.get();
991 for child in self.children.get().iter() {
992 child.recurse_emit_mount_updates();
993 }
994 for child in borrow!(self.sidecar_children).iter() {
995 child.recurse_emit_mount_updates();
996 }
997 }
998
999 fn refresh_properties_scope(self: &Rc<Self>, template: &Rc<dyn InstanceNode>) {
1000 let common_properties = Rc::clone(&*borrow!(self.common_properties));
1001 let mut refreshed_scope = borrow!(*common_properties).retrieve_property_scope();
1002 refreshed_scope.extend(
1003 template
1004 .base()
1005 .properties_scope
1006 .build(Rc::clone(&*borrow!(self.properties))),
1007 );
1008 *borrow_mut!(self.properties_scope) = refreshed_scope;
1009 }
1010
1011 pub fn is_descendant_of(&self, other_expanded_node_id: &ExpandedNodeIdentifier) -> bool {
1015 if let Some(parent) = borrow!(self.render_parent).upgrade() {
1016 if parent.id.eq(other_expanded_node_id) {
1018 true
1019 } else {
1020 parent.is_descendant_of(other_expanded_node_id)
1021 }
1022 } else {
1023 false
1024 }
1025 }
1026
1027 pub fn create_children_detached(
1028 self: &Rc<Self>,
1029 templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
1030 context: &Rc<RuntimeContext>,
1031 template_parent: &Weak<ExpandedNode>,
1032 ) -> Vec<Rc<ExpandedNode>> {
1033 let containing_component = if borrow!(self.instance_node).base().flags().is_component {
1034 Rc::downgrade(&self)
1035 } else {
1036 Weak::clone(&self.containing_component)
1037 };
1038
1039 let mut children = Vec::new();
1040
1041 for (template, env) in templates {
1042 children.push(Self::new(
1043 template,
1044 env,
1045 context,
1046 Weak::clone(&containing_component),
1047 Weak::clone(&template_parent),
1048 ));
1049 }
1050 children
1051 }
1052
1053 pub(crate) fn current_attached_children(&self) -> Vec<Rc<ExpandedNode>> {
1054 borrow!(self.mounted_children).clone()
1055 }
1056
1057 pub(crate) fn rescue_exiting_child_matching(
1058 self: &Rc<Self>,
1059 predicate: impl Fn(&Rc<ExpandedNode>) -> bool,
1060 ) -> Option<Rc<ExpandedNode>> {
1061 let position = borrow!(self.exiting_children).iter().position(predicate)?;
1062 let child = borrow_mut!(self.exiting_children).remove(position);
1063 child
1064 .exit_retention_generation
1065 .set(child.exit_retention_generation.get().wrapping_add(1));
1066 borrow_mut!(self.exiting_child_generations).remove(&child.id);
1067 if borrow!(self.exiting_children).is_empty() {
1068 self.exit_cleanup_active.set(false);
1069 self.exit_cleanup_listener
1070 .replace_with(Property::new_with_name((), "exit transition cleanup"));
1071 }
1072 Some(child)
1073 }
1074
1075 fn sync_mounted_children_from_active_and_exiting(&self) -> Vec<Rc<ExpandedNode>> {
1076 let active_children = borrow!(self.active_children).clone();
1077 let exiting_children = borrow!(self.exiting_children).clone();
1078 if !self
1079 .active_children_view
1080 .read(|old| Self::same_children(old, &active_children))
1081 {
1082 self.active_children_view.set(active_children.clone());
1083 }
1084 if !self
1085 .exiting_children_view
1086 .read(|old| Self::same_children(old, &exiting_children))
1087 {
1088 self.exiting_children_view.set(exiting_children.clone());
1089 }
1090 let mut combined = exiting_children;
1091 combined.extend(active_children);
1092 *borrow_mut!(self.mounted_children) = combined.clone();
1093 combined
1094 }
1095
1096 fn has_child(children: &[Rc<ExpandedNode>], target: &Rc<ExpandedNode>) -> bool {
1097 children.iter().any(|child| Rc::ptr_eq(child, target))
1098 }
1099
1100 fn same_children(a: &[Rc<ExpandedNode>], b: &[Rc<ExpandedNode>]) -> bool {
1101 a.len() == b.len() && a.iter().zip(b).all(|(a, b)| Rc::ptr_eq(a, b))
1102 }
1103
1104 fn detach_child_for_reparent(&self, target: &Rc<ExpandedNode>) {
1105 let mut removed = false;
1106 {
1107 let active_children = &mut *borrow_mut!(self.active_children);
1108 let old_len = active_children.len();
1109 active_children.retain(|child| !Rc::ptr_eq(child, target));
1110 removed |= active_children.len() != old_len;
1111 }
1112 {
1113 let exiting_children = &mut *borrow_mut!(self.exiting_children);
1114 let old_len = exiting_children.len();
1115 exiting_children.retain(|child| !Rc::ptr_eq(child, target));
1116 removed |= exiting_children.len() != old_len;
1117 }
1118 if removed {
1119 self.sync_mounted_children_from_active_and_exiting();
1120 self.mark_non_reactive_update_subtree_dirty();
1121 }
1122 }
1123
1124 fn template_node_identifier(&self) -> Option<UniqueTemplateNodeIdentifier> {
1125 borrow!(self.instance_node)
1126 .base()
1127 .template_node_identifier
1128 .clone()
1129 }
1130
1131 fn transition_source_matches(
1132 sources: &[UniqueTemplateNodeIdentifier],
1133 own_identifier: Option<&UniqueTemplateNodeIdentifier>,
1134 requested_source: Option<&UniqueTemplateNodeIdentifier>,
1135 ) -> bool {
1136 if let Some(source) = requested_source {
1137 return sources.contains(source);
1138 }
1139 if sources.is_empty() {
1140 return true;
1141 }
1142 own_identifier
1143 .map(|identifier| sources.contains(identifier))
1144 .unwrap_or(false)
1145 }
1146
1147 fn has_exit_transition_for_source(
1148 &self,
1149 requested_source: Option<&UniqueTemplateNodeIdentifier>,
1150 ) -> bool {
1151 let instance_node = borrow!(self.instance_node);
1152 let transition_config = instance_node.base().transition_config();
1153 transition_config.has_exit
1154 && Self::transition_source_matches(
1155 &transition_config.exit_sources,
1156 instance_node.base().template_node_identifier.as_ref(),
1157 requested_source,
1158 )
1159 }
1160
1161 fn mark_non_reactive_update_subtree_dirty(&self) {
1162 if self.subtree_requires_non_reactive_update.replace(true) {
1163 return;
1164 }
1165 if let Some(parent) = borrow!(self.render_parent).upgrade() {
1166 parent.mark_non_reactive_update_subtree_dirty();
1167 }
1168 }
1169
1170 fn start_self_enter_transition_for_source(
1171 self: &Rc<Self>,
1172 context: &Rc<RuntimeContext>,
1173 requested_source: Option<&UniqueTemplateNodeIdentifier>,
1174 ) -> bool {
1175 let instance_node = borrow!(self.instance_node);
1176 let transition_config = instance_node.base().transition_config();
1177 if !transition_config.has_enter
1178 || !Self::transition_source_matches(
1179 &transition_config.enter_sources,
1180 instance_node.base().template_node_identifier.as_ref(),
1181 requested_source,
1182 )
1183 {
1184 return false;
1185 }
1186 drop(instance_node);
1187 self.begin_transition_run(context, TRANSITION_PHASE_ENTER);
1188 self.exit_started_millis.set(None);
1189 self.exit_timeout_warning_emitted.set(false);
1190 self.enable_enter_cleanup_listener(context);
1191 true
1192 }
1193
1194 fn start_self_enter_transition(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1195 let started = self.start_self_enter_transition_for_source(context, None);
1196 if started {
1197 if let Some(source) = self.template_node_identifier() {
1198 self.start_bound_enter_transitions(context, &source);
1199 }
1200 }
1201 }
1202
1203 fn start_self_exit_transition_for_source(
1204 self: &Rc<Self>,
1205 context: &Rc<RuntimeContext>,
1206 requested_source: Option<&UniqueTemplateNodeIdentifier>,
1207 ) -> bool {
1208 if !self.has_exit_transition_for_source(requested_source) {
1209 return false;
1210 }
1211 self.begin_transition_run(context, TRANSITION_PHASE_EXIT);
1212 self.disable_enter_cleanup_listener();
1213 self.exit_started_millis
1214 .set(Some((context.globals().get_elapsed_millis)()));
1215 self.exit_timeout_warning_emitted.set(false);
1216 true
1217 }
1218
1219 fn start_self_exit_transition(self: &Rc<Self>, context: &Rc<RuntimeContext>) -> bool {
1220 let started = self.start_self_exit_transition_for_source(context, None);
1221 if started {
1222 if let Some(source) = self.template_node_identifier() {
1223 self.start_bound_exit_transitions(context, &source);
1224 }
1225 }
1226 started
1227 }
1228
1229 fn begin_transition_run(self: &Rc<Self>, context: &Rc<RuntimeContext>, phase: u64) {
1230 let previous_phase = self.transition_phase.get();
1231 let is_takeover = matches!(
1232 (previous_phase, phase),
1233 (TRANSITION_PHASE_ENTER, TRANSITION_PHASE_EXIT)
1234 | (TRANSITION_PHASE_EXIT, TRANSITION_PHASE_ENTER)
1235 );
1236 self.transition_origin_frame
1237 .set(context.globals().elapsed_frames.get());
1238 self.transition_origin_millis
1239 .set(context.globals().elapsed_millis.get());
1240 self.transition_takeover.set(is_takeover);
1241 self.transition_generation
1242 .set(self.transition_generation.get().wrapping_add(1));
1243 self.activate_transition_clock(context);
1244 self.transition_phase.set(phase);
1245 *self.transition_selector_classes.borrow_mut() =
1246 Some(self.selector_metadata.borrow().classes.get());
1247 self.reset_removed_runtime_properties.set(true);
1248 self.reapply_runtime_settings(context);
1249 }
1250
1251 fn activate_transition_clock(&self, context: &Rc<RuntimeContext>) {
1252 let elapsed_frames = context.globals().elapsed_frames.clone();
1253 let origin_frame = self.transition_origin_frame.clone();
1254 let elapsed_frames_for_playhead = elapsed_frames.clone();
1255 let origin_frame_for_playhead = origin_frame.clone();
1256 self.transition_playhead
1257 .replace_with(Property::computed_with_name(
1258 move || {
1259 elapsed_frames_for_playhead
1260 .get()
1261 .saturating_sub(origin_frame_for_playhead.get()) as f64
1262 },
1263 &[elapsed_frames.untyped(), origin_frame.untyped()],
1264 "transition playhead",
1265 ));
1266
1267 let elapsed_millis = context.globals().elapsed_millis.clone();
1268 let origin_millis = self.transition_origin_millis.clone();
1269 let elapsed_millis_for_playhead = elapsed_millis.clone();
1270 let origin_millis_for_playhead = origin_millis.clone();
1271 self.transition_playhead_millis
1272 .replace_with(Property::computed_with_name(
1273 move || {
1274 elapsed_millis_for_playhead
1275 .get()
1276 .saturating_sub(origin_millis_for_playhead.get()) as f64
1277 },
1278 &[elapsed_millis.untyped(), origin_millis.untyped()],
1279 "transition playhead millis",
1280 ));
1281 }
1282
1283 fn deactivate_transition_clock(&self) {
1284 self.transition_playhead
1285 .replace_with(Property::new_with_name(0.0, "transition playhead"));
1286 self.transition_playhead_millis
1287 .replace_with(Property::new_with_name(0.0, "transition playhead millis"));
1288 }
1289
1290 fn start_bound_enter_transitions(
1291 self: &Rc<Self>,
1292 context: &Rc<RuntimeContext>,
1293 source: &UniqueTemplateNodeIdentifier,
1294 ) {
1295 if let Some(containing_component) = self.containing_component.upgrade() {
1296 containing_component.start_descendant_enter_transitions_for_source(context, source);
1297 }
1298 }
1299
1300 fn start_bound_exit_transitions(
1301 self: &Rc<Self>,
1302 context: &Rc<RuntimeContext>,
1303 source: &UniqueTemplateNodeIdentifier,
1304 ) {
1305 if let Some(containing_component) = self.containing_component.upgrade() {
1306 containing_component.start_descendant_exit_transitions_for_source(context, source);
1307 }
1308 }
1309
1310 fn start_descendant_enter_transitions_for_source(
1311 self: &Rc<Self>,
1312 context: &Rc<RuntimeContext>,
1313 source: &UniqueTemplateNodeIdentifier,
1314 ) {
1315 for child in borrow!(self.mounted_children).iter() {
1316 child.start_enter_transition_for_source_recursive(context, source);
1317 }
1318 for child in borrow!(self.sidecar_children).iter() {
1319 child.start_enter_transition_for_source_recursive(context, source);
1320 }
1321 }
1322
1323 fn start_descendant_exit_transitions_for_source(
1324 self: &Rc<Self>,
1325 context: &Rc<RuntimeContext>,
1326 source: &UniqueTemplateNodeIdentifier,
1327 ) {
1328 for child in borrow!(self.mounted_children).iter() {
1329 child.start_exit_transition_for_source_recursive(context, source);
1330 }
1331 for child in borrow!(self.sidecar_children).iter() {
1332 child.start_exit_transition_for_source_recursive(context, source);
1333 }
1334 }
1335
1336 fn start_enter_transition_for_source_recursive(
1337 self: &Rc<Self>,
1338 context: &Rc<RuntimeContext>,
1339 source: &UniqueTemplateNodeIdentifier,
1340 ) {
1341 if self.template_node_identifier().as_ref() != Some(source) {
1342 self.start_self_enter_transition_for_source(context, Some(source));
1343 }
1344 for child in borrow!(self.mounted_children).iter() {
1345 child.start_enter_transition_for_source_recursive(context, source);
1346 }
1347 for child in borrow!(self.sidecar_children).iter() {
1348 child.start_enter_transition_for_source_recursive(context, source);
1349 }
1350 }
1351
1352 fn trigger_bound_enter_transitions_for_mounted_sources_recursive(
1353 self: &Rc<Self>,
1354 context: &Rc<RuntimeContext>,
1355 ) {
1356 if let Some(source) = self.template_node_identifier() {
1357 let should_trigger = {
1358 let instance_node = borrow!(self.instance_node);
1359 instance_node
1360 .base()
1361 .transition_config()
1362 .enter_sources
1363 .contains(&source)
1364 };
1365 if should_trigger {
1366 self.start_bound_enter_transitions(context, &source);
1367 }
1368 }
1369 for child in borrow!(self.mounted_children).iter() {
1370 child.trigger_bound_enter_transitions_for_mounted_sources_recursive(context);
1371 }
1372 for child in borrow!(self.sidecar_children).iter() {
1373 child.trigger_bound_enter_transitions_for_mounted_sources_recursive(context);
1374 }
1375 }
1376
1377 fn start_exit_transition_for_source_recursive(
1378 self: &Rc<Self>,
1379 context: &Rc<RuntimeContext>,
1380 source: &UniqueTemplateNodeIdentifier,
1381 ) {
1382 if self.template_node_identifier().as_ref() != Some(source) {
1383 self.start_self_exit_transition_for_source(context, Some(source));
1384 }
1385 for child in borrow!(self.mounted_children).iter() {
1386 child.start_exit_transition_for_source_recursive(context, source);
1387 }
1388 for child in borrow!(self.sidecar_children).iter() {
1389 child.start_exit_transition_for_source_recursive(context, source);
1390 }
1391 }
1392
1393 fn start_exit_transition_tree(self: &Rc<Self>, context: &Rc<RuntimeContext>) -> bool {
1394 let mut started = self.start_self_exit_transition(context);
1395 for child in borrow!(self.mounted_children).iter() {
1396 started |= child.start_exit_transition_tree(context);
1397 }
1398 started
1399 }
1400
1401 fn start_enter_transition_tree(self: &Rc<Self>, context: &Rc<RuntimeContext>) -> bool {
1402 let was_exiting = self.transition_phase.get() == TRANSITION_PHASE_EXIT;
1403 let started_here = self.start_self_enter_transition_for_source(context, None);
1404 if was_exiting && !started_here {
1405 self.transition_phase.set(TRANSITION_PHASE_IDLE);
1406 self.transition_takeover.set(false);
1407 self.deactivate_transition_clock();
1408 self.exit_started_millis.set(None);
1409 self.transition_selector_classes.borrow_mut().take();
1410 self.reset_removed_runtime_properties.set(true);
1411 self.reapply_runtime_settings(context);
1412 }
1413 let mut started = started_here;
1414 for child in borrow!(self.mounted_children).iter() {
1415 started |= child.start_enter_transition_tree(context);
1416 }
1417 if started_here {
1418 if let Some(source) = self.template_node_identifier() {
1419 self.start_bound_enter_transitions(context, &source);
1420 }
1421 }
1422 started
1423 }
1424
1425 fn retain_exiting_child(
1426 self: &Rc<Self>,
1427 child: Rc<ExpandedNode>,
1428 context: &Rc<RuntimeContext>,
1429 ) {
1430 if Self::has_child(&borrow!(self.exiting_children), &child) {
1431 return;
1432 }
1433 let generation = child.exit_retention_generation.get().wrapping_add(1);
1434 child.exit_retention_generation.set(generation);
1435 borrow_mut!(self.exiting_child_generations).insert(child.id, generation);
1436 borrow_mut!(self.exiting_children).push(child);
1437 self.enable_exit_cleanup_listener(context);
1438 }
1439
1440 fn self_exit_transition_complete(&self, context: &Rc<RuntimeContext>) -> bool {
1441 if self.transition_phase.get() != TRANSITION_PHASE_EXIT {
1442 return true;
1443 }
1444 let instance_node = borrow!(self.instance_node);
1445 let base = instance_node.base();
1446 let transition_config = base.transition_config().clone();
1447 let template_node_identifier = base.template_node_identifier.clone();
1448 let template_node_type_id = base.template_node_type_id.clone();
1449 drop(instance_node);
1450 let duration_complete = self.transition_duration_complete(
1451 transition_config.exit_frame_count,
1452 transition_config.exit_millis_count,
1453 &transition_config.exit_dynamic_durations,
1454 );
1455 let timeout_complete = self
1456 .exit_started_millis
1457 .get()
1458 .map(|started| {
1459 (context.globals().get_elapsed_millis)().saturating_sub(started)
1460 >= transition_config.timeout_ms as u128
1461 })
1462 .unwrap_or(false);
1463 if timeout_complete
1464 && !duration_complete
1465 && !self.exit_timeout_warning_emitted.replace(true)
1466 {
1467 log::warn!(
1468 "Exit transition timed out after {}ms and will be truncated for node {:?}{}{}. Check @out/@transition duration, percent keyframes, and dynamic duration expressions.",
1469 transition_config.timeout_ms,
1470 self.id,
1471 template_node_identifier
1472 .as_ref()
1473 .map(|identifier| format!(" template={}", identifier))
1474 .unwrap_or_default(),
1475 template_node_type_id
1476 .as_ref()
1477 .map(|type_id| format!(" type={}", type_id))
1478 .unwrap_or_default(),
1479 );
1480 }
1481 duration_complete || timeout_complete
1482 }
1483
1484 fn transition_duration_complete(
1485 &self,
1486 frame_count: u64,
1487 millis_count: Option<u64>,
1488 dynamic_durations: &[ValueDefinition],
1489 ) -> bool {
1490 let mut frame_count = frame_count as f64;
1491 let mut millis_count = millis_count.map(|value| value as f64).unwrap_or_default();
1492 let mut unresolved_dynamic_duration = false;
1493
1494 for duration_definition in dynamic_durations {
1495 match evaluate_timeline_duration(duration_definition, &self.stack) {
1496 Some(duration) if duration.is_frame_based() => {
1497 frame_count = frame_count.max(duration.as_frames_f64().max(0.0).ceil());
1498 }
1499 Some(duration) => {
1500 millis_count = millis_count.max(duration.as_milliseconds_f64().max(0.0).ceil());
1501 }
1502 None => unresolved_dynamic_duration = true,
1503 }
1504 }
1505
1506 if unresolved_dynamic_duration
1507 && frame_count <= f64::EPSILON
1508 && millis_count <= f64::EPSILON
1509 {
1510 frame_count = 100.0;
1511 }
1512
1513 let frame_complete =
1514 frame_count <= f64::EPSILON || self.transition_playhead.get() >= frame_count;
1515 let millis_complete =
1516 millis_count <= f64::EPSILON || self.transition_playhead_millis.get() >= millis_count;
1517 frame_complete && millis_complete
1518 }
1519
1520 fn self_enter_transition_complete(&self) -> bool {
1521 if self.transition_phase.get() != TRANSITION_PHASE_ENTER {
1522 return true;
1523 }
1524 let instance_node = borrow!(self.instance_node);
1525 let transition_config = instance_node.base().transition_config().clone();
1526 drop(instance_node);
1527 self.transition_duration_complete(
1528 transition_config.enter_frame_count,
1529 transition_config.enter_millis_count,
1530 &transition_config.enter_dynamic_durations,
1531 )
1532 }
1533
1534 fn enable_enter_cleanup_listener(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1535 if self.enter_cleanup_active.get() {
1536 return;
1537 }
1538 self.enter_cleanup_active.set(true);
1539 let weak_self = Rc::downgrade(self);
1540 let runtime_context = Rc::clone(context);
1541 let elapsed_frames = context.globals().elapsed_frames.clone();
1542 let elapsed_frames_dep = elapsed_frames.untyped();
1543 self.enter_cleanup_listener
1544 .replace_with(Property::computed_with_name(
1545 move || {
1546 let _ = elapsed_frames.get();
1547 if let Some(node) = weak_self.upgrade() {
1548 node.complete_self_enter_transition_if_needed(&runtime_context);
1549 }
1550 },
1551 &[elapsed_frames_dep],
1552 "enter transition cleanup",
1553 ));
1554 context.register_expanded_node_effect_property_named(
1555 self,
1556 &self.enter_cleanup_listener,
1557 "enter transition cleanup",
1558 );
1559 }
1560
1561 fn complete_self_enter_transition_if_needed(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1562 if !self.enter_cleanup_active.get() {
1563 return;
1564 }
1565 if self.transition_phase.get() != TRANSITION_PHASE_ENTER {
1566 self.disable_enter_cleanup_listener();
1567 return;
1568 }
1569 if self.self_enter_transition_complete() {
1570 self.transition_phase.set(TRANSITION_PHASE_IDLE);
1571 self.deactivate_transition_clock();
1572 self.disable_enter_cleanup_listener();
1573 self.transition_selector_classes.borrow_mut().take();
1574 self.reset_removed_runtime_properties.set(true);
1575 self.reapply_runtime_settings(context);
1576 }
1577 }
1578
1579 fn disable_enter_cleanup_listener(&self) {
1580 self.enter_cleanup_active.set(false);
1581 self.enter_cleanup_listener
1582 .replace_with(Property::new_with_name((), "enter transition cleanup"));
1583 }
1584
1585 fn exit_transition_tree_complete(self: &Rc<Self>, context: &Rc<RuntimeContext>) -> bool {
1586 self.self_exit_transition_complete(context)
1587 && borrow!(self.mounted_children)
1588 .iter()
1589 .all(|child| child.exit_transition_tree_complete(context))
1590 }
1591
1592 fn enable_exit_cleanup_listener(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1593 if self.exit_cleanup_active.get() {
1594 return;
1595 }
1596 self.exit_cleanup_active.set(true);
1597 let weak_self = Rc::downgrade(self);
1598 let cloned_context = Rc::clone(context);
1599 let elapsed_frames = context.globals().elapsed_frames.clone();
1600 let elapsed_frames_dep = elapsed_frames.untyped();
1601 self.exit_cleanup_listener
1602 .replace_with(Property::computed_with_name(
1603 move || {
1604 let _ = elapsed_frames.get();
1605 if let Some(node) = weak_self.upgrade() {
1606 node.prune_completed_exit_children(&cloned_context);
1607 }
1608 },
1609 &[elapsed_frames_dep],
1610 "exit transition cleanup",
1611 ));
1612 context.register_expanded_node_effect_property_named(
1613 self,
1614 &self.exit_cleanup_listener,
1615 "exit transition cleanup",
1616 );
1617 }
1618
1619 fn prune_completed_exit_children(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1620 let exiting = std::mem::take(&mut *borrow_mut!(self.exiting_children));
1621 if exiting.is_empty() {
1622 self.exit_cleanup_active.set(false);
1623 self.exit_cleanup_listener
1624 .replace_with(Property::new_with_name((), "exit transition cleanup"));
1625 return;
1626 }
1627
1628 let mut retained = Vec::new();
1629 let active = borrow!(self.active_children).clone();
1630 for child in exiting {
1631 let expected_generation = borrow_mut!(self.exiting_child_generations).remove(&child.id);
1632 if Self::has_child(&active, &child)
1633 || expected_generation != Some(child.exit_retention_generation.get())
1634 {
1635 continue;
1636 }
1637 if child.exit_transition_tree_complete(context) {
1638 child.recurse_unmount(context);
1639 } else {
1640 borrow_mut!(self.exiting_child_generations)
1641 .insert(child.id, child.exit_retention_generation.get());
1642 retained.push(child);
1643 }
1644 }
1645 *borrow_mut!(self.exiting_children) = retained;
1646 self.sync_mounted_children_from_active_and_exiting();
1647 self.children.invalidate();
1651 context.mark_occlusion_dirty();
1652
1653 if borrow!(self.exiting_children).is_empty() {
1654 self.exit_cleanup_active.set(false);
1655 self.exit_cleanup_listener
1656 .replace_with(Property::new_with_name((), "exit transition cleanup"));
1657 }
1658 }
1659
1660 pub fn attach_children(
1661 self: &Rc<Self>,
1662 new_children: Vec<Rc<ExpandedNode>>,
1663 context: &Rc<RuntimeContext>,
1664 parent_frame: &Property<Option<ExpandedNodeIdentifier>>,
1665 ) -> Vec<Rc<ExpandedNode>> {
1666 self.attach_children_with_liquid_glass_scope(
1667 new_children,
1668 context,
1669 parent_frame,
1670 &self.liquid_glass_scope,
1671 )
1672 }
1673
1674 pub fn attach_children_with_liquid_glass_scope(
1675 self: &Rc<Self>,
1676 new_children: Vec<Rc<ExpandedNode>>,
1677 context: &Rc<RuntimeContext>,
1678 parent_frame: &Property<Option<ExpandedNodeIdentifier>>,
1679 liquid_glass_scope: &Property<Option<NativeLiquidGlassScope>>,
1680 ) -> Vec<Rc<ExpandedNode>> {
1681 let mut bindings_changed = false;
1682 for child in new_children.iter() {
1683 let previous_parent = borrow!(child.render_parent).upgrade();
1684 let is_reparented = child.attached.get() > 0
1685 && previous_parent
1686 .as_ref()
1687 .is_some_and(|parent| parent.id != self.id);
1688 if is_reparented {
1689 child.mark_canvas_subtree_dirty_for_reparent(context, true);
1690 if let Some(previous_parent) = previous_parent.as_ref() {
1691 previous_parent.detach_child_for_reparent(child);
1692 }
1693 }
1694
1695 bindings_changed |=
1696 child.ensure_parent_bindings(self, context, parent_frame, liquid_glass_scope);
1697 if is_reparented {
1698 child.rebind_parent_bounds_for_mounted_subtree(context);
1699 child.mark_canvas_subtree_dirty_for_reparent(context, false);
1700 }
1701 }
1702 if Self::same_children(&borrow!(self.active_children), &new_children)
1705 && (self.attached.get() == 0
1706 || new_children.iter().all(|child| child.attached.get() > 0))
1707 {
1708 if bindings_changed {
1709 self.mark_non_reactive_update_subtree_dirty();
1710 }
1711 return self.current_attached_children();
1712 }
1713 let mut newly_mounted_children = Vec::new();
1714 if self.attached.get() > 0 {
1715 let old_active_children = borrow!(self.active_children).clone();
1716 for child in old_active_children.iter() {
1717 if Self::has_child(&new_children, child) {
1718 continue;
1719 }
1720 if child.transition_phase.get() == TRANSITION_PHASE_EXIT {
1721 if child.exit_transition_tree_complete(context) {
1722 Rc::clone(child).recurse_unmount(context);
1723 } else {
1724 self.retain_exiting_child(Rc::clone(child), context);
1725 }
1726 continue;
1727 }
1728 if child.start_exit_transition_tree(context) {
1729 self.retain_exiting_child(Rc::clone(child), context);
1730 } else {
1731 Rc::clone(child).recurse_unmount(context);
1732 }
1733 }
1734 for child in new_children.iter() {
1735 if !Self::has_child(&old_active_children, child) || child.attached.get() == 0 {
1736 if child.attached.get() > 0 {
1737 child.start_enter_transition_tree(context);
1738 } else {
1739 Rc::clone(child).recurse_mount(context);
1740 newly_mounted_children.push(Rc::clone(child));
1741 }
1742 }
1743 }
1744 }
1745 *borrow_mut!(self.active_children) = new_children;
1746 let mounted = self.sync_mounted_children_from_active_and_exiting();
1747 for child in newly_mounted_children {
1748 child.trigger_bound_enter_transitions_for_mounted_sources_recursive(context);
1749 }
1750 self.mark_non_reactive_update_subtree_dirty();
1751 mounted
1752 }
1753
1754 pub fn attach_sidecar_children(
1755 self: &Rc<Self>,
1756 new_children: Vec<Rc<ExpandedNode>>,
1757 context: &Rc<RuntimeContext>,
1758 parent_frame: &Property<Option<ExpandedNodeIdentifier>>,
1759 ) -> Vec<Rc<ExpandedNode>> {
1760 for child in new_children.iter() {
1761 child.ensure_parent_bindings(self, context, parent_frame, &self.liquid_glass_scope);
1762 }
1763 *borrow_mut!(self.sidecar_children) = new_children.clone();
1764 self.mark_non_reactive_update_subtree_dirty();
1765 new_children
1766 }
1767
1768 fn ensure_parent_bindings(
1769 self: &Rc<Self>,
1770 parent: &Rc<Self>,
1771 context: &Rc<RuntimeContext>,
1772 frame: &Property<Option<ExpandedNodeIdentifier>>,
1773 glass: &Property<Option<NativeLiquidGlassScope>>,
1774 ) -> bool {
1775 let properties = {
1776 let parent_cp = parent.get_common_properties();
1777 let parent_cp = borrow!(parent_cp);
1778 let cp = self.get_common_properties();
1779 let cp = borrow!(cp);
1780 vec![
1781 frame.untyped(),
1782 glass.untyped(),
1783 parent.suspended.untyped(),
1784 parent.transform_and_bounds.untyped(),
1785 parent.computed_opacity.untyped(),
1786 parent_cp.padding_x.untyped(),
1787 parent_cp.padding_y.untyped(),
1788 self.container_frame.untyped(),
1789 self.measured_size.untyped(),
1790 cp._suspended.untyped(),
1791 cp.opacity.untyped(),
1792 cp.transform.untyped(),
1793 cp.width.untyped(),
1794 cp.height.untyped(),
1795 cp.x.untyped(),
1796 cp.y.untyped(),
1797 cp.anchor_x.untyped(),
1798 cp.anchor_y.untyped(),
1799 cp.scale_x.untyped(),
1800 cp.scale_y.untyped(),
1801 cp.skew_x.untyped(),
1802 cp.skew_y.untyped(),
1803 cp.rotate.untyped(),
1804 ]
1805 };
1806 let sources = ParentBindingSources {
1807 parent: parent.id,
1808 properties,
1809 };
1810 let same_parent = borrow!(self.render_parent)
1811 .upgrade()
1812 .is_some_and(|old| Rc::ptr_eq(&old, parent));
1813 if same_parent
1814 && borrow!(self.parent_binding_sources)
1815 .as_ref()
1816 .is_some_and(|old| old.same_as(&sources))
1817 {
1818 return false;
1819 }
1820 *borrow_mut!(self.render_parent) = Rc::downgrade(parent);
1821 let frame = frame.clone();
1822 let deps = [frame.untyped()];
1823 self.parent_frame
1824 .replace_with(Property::computed(move || frame.get(), &deps));
1825 let glass = glass.clone();
1826 let deps = [glass.untyped()];
1827 self.liquid_glass_scope
1828 .replace_with(Property::computed(move || glass.get(), &deps));
1829 self.inherit_suspend(parent);
1830 self.bind_to_parent_bounds(context);
1831 *borrow_mut!(self.parent_binding_sources) = Some(sources);
1832 true
1833 }
1834
1835 fn rebind_parent_bounds_for_mounted_subtree(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1836 self.bind_to_parent_bounds(context);
1837 self.bind_occlusion_listener(context);
1838 let mounted_children = borrow!(self.mounted_children).clone();
1839 for child in mounted_children {
1840 child.rebind_parent_bounds_for_mounted_subtree(context);
1841 }
1842 let sidecar_children = borrow!(self.sidecar_children).clone();
1843 for child in sidecar_children {
1844 child.rebind_parent_bounds_for_mounted_subtree(context);
1845 }
1846 }
1847
1848 fn mark_canvas_subtree_dirty_for_reparent(
1849 self: &Rc<Self>,
1850 context: &Rc<RuntimeContext>,
1851 queue_retained_removal: bool,
1852 ) {
1853 let layer = self.occlusion.get().render_layer_id;
1854 context.set_canvas_dirty(layer);
1855 if borrow!(self.instance_node).base().flags().layer == Layer::Canvas {
1856 if queue_retained_removal {
1857 context.enqueue_canvas_node_removal(layer, self.id.to_u32());
1858 }
1859 context.mark_canvas_node_dirty(self.id);
1860 }
1861 context.mark_occlusion_dirty();
1862
1863 let mounted_children = borrow!(self.mounted_children).clone();
1864 for child in mounted_children {
1865 child.mark_canvas_subtree_dirty_for_reparent(context, queue_retained_removal);
1866 }
1867 let sidecar_children = borrow!(self.sidecar_children).clone();
1868 for child in sidecar_children {
1869 child.mark_canvas_subtree_dirty_for_reparent(context, queue_retained_removal);
1870 }
1871 }
1872
1873 fn bind_to_parent_bounds(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) {
1874 self.parent_binding_sources.borrow_mut().take();
1876 self.layout_binding_generation
1877 .set(self.layout_binding_generation.get().wrapping_add(1));
1878 self.subtree_layout_hull_listener.invalidate();
1879 self.children_listener.invalidate();
1880 #[cfg(test)]
1881 self.parent_binding_rebuilds
1882 .set(self.parent_binding_rebuilds.get() + 1);
1883 let render_parent = borrow!(self.render_parent).upgrade();
1884 let parent_transform_and_bounds = render_parent
1885 .as_ref()
1886 .map(|n| n.transform_and_bounds.clone())
1887 .unwrap_or_else(|| ctx.globals().viewport);
1888 let (parent_padding_x, parent_padding_y) = render_parent
1889 .as_ref()
1890 .map(|n| {
1891 let common_props = n.get_common_properties();
1892 let common_props = borrow!(common_props);
1893 (
1894 common_props.padding_x.clone(),
1895 common_props.padding_y.clone(),
1896 )
1897 })
1898 .unwrap_or_default();
1899 let container_frame = self.container_frame.clone();
1900 let deps = [
1901 parent_transform_and_bounds.untyped(),
1902 parent_padding_x.untyped(),
1903 parent_padding_y.untyped(),
1904 container_frame.untyped(),
1905 ];
1906 let effective_parent_transform_and_bounds = Property::computed(
1907 move || {
1908 apply_container_frame(
1909 apply_padding_frame(
1910 parent_transform_and_bounds.get(),
1911 parent_padding_x.get(),
1912 parent_padding_y.get(),
1913 ),
1914 container_frame.get(),
1915 )
1916 },
1917 &deps,
1918 );
1919 let common_props = borrow!(self.common_properties);
1920 let extra_transform = borrow!(common_props).transform.clone();
1921
1922 let transform_and_bounds = compute_tab(
1923 self.layout_properties(),
1924 extra_transform,
1925 effective_parent_transform_and_bounds,
1926 );
1927 self.transform_and_bounds.replace_with(transform_and_bounds);
1928
1929 let parent_opacity = borrow!(self.render_parent)
1930 .upgrade()
1931 .map(|n| n.computed_opacity.clone())
1932 .unwrap_or_else(|| Property::new(1.0));
1933 let common_props = self.get_common_properties();
1934 let self_opacity = borrow!(common_props).opacity.clone();
1935 let deps = [parent_opacity.untyped(), self_opacity.untyped()];
1936 self.computed_opacity.replace_with(Property::computed(
1937 move || {
1938 let parent = parent_opacity.get().clamp(0.0, 1.0);
1939 let local = self_opacity
1940 .get()
1941 .unwrap_or_default()
1942 .to_float_0_1()
1943 .clamp(0.0, 1.0);
1944 (parent * local).clamp(0.0, 1.0)
1945 },
1946 &deps,
1947 ));
1948 }
1949
1950 fn bind_occlusion_listener(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) {
1951 let instance_node = borrow!(self.instance_node);
1952 let mut deps: Vec<_> = borrow!(self.properties_scope)
1953 .iter()
1954 .filter(|(name, _)| instance_node.property_requires_occlusion_recompute(name))
1955 .map(|(_, v)| v.get_untyped_property().clone())
1956 .collect();
1957 drop(instance_node);
1958
1959 deps.extend([
1962 self.transform_and_bounds.untyped(),
1963 self.computed_opacity.untyped(),
1964 self.liquid_glass_scope.untyped(),
1965 ]);
1966
1967 let context = Rc::clone(ctx);
1968 self.occlusion_listener
1969 .replace_with(Property::computed_with_name(
1970 move || {
1971 context.mark_occlusion_dirty();
1972 },
1973 &deps,
1974 "occlusion listener",
1975 ));
1976 ctx.register_expanded_node_effect_property_named(
1977 self,
1978 &self.occlusion_listener,
1979 "occlusion listener",
1980 );
1981 }
1982
1983 fn reapply_runtime_settings(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1984 let instance = Rc::clone(&*borrow!(self.instance_node));
1985 instance.base().bind_properties(self);
1986 self.reset_removed_runtime_properties.set(false);
1987 self.refresh_properties_scope(&instance);
1988 self.mark_non_reactive_update_subtree_dirty();
1989 context.mark_occlusion_dirty();
1990 context.set_canvas_dirty(self.occlusion.get().render_layer_id);
1991 }
1992
1993 fn bind_selector_classes_listener(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
1994 let classes = self.selector_metadata.borrow().classes.clone();
1995 let previous = Rc::new(RefCell::new(classes.get()));
1996 let weak_self = Rc::downgrade(self);
1997 let runtime_context = Rc::clone(context);
1998 self.selector_classes_listener
1999 .replace_with(Property::computed_with_name(
2000 move || {
2001 let current = classes.get();
2002 if *previous.borrow() == current {
2003 return;
2004 }
2005 *previous.borrow_mut() = current;
2006 let Some(node) = weak_self.upgrade() else {
2007 return;
2008 };
2009 node.reset_removed_runtime_properties.set(true);
2010 node.reapply_runtime_settings(&runtime_context);
2011 },
2012 &[self.selector_metadata.borrow().classes.untyped()],
2013 "selector classes listener",
2014 ));
2015 context.register_expanded_node_effect_property_named(
2016 self,
2017 &self.selector_classes_listener,
2018 "selector classes listener",
2019 );
2020 }
2021
2022 fn bind_children_listener(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) {
2023 let deps = [self.children.untyped()];
2024 let weak_self = Rc::downgrade(self);
2025 let context = Rc::clone(ctx);
2026 let previous = RefCell::new(None);
2027 self.children_listener
2028 .replace_with(Property::computed_with_name(
2029 move || {
2030 let Some(node) = weak_self.upgrade() else {
2031 return;
2032 };
2033 if !node.child_structure_changed(&previous) {
2034 return;
2035 }
2036 #[cfg(test)]
2037 node.structural_children_updates
2038 .set(node.structural_children_updates.get() + 1);
2039 if borrow!(node.instance_node).base().flags().is_component
2040 || borrow!(node.expanded_projected_children).is_some()
2041 {
2042 node.compute_flattened_projected_children();
2043 }
2044 if borrow!(node.instance_node).base().flags().invisible_to_slot {
2048 let mut ancestor = node.template_parent.upgrade();
2049 while let Some(parent) = ancestor {
2050 if borrow!(parent.expanded_projected_children).is_some() {
2051 parent.compute_flattened_projected_children();
2052 break;
2053 }
2054 if !borrow!(parent.instance_node)
2055 .base()
2056 .flags()
2057 .invisible_to_slot
2058 {
2059 break;
2060 }
2061 ancestor = parent.template_parent.upgrade();
2062 }
2063 }
2064 context.mark_occlusion_dirty();
2065
2066 let (is_slot, is_component) = {
2067 let instance = borrow!(node.instance_node);
2068 let flags = instance.base().flags();
2069 (flags.is_slot, flags.is_component)
2070 };
2071 if !is_slot {
2072 if is_component {
2073 node.slot_projection_changed.set(());
2074 } else if let Some(containing_component) =
2075 node.containing_component.upgrade()
2076 {
2077 containing_component.slot_projection_changed.set(());
2078 }
2079 }
2080 },
2081 &deps,
2082 "children listener",
2083 ));
2084 ctx.register_expanded_node_effect_property_named(
2085 self,
2086 &self.children_listener,
2087 "children listener",
2088 );
2089 }
2090
2091 fn bind_subtree_layout_hull(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) {
2092 self.rebind_subtree_layout_hull();
2093 let deps = [self.children.untyped()];
2094 let weak_self = Rc::downgrade(self);
2095 let previous = RefCell::new(None);
2096 self.subtree_layout_hull_listener
2097 .replace_with(Property::computed_with_name(
2098 move || {
2099 let Some(node) = weak_self.upgrade() else {
2100 return;
2101 };
2102 if node.child_structure_changed(&previous) {
2103 node.rebind_subtree_layout_hull();
2104 }
2105 },
2106 &deps,
2107 "subtree layout hull listener",
2108 ));
2109 ctx.register_expanded_node_effect_property_named(
2110 self,
2111 &self.subtree_layout_hull_listener,
2112 "subtree layout hull listener",
2113 );
2114 }
2115 fn child_structure_changed(&self, previous: &RefCell<Option<ChildStructure>>) -> bool {
2116 let children = self.children.get();
2119 let ids = |children: &[Rc<ExpandedNode>]| {
2120 children.iter().map(|child| child.id).collect::<Vec<_>>()
2121 };
2122 let current = ChildStructure {
2125 binding_generation: self.layout_binding_generation.get(),
2126 rendered: ids(&children),
2127 active: ids(&borrow!(self.active_children)),
2128 exiting: ids(&borrow!(self.exiting_children)),
2129 };
2130 let mut previous = previous.borrow_mut();
2131 if previous.as_ref() == Some(¤t) {
2132 return false;
2133 }
2134 *previous = Some(current);
2135 true
2136 }
2137
2138 fn rebind_subtree_layout_hull(self: &Rc<Self>) {
2139 #[cfg(test)]
2140 self.layout_hull_rebuilds
2141 .set(self.layout_hull_rebuilds.get() + 1);
2142 let self_transform_and_bounds = self.transform_and_bounds.clone();
2143 let layout_properties = self.layout_properties();
2144 let common_props = self.get_common_properties();
2145 let (width, height, x, y, padding_x, padding_y) = {
2146 let common_props = borrow!(common_props);
2147 (
2148 common_props.width.clone(),
2149 common_props.height.clone(),
2150 common_props.x.clone(),
2151 common_props.y.clone(),
2152 common_props.padding_x.clone(),
2153 common_props.padding_y.clone(),
2154 )
2155 };
2156 let children = self.children.get();
2157 let has_children = !children.is_empty();
2158 let bounds_name = format!("layout bounds (node id: {})", self.id.0);
2159 let bounds_transform_and_bounds = self_transform_and_bounds.clone();
2160 let bounds = Property::computed_with_cutoff_and_name(
2161 move || bounds_transform_and_bounds.get().bounds,
2162 &[self_transform_and_bounds.untyped()],
2163 <(f64, f64)>::eq,
2164 &bounds_name,
2165 );
2166 let mut own_deps = vec![bounds.untyped()];
2167 if has_children {
2168 own_deps.extend([width.untyped(), height.untyped(), x.untyped(), y.untyped()]);
2169 } else {
2170 own_deps.push(layout_properties.untyped());
2171 }
2172
2173 let own_hull_name = format!("own layout hull (node id: {})", self.id.0);
2174 let own_hull = Property::computed_with_cutoff_and_name(
2175 move || {
2176 let bounds = bounds.get();
2177 let (contributes_x, contributes_y) = if has_children {
2178 (
2179 layout_axis_can_contribute_from_parts(width.get(), x.get()),
2180 layout_axis_can_contribute_from_parts(height.get(), y.get()),
2181 )
2182 } else {
2183 let layout_properties = layout_properties.get();
2184 (
2185 layout_axis_can_contribute(&layout_properties, Axis::X),
2186 layout_axis_can_contribute(&layout_properties, Axis::Y),
2187 )
2188 };
2189 LayoutHull::from_axis_ranges(
2190 contributes_x.then_some((0.0, bounds.0)),
2191 contributes_y.then_some((0.0, bounds.1)),
2192 )
2193 },
2194 &own_deps,
2195 crate::layout_hulls_equivalent,
2196 &own_hull_name,
2197 );
2198
2199 if !has_children {
2200 self.subtree_layout_hull.replace_with(own_hull);
2201 return;
2202 }
2203
2204 let mut projected_child_hulls = Vec::with_capacity(children.len());
2205 for child in children.iter() {
2206 let child_cp = child.get_common_properties();
2207 let child_layout_role = borrow!(child_cp).layout_role.clone();
2208 let projection_name = format!(
2209 "projected child layout hull (parent id: {}, child id: {})",
2210 self.id.0, child.id.0
2211 );
2212 projected_child_hulls.push(crate::projected_child_layout_hull_property(
2213 self_transform_and_bounds.clone(),
2214 padding_x.clone(),
2215 padding_y.clone(),
2216 child.transform_and_bounds.clone(),
2217 child.subtree_layout_hull.clone(),
2218 child_layout_role,
2219 &projection_name,
2220 ));
2221 }
2222
2223 let mut deps = vec![own_hull.untyped(), padding_x.untyped(), padding_y.untyped()];
2224 deps.extend(projected_child_hulls.iter().map(Property::untyped));
2225
2226 let property_name = format!("subtree layout hull (node id: {})", self.id.0);
2227 self.subtree_layout_hull
2228 .replace_with(Property::computed_with_cutoff_and_name(
2229 move || {
2230 let mut hull = own_hull.get();
2231 let mut children_hull = LayoutHull::default();
2232 for projected_hull in &projected_child_hulls {
2233 children_hull = children_hull.union(projected_hull.get());
2234 }
2235 hull = hull.union(add_symmetric_padding_to_content_layout_hull(
2236 children_hull,
2237 padding_x.get(),
2238 padding_y.get(),
2239 ));
2240
2241 hull
2242 },
2243 &deps,
2244 crate::layout_hulls_equivalent,
2245 &property_name,
2246 ));
2247 }
2248
2249 pub fn inherit_suspend(self: &Rc<Self>, node: &Rc<Self>) {
2250 self.parent_binding_sources.borrow_mut().take();
2251 let cp = self.get_common_properties();
2252 let self_suspended = borrow!(cp)._suspended.clone();
2253 let parent_suspended = node.suspended.clone();
2254 let deps = [parent_suspended.untyped(), self_suspended.untyped()];
2255 self.suspended.replace_with(Property::computed(
2256 move || {
2257 self_suspended
2258 .get()
2259 .unwrap_or_else(|| parent_suspended.get())
2260 },
2261 &deps,
2262 ));
2263 }
2264
2265 pub fn generate_children(
2266 self: &Rc<Self>,
2267 templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
2268 context: &Rc<RuntimeContext>,
2269 parent_frame: &Property<Option<ExpandedNodeIdentifier>>,
2270 is_mount: bool,
2271 ) -> Vec<Rc<ExpandedNode>> {
2272 self.generate_children_with_liquid_glass_scope(
2273 templates,
2274 context,
2275 parent_frame,
2276 &self.liquid_glass_scope,
2277 is_mount,
2278 )
2279 }
2280
2281 pub fn generate_children_with_liquid_glass_scope(
2282 self: &Rc<Self>,
2283 templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
2284 context: &Rc<RuntimeContext>,
2285 parent_frame: &Property<Option<ExpandedNodeIdentifier>>,
2286 liquid_glass_scope: &Property<Option<NativeLiquidGlassScope>>,
2287 is_mount: bool,
2288 ) -> Vec<Rc<ExpandedNode>> {
2289 let new_children = self.create_children_detached(templates, context, &Rc::downgrade(&self));
2290 let res = if is_mount {
2291 self.attach_children_with_liquid_glass_scope(
2292 new_children,
2293 context,
2294 parent_frame,
2295 liquid_glass_scope,
2296 )
2297 } else {
2298 for child in new_children.iter() {
2299 child.recurse_control_flow_expansion(context);
2300 }
2301
2302 new_children
2303 };
2304 res
2305 }
2306
2307 pub fn run_lifecycle_handlers(
2310 self: &Rc<Self>,
2311 handler_key: &str,
2312 context: &Rc<RuntimeContext>,
2313 ) {
2314 if let Some(ref registry) = borrow!(self.instance_node).base().handler_registry {
2315 if !self.suspended.get() {
2316 for handler in borrow!(registry)
2317 .handlers
2318 .get(handler_key)
2319 .unwrap_or(&Vec::new())
2320 {
2321 (handler.function)(
2322 Rc::clone(&*borrow!(self.properties)),
2323 &self.get_node_context(context),
2324 None,
2325 )
2326 }
2327 }
2328 }
2329 }
2330
2331 pub fn recurse_update(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2332 context.drain_node_effects();
2335 self.recurse_update_mounted(context);
2336 context.drain_node_effects();
2337 }
2338
2339 fn recurse_update_mounted(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2340 if !self.subtree_requires_non_reactive_update.get() {
2341 return;
2342 }
2343 let instance_node = Rc::clone(&*borrow!(self.instance_node));
2344 if instance_node.requires_non_reactive_update(self) {
2345 instance_node.update(&self, context);
2346 }
2347 let mut subtree_requires_non_reactive_update =
2348 Rc::clone(&*borrow!(self.instance_node)).requires_non_reactive_update(self);
2349 let children = borrow!(self.mounted_children).clone();
2350 for child in children.iter() {
2351 child.recurse_update_mounted(context);
2352 subtree_requires_non_reactive_update |=
2353 child.subtree_requires_non_reactive_update.get();
2354 }
2355 let sidecar_children = borrow!(self.sidecar_children).clone();
2356 for child in sidecar_children.iter() {
2357 child.recurse_update_mounted(context);
2358 subtree_requires_non_reactive_update |=
2359 child.subtree_requires_non_reactive_update.get();
2360 }
2361 self.subtree_requires_non_reactive_update
2362 .set(subtree_requires_non_reactive_update);
2363 }
2364
2365 pub fn recurse_sync_import_settings(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2366 if borrow!(self.instance_node).base().flags().is_component {
2367 self.sync_imported_settings(context);
2368 }
2369 for child in self.children.get().iter() {
2370 child.recurse_sync_import_settings(context);
2371 }
2372 for child in borrow!(self.sidecar_children).iter() {
2373 child.recurse_sync_import_settings(context);
2374 }
2375 }
2376
2377 fn recurse_reapply_runtime_settings_for_component_owner(
2378 self: &Rc<Self>,
2379 owner_component_id: ExpandedNodeIdentifier,
2380 context: &Rc<RuntimeContext>,
2381 ) {
2382 if self.is_import_settings_node() {
2383 return;
2384 }
2385
2386 let owned_by_component = self
2387 .containing_component
2388 .upgrade()
2389 .map(|component| component.id == owner_component_id)
2390 .unwrap_or(false);
2391
2392 if owned_by_component {
2393 self.reset_removed_runtime_properties.set(true);
2394 let instance = borrow!(self.instance_node).clone();
2395 self.recreate_with_new_data(instance, context);
2396 }
2397
2398 let children = self.children.get();
2399 for child in children.iter() {
2400 child.recurse_reapply_runtime_settings_for_component_owner(owner_component_id, context);
2401 }
2402
2403 let sidecar_children = borrow!(self.sidecar_children).clone();
2404 for child in sidecar_children.iter() {
2405 child.recurse_reapply_runtime_settings_for_component_owner(owner_component_id, context);
2406 }
2407 }
2408
2409 pub fn recurse_control_flow_expansion(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2410 borrow!(self.instance_node)
2411 .clone()
2412 .handle_control_flow_node_expansion(&self, context);
2413 }
2414
2415 pub fn recurse_mount(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2416 if self.attached.get() == 0 {
2417 borrow!(self.instance_node)
2419 .clone()
2420 .handle_setup_projected_children(&self, context);
2421
2422 if let Some(projected_children) = borrow!(self.expanded_projected_children).as_ref() {
2424 for projected_child in projected_children {
2425 projected_child.recurse_control_flow_expansion(context);
2426 }
2427 self.compute_flattened_projected_children();
2429 }
2430 self.attached.set(self.attached.get() + 1);
2431 self.start_self_enter_transition(context);
2432 context.add_to_cache(&self);
2433 if let Some(ref registry) = borrow!(self.instance_node).base().handler_registry {
2434 for handler in borrow!(registry)
2435 .handlers
2436 .get("mount")
2437 .unwrap_or(&Vec::new())
2438 {
2439 (handler.function)(
2440 Rc::clone(&*borrow!(self.properties)),
2441 &self.get_node_context(context),
2442 None,
2443 )
2444 }
2445 }
2446 borrow!(self.instance_node)
2447 .clone()
2448 .handle_mount(&self, context);
2449 context.register_expanded_node_effect_property_named(
2450 self,
2451 &self.changed_listener,
2452 "changed listener",
2453 );
2454 }
2455 }
2456
2457 pub fn recurse_unmount(self: Rc<Self>, context: &Rc<RuntimeContext>) {
2458 if self.attached.get() == 1 {
2463 self.attached.set(self.attached.get() - 1);
2464 context.remove_from_cache(&self);
2465 for child in borrow!(self.mounted_children).iter() {
2466 Rc::clone(child).recurse_unmount(context);
2467 }
2468 borrow!(self.instance_node).handle_unmount(&self, context);
2469 if let Some(ref registry) = borrow!(self.instance_node).base().handler_registry {
2470 for handler in borrow!(registry)
2471 .handlers
2472 .get("unmount")
2473 .unwrap_or(&Vec::new())
2474 {
2475 (handler.function)(
2476 Rc::clone(&*borrow!(self.properties)),
2477 &self.get_node_context(context),
2478 None,
2479 )
2480 }
2481 }
2482
2483 if self.instance_node.borrow().base().flags().layer == Layer::Canvas {
2484 context.enqueue_canvas_node_removal(
2485 self.occlusion.get().render_layer_id,
2486 self.id.to_u32(),
2487 );
2488 context.set_canvas_dirty(self.occlusion.get().render_layer_id);
2489 }
2490
2491 self.occlusion.set(Default::default());
2493 self.browser_content_layer_id.set(None);
2494 self.changed_listener.replace_with(Property::default());
2495 self.selector_classes_listener
2496 .replace_with(Property::default());
2497 borrow_mut!(self.subscriptions).clear();
2498 borrow_mut!(self.active_children).clear();
2499 borrow_mut!(self.exiting_children).clear();
2500 borrow_mut!(self.exiting_child_generations).clear();
2501 borrow_mut!(self.mounted_children).clear();
2502 borrow_mut!(self.imported_settings_layers).clear();
2503 borrow_mut!(self.imported_settings_signature).clear();
2504 borrow_mut!(self.resolved_property_columns).clear();
2505 borrow_mut!(self.resolved_property_provenance).clear();
2506 self.reset_removed_runtime_properties.set(false);
2507 self.active_children_view.set(Vec::new());
2508 self.exiting_children_view.set(Vec::new());
2509 self.transition_phase.set(TRANSITION_PHASE_IDLE);
2510 self.transition_takeover.set(false);
2511 self.transition_selector_classes.borrow_mut().take();
2512 self.deactivate_transition_clock();
2513 self.enter_cleanup_active.set(false);
2514 self.enter_cleanup_listener
2515 .replace_with(Property::new_with_name((), "enter transition cleanup"));
2516 self.exit_cleanup_active.set(false);
2517 self.exit_cleanup_listener.replace_with(Property::default());
2518 }
2519 }
2520
2521 pub fn is_import_settings_node(&self) -> bool {
2522 borrow!(self.instance_node)
2523 .base()
2524 .template_node_type_id
2525 .as_ref()
2526 .and_then(|type_id| type_id.get_pascal_identifier())
2527 .as_deref()
2528 == Some("ImportSettings")
2529 }
2530
2531 pub fn is_in_import_settings_subtree(&self) -> bool {
2532 let mut current = borrow!(self.render_parent).upgrade();
2533 while let Some(node) = current {
2534 if node.is_import_settings_node() {
2535 return true;
2536 }
2537 current = borrow!(node.render_parent).upgrade();
2538 }
2539 false
2540 }
2541
2542 fn collect_imported_settings_from_node(
2543 node: &Rc<Self>,
2544 in_import_settings: bool,
2545 descend_components: bool,
2546 providers: &mut Vec<RuntimeSettingsLayer>,
2547 ) {
2548 if node.is_import_settings_node() {
2549 let sidecar_children = borrow!(node.sidecar_children).clone();
2550 for child in sidecar_children.iter() {
2551 Self::collect_imported_settings_from_node(child, true, true, providers);
2552 }
2553 return;
2554 }
2555
2556 if borrow!(node.instance_node).base().flags().is_component {
2557 if in_import_settings {
2558 let base = borrow!(node.instance_node);
2559 let component_settings = base.base().component_settings.clone();
2560 let provider_type_id = base
2561 .base()
2562 .template_node_type_id
2563 .clone()
2564 .unwrap_or_default();
2565 drop(base);
2566 if let Some(settings) = component_settings {
2567 if !settings.is_empty() {
2568 providers.push(RuntimeSettingsLayer {
2569 provider_id: node.id,
2570 provider_type_id,
2571 provider_stack: node.stack.push(borrow!(node.properties_scope).clone()),
2572 settings,
2573 });
2574 }
2575 }
2576 let children = node.children.get();
2577 for child in children.iter() {
2578 Self::collect_imported_settings_from_node(child, false, true, providers);
2579 }
2580 let sidecar_children = borrow!(node.sidecar_children).clone();
2581 for child in sidecar_children.iter() {
2582 Self::collect_imported_settings_from_node(child, false, true, providers);
2583 }
2584 return;
2585 }
2586 if !descend_components {
2587 return;
2588 }
2589 }
2590
2591 let children = node.children.get();
2592 for child in children.iter() {
2593 Self::collect_imported_settings_from_node(
2594 child,
2595 in_import_settings,
2596 descend_components,
2597 providers,
2598 );
2599 }
2600 let sidecar_children = borrow!(node.sidecar_children).clone();
2601 for child in sidecar_children.iter() {
2602 Self::collect_imported_settings_from_node(
2603 child,
2604 in_import_settings,
2605 descend_components,
2606 providers,
2607 );
2608 }
2609 }
2610
2611 pub fn sync_imported_settings(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
2612 let mut providers = Vec::new();
2613 let children = self.children.get();
2614 for child in children.iter() {
2615 Self::collect_imported_settings_from_node(child, false, false, &mut providers);
2616 }
2617 let sidecar_children = borrow!(self.sidecar_children).clone();
2618 for child in sidecar_children.iter() {
2619 Self::collect_imported_settings_from_node(child, false, false, &mut providers);
2620 }
2621
2622 let signature = providers
2623 .iter()
2624 .map(|layer| RuntimeSettingsSignatureEntry {
2625 provider_id: layer.provider_id,
2626 provider_type_id: layer.provider_type_id.clone(),
2627 })
2628 .collect::<Vec<_>>();
2629 if *borrow!(self.imported_settings_signature) == signature {
2630 return;
2631 }
2632
2633 *borrow_mut!(self.imported_settings_signature) = signature;
2634 *borrow_mut!(self.imported_settings_layers) = providers;
2635
2636 let root_children = self.children.get();
2637 for child in root_children.iter() {
2638 if child.is_import_settings_node() {
2639 continue;
2640 }
2641 child.recurse_reapply_runtime_settings_for_component_owner(self.id, context);
2642 child.recurse_emit_mount_updates();
2643 }
2644 }
2645
2646 pub fn recurse_render_queue(
2647 self: &Rc<Self>,
2648 ctx: &Rc<RuntimeContext>,
2649 rcs: &mut dyn RenderContext,
2650 ) {
2651 let cp = self.get_common_properties();
2652 let cp = borrow!(cp);
2653 if cp.unclippable.get().unwrap_or(false) {
2654 ctx.queue_render(Rc::clone(&self));
2655 } else {
2656 self.recurse_render(ctx, rcs);
2657 }
2658 }
2659
2660 pub(crate) fn render_parent_node(&self) -> Option<Rc<ExpandedNode>> {
2661 borrow!(self.render_parent).upgrade()
2662 }
2663
2664 pub(crate) fn is_unclippable(&self) -> bool {
2665 let cp = self.get_common_properties();
2666 let cp = borrow!(cp);
2667 cp.unclippable.get().unwrap_or(false)
2668 }
2669
2670 pub(crate) fn recurse_render_filtered(
2671 self: &Rc<Self>,
2672 ctx: &Rc<RuntimeContext>,
2673 rcs: &mut dyn RenderContext,
2674 render_path_nodes: &HashSet<ExpandedNodeIdentifier>,
2675 dirty_nodes: &HashSet<ExpandedNodeIdentifier>,
2676 stats: &mut FilteredRenderStats,
2677 ) {
2678 stats.path_nodes_visited += 1;
2679 borrow!(self.instance_node).handle_pre_render(&self, ctx, rcs);
2680 for child in self.children.get().iter().rev() {
2681 if render_path_nodes.contains(&child.id) {
2682 child.recurse_render_filtered(ctx, rcs, render_path_nodes, dirty_nodes, stats);
2683 } else {
2684 stats.skipped_subtrees += 1;
2685 }
2686 }
2687 if dirty_nodes.contains(&self.id) {
2688 borrow!(self.instance_node).render(&self, ctx, rcs);
2689 stats.dirty_nodes_rendered += 1;
2690 }
2691 borrow!(self.instance_node).handle_post_render(&self, ctx, rcs);
2692 }
2693
2694 pub fn recurse_render(self: &Rc<Self>, ctx: &Rc<RuntimeContext>, rcs: &mut dyn RenderContext) {
2695 borrow!(self.instance_node).handle_pre_render(&self, ctx, rcs);
2696 for child in self.children.get().iter().rev() {
2697 child.recurse_render_queue(ctx, rcs);
2698 }
2699 borrow!(self.instance_node).render(&self, ctx, rcs);
2700 borrow!(self.instance_node).handle_post_render(&self, ctx, rcs);
2701 }
2702
2703 pub fn with_properties_unwrapped<T: ToFromPaxAny, R>(
2709 &self,
2710 callback: impl FnOnce(&mut T) -> R,
2711 ) -> R {
2712 self.try_with_properties_unwrapped(callback)
2713 .expect("properties not of expected type")
2714 }
2715
2716 pub fn try_with_properties_unwrapped<T: ToFromPaxAny, R>(
2717 &self,
2718 callback: impl FnOnce(&mut T) -> R,
2719 ) -> Option<R> {
2720 let properties = borrow_mut!(self.properties);
2722 let mut borrowed = borrow_mut!(properties);
2723 let Ok(mut val) = T::mut_from_pax_any(&mut *borrowed) else {
2725 return None;
2726 };
2727 Some(callback(&mut val))
2728 }
2729
2730 pub fn recurse_visit_postorder(self: &Rc<Self>, func: &mut impl FnMut(&Rc<Self>)) {
2731 self.compute_flattened_projected_children();
2734 for child in self.children.get().iter().rev() {
2735 child.recurse_visit_postorder(func)
2736 }
2737 func(self);
2738 }
2739
2740 pub fn get_node_context(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) -> NodeContext {
2741 let globals = ctx.globals();
2742 let viewport = super::viewport_info_property(&globals.viewport);
2743 let target = Property::new(globals.target);
2744 let t_and_b = self.transform_and_bounds.clone();
2745 let deps = [t_and_b.untyped()];
2746 let bounds_name = format!("node context bounds (node id: {})", self.id.0);
2747 let bounds_self = Property::computed_with_cutoff_and_name(
2748 move || t_and_b.get().bounds,
2749 &deps,
2750 <(f64, f64)>::eq,
2751 &bounds_name,
2752 );
2753 let render_parent = borrow!(self.render_parent).upgrade();
2754 let t_and_b_parent = render_parent
2755 .as_ref()
2756 .map(|parent| parent.transform_and_bounds.clone())
2757 .unwrap_or_else(|| globals.viewport.clone());
2758 let (parent_padding_x, parent_padding_y) = render_parent
2759 .as_ref()
2760 .map(|parent| {
2761 let common_props = parent.get_common_properties();
2762 let common_props = borrow!(common_props);
2763 (
2764 common_props.padding_x.clone(),
2765 common_props.padding_y.clone(),
2766 )
2767 })
2768 .unwrap_or_default();
2769 let deps = [
2770 t_and_b_parent.untyped(),
2771 parent_padding_x.untyped(),
2772 parent_padding_y.untyped(),
2773 ];
2774 let bounds_parent = Property::computed(
2775 move || {
2776 apply_padding_frame(
2777 t_and_b_parent.get(),
2778 parent_padding_x.get(),
2779 parent_padding_y.get(),
2780 )
2781 .bounds
2782 },
2783 &deps,
2784 );
2785
2786 let owns_projected_children = borrow!(self.instance_node).base().flags().is_component
2787 || borrow!(self.expanded_projected_children).is_some();
2788
2789 let projected_children_count = if owns_projected_children {
2790 self.flattened_projected_children_count.clone()
2791 } else {
2792 self.containing_component
2793 .upgrade()
2794 .map(|v| v.flattened_projected_children_count.clone())
2795 .unwrap_or_default()
2796 };
2797
2798 let projected_children = if owns_projected_children {
2799 self.expanded_and_flattened_projected_children.clone()
2800 } else {
2801 self.containing_component
2802 .upgrade()
2803 .map(|v| v.expanded_and_flattened_projected_children.clone())
2804 .unwrap_or_default()
2805 };
2806
2807 let projected_children_changed = if owns_projected_children {
2808 self.projected_children_changed.clone()
2809 } else {
2810 self.containing_component
2811 .upgrade()
2812 .map(|v| v.projected_children_changed.clone())
2813 .unwrap_or_default()
2814 };
2815 let received_children = match borrow!(self.instance_node).received_children_source() {
2819 ReceivedChildrenSource::Owned => self.active_children_view.clone(),
2820 ReceivedChildrenSource::Projected => {
2821 if owns_projected_children {
2822 self.expanded_and_flattened_projected_children.clone()
2823 } else {
2824 self.containing_component
2825 .upgrade()
2826 .map(|v| v.expanded_and_flattened_projected_children.clone())
2827 .unwrap_or_default()
2828 }
2829 }
2830 };
2831 let received_children_count_source = received_children.clone();
2832 let received_children_count = Property::computed(
2833 move || received_children_count_source.get().len(),
2834 &[received_children.untyped()],
2835 );
2836 let received_children_signal = received_children.clone();
2837 let received_children_changed = Property::computed(
2838 move || {
2839 let _ = received_children_signal.get();
2840 },
2841 &[received_children.untyped()],
2842 );
2843 let retained_received_children =
2847 match borrow!(self.instance_node).received_children_source() {
2848 ReceivedChildrenSource::Owned => self.exiting_children_view.clone(),
2849 ReceivedChildrenSource::Projected
2850 if borrow!(self.expanded_projected_children).is_some() =>
2851 {
2852 self.exiting_children_view.clone()
2853 }
2854 ReceivedChildrenSource::Projected => Property::default(),
2855 };
2856 let retained_received_children_signal = retained_received_children.clone();
2857 let retained_received_children_changed = Property::computed(
2858 move || {
2859 let _ = retained_received_children_signal.get();
2860 },
2861 &[retained_received_children.untyped()],
2862 );
2863
2864 let last_frame = Rc::new(RefCell::new(globals.elapsed_frames.get()));
2865 let suspended = self.suspended.clone();
2866 let elapsed_frames = globals.elapsed_frames.clone();
2867 let elapsed_millis = globals.elapsed_millis.clone();
2868 let deps = [elapsed_frames.untyped(), suspended.untyped()];
2869 let elapsed_frames_frozen_if_suspended = Property::computed(
2873 move || {
2874 if suspended.get() {
2875 *borrow!(last_frame)
2876 } else {
2877 let val = elapsed_frames.get();
2878 *borrow_mut!(last_frame) = val;
2879 val
2880 }
2881 },
2882 &deps,
2883 );
2884 NodeContext {
2888 slot_index: self.slot_index.clone(),
2889 local_stack_frame: Rc::clone(&self.stack),
2890 expanded_node: Rc::downgrade(&self),
2891 containing_component: Weak::clone(&self.containing_component),
2892 elapsed_frames: elapsed_frames_frozen_if_suspended,
2893 elapsed_millis,
2894 gyro: globals.gyro.clone(),
2895 accel: globals.accel.clone(),
2896 bounds_self,
2897 bounds_parent,
2898 measured_size: self.measured_size.clone(),
2899 subtree_layout_hull: self.subtree_layout_hull.clone(),
2900 runtime_context: ctx.clone(),
2901 platform: globals.platform.clone(),
2902 os: globals.os.clone(),
2903 target,
2904 viewport,
2905 get_elapsed_millis: globals.get_elapsed_millis,
2906 projected_children_count,
2907 projected_children,
2908 node_transform_and_bounds: self.transform_and_bounds.get(),
2909 projected_children_changed,
2910 received_children,
2911 received_children_count,
2912 received_children_changed,
2913 retained_received_children,
2914 retained_received_children_changed,
2915 #[cfg(feature = "designtime")]
2916 designtime: globals.designtime.clone(),
2917 }
2918 }
2919
2920 pub fn get_common_properties(&self) -> Rc<RefCell<CommonProperties>> {
2921 Rc::clone(&*borrow!(self.common_properties))
2922 }
2923
2924 pub fn is_layout_breakout(&self) -> bool {
2925 let common_props = self.get_common_properties();
2926 let is_breakout = borrow!(common_props).layout_role.get() == Some(LayoutRole::Breakout);
2927 is_breakout
2928 }
2929
2930 pub fn ray_cast_test(&self, ray: Point2<Window>) -> bool {
2933 let cp = borrow!(self.common_properties);
2934
2935 if !borrow!(&*cp)._raycastable.get().unwrap_or(true) {
2937 return false;
2938 }
2939 drop(cp);
2940 borrow!(self.instance_node).ray_cast_test(self, ray)
2941 }
2942
2943 pub fn compute_flattened_projected_children(&self) {
2944 if let Some(projected_children) = borrow!(self.expanded_projected_children).as_ref() {
2949 let new_flattened = flatten_expanded_nodes_for_projection(projected_children);
2950 let old_and_new_filtered_same =
2951 self.expanded_and_flattened_projected_children
2952 .read(|flattened| {
2953 flattened
2954 .iter()
2955 .map(|n| n.id)
2956 .eq(new_flattened.iter().map(|n| n.id))
2957 });
2958
2959 if !old_and_new_filtered_same {
2960 self.flattened_projected_children_count
2961 .set(new_flattened.len());
2962 self.expanded_and_flattened_projected_children
2963 .set(new_flattened);
2964 for (i, projected_child) in self
2965 .expanded_and_flattened_projected_children
2966 .get()
2967 .iter()
2968 .enumerate()
2969 {
2970 if projected_child.slot_index.get() != Some(i) {
2971 projected_child.slot_index.set(Some(i));
2972 };
2973 }
2974 }
2975 }
2976 }
2977
2978 dispatch_event_handler!(dispatch_scroll, Scroll, SCROLL_HANDLERS, true);
2979 dispatch_event_handler!(dispatch_touch_start, TouchStart, TOUCH_START_HANDLERS, true);
2980
2981 dispatch_event_handler!(dispatch_touch_move, TouchMove, TOUCH_MOVE_HANDLERS, true);
2982 dispatch_event_handler!(dispatch_touch_end, TouchEnd, TOUCH_END_HANDLERS, true);
2983 dispatch_event_handler!(
2984 dispatch_touch_cancel,
2985 TouchCancel,
2986 TOUCH_CANCEL_HANDLERS,
2987 true
2988 );
2989 dispatch_event_handler!(dispatch_key_down, KeyDown, KEY_DOWN_HANDLERS, false);
2990 dispatch_event_handler!(dispatch_key_up, KeyUp, KEY_UP_HANDLERS, false);
2991 dispatch_event_handler!(dispatch_key_press, KeyPress, KEY_PRESS_HANDLERS, false);
2992 dispatch_event_handler!(
2993 dispatch_checkbox_change,
2994 CheckboxChange,
2995 CHECKBOX_CHANGE_HANDLERS,
2996 true
2997 );
2998 dispatch_event_handler!(
2999 dispatch_slider_change,
3000 SliderChange,
3001 SLIDER_CHANGE_HANDLERS,
3002 true
3003 );
3004 dispatch_event_handler!(
3005 dispatch_textbox_change,
3006 TextboxChange,
3007 TEXTBOX_CHANGE_HANDLERS,
3008 true
3009 );
3010 dispatch_event_handler!(dispatch_text_input, TextInput, TEXT_INPUT_HANDLERS, true);
3011 dispatch_event_handler!(
3012 dispatch_textbox_input,
3013 TextboxInput,
3014 TEXTBOX_INPUT_HANDLERS,
3015 true
3016 );
3017 dispatch_event_handler!(
3018 dispatch_button_click,
3019 ButtonClick,
3020 BUTTON_CLICK_HANDLERS,
3021 true
3022 );
3023 dispatch_event_handler!(
3024 dispatch_photo_picker_change,
3025 PhotoPickerChange,
3026 PHOTO_PICKER_CHANGE_HANDLERS,
3027 true
3028 );
3029 dispatch_event_handler!(dispatch_mouse_down, MouseDown, MOUSE_DOWN_HANDLERS, true);
3030 dispatch_event_handler!(dispatch_mouse_up, MouseUp, MOUSE_UP_HANDLERS, true);
3031 dispatch_event_handler!(dispatch_mouse_move, MouseMove, MOUSE_MOVE_HANDLERS, true);
3032 dispatch_event_handler!(dispatch_mouse_over, MouseOver, MOUSE_OVER_HANDLERS, false);
3033 dispatch_event_handler!(dispatch_mouse_out, MouseOut, MOUSE_OUT_HANDLERS, false);
3034 dispatch_event_handler!(
3035 dispatch_double_click,
3036 DoubleClick,
3037 DOUBLE_CLICK_HANDLERS,
3038 true
3039 );
3040 dispatch_event_handler!(
3041 dispatch_context_menu,
3042 ContextMenu,
3043 CONTEXT_MENU_HANDLERS,
3044 true
3045 );
3046 fn dispatch_pointer_activation(
3047 self: &Rc<Self>,
3048 event: Event<Click>,
3049 source: PointerActivationSource,
3050 ctx: &Rc<RuntimeContext>,
3051 ) -> bool {
3052 let has_click = self.has_event_handlers(CLICK_HANDLERS);
3053 let has_tap = self.has_event_handlers(TAP_HANDLERS);
3054 let handler_key = match (has_click, has_tap, source) {
3055 (true, true, PointerActivationSource::Mouse) => Some(CLICK_HANDLERS),
3056 (true, true, PointerActivationSource::Touch) => Some(TAP_HANDLERS),
3057 (true, false, _) => Some(CLICK_HANDLERS),
3058 (false, true, _) => Some(TAP_HANDLERS),
3059 (false, false, _) => None,
3060 };
3061
3062 if let Some(handler_key) = handler_key {
3063 self.run_event_handlers_for_key(handler_key, &event, ctx);
3064 }
3065
3066 if let Some(parent) = self.template_parent.upgrade() {
3067 return parent.dispatch_pointer_activation(event, source, ctx);
3068 }
3069 event.cancelled()
3070 }
3071
3072 pub fn dispatch_click(
3073 self: &Rc<Self>,
3074 event: Event<Click>,
3075 _globals: &Globals,
3076 ctx: &Rc<RuntimeContext>,
3077 ) -> bool {
3078 self.dispatch_pointer_activation(event, PointerActivationSource::Mouse, ctx)
3079 }
3080
3081 pub fn dispatch_tap(
3082 self: &Rc<Self>,
3083 event: Event<Click>,
3084 _globals: &Globals,
3085 ctx: &Rc<RuntimeContext>,
3086 ) -> bool {
3087 self.dispatch_pointer_activation(event, PointerActivationSource::Touch, ctx)
3088 }
3089
3090 dispatch_event_handler!(dispatch_wheel, Wheel, WHEEL_HANDLERS, true);
3091 dispatch_event_handler!(dispatch_drop, Drop, DROP_HANDLERS, true);
3092 dispatch_event_handler!(dispatch_gyro, Gyro, GYRO_HANDLERS, false);
3093 dispatch_event_handler!(dispatch_accel, Accel, ACCEL_HANDLERS, false);
3094 dispatch_event_handler!(dispatch_focus, Focus, FOCUSED_HANDLERS, false);
3095 dispatch_event_handler!(
3096 dispatch_select_start,
3097 SelectStart,
3098 SELECT_START_HANDLERS,
3099 false
3100 );
3101
3102 pub fn dispatch_custom_event(
3103 self: &Rc<Self>,
3104 identifier: &str,
3105 ctx: &Rc<RuntimeContext>,
3106 ) -> Result<(), String> {
3107 let component_origin_instance = borrow!(self.instance_node);
3108 let registry = component_origin_instance
3109 .base()
3110 .handler_registry
3111 .as_ref()
3112 .ok_or_else(|| "no registry present".to_owned())?;
3113
3114 let parent_component = self
3115 .containing_component
3116 .upgrade()
3117 .ok_or_else(|| "can't dispatch from root (has no parent)".to_owned())?;
3118 let properties = borrow!(parent_component.properties);
3119
3120 for handler in borrow!(registry)
3121 .handlers
3122 .get(identifier)
3123 .expect("presence should have been checked when added to custom_event_queue")
3124 {
3125 (handler.function)(Rc::clone(&*properties), &self.get_node_context(ctx), None)
3126 }
3127 Ok(())
3128 }
3129
3130 pub fn set_measured_size(self: &Rc<ExpandedNode>, width: f64, height: f64) {
3132 self.measured_size.set(Some((width, height)));
3133 }
3134
3135 pub fn chassis_resize_request(self: &Rc<ExpandedNode>, width: f64, height: f64) {
3137 self.set_measured_size(width, height);
3138 }
3139
3140 pub fn layout_properties(self: &Rc<ExpandedNode>) -> Property<LayoutProperties> {
3143 let common_props = self.get_common_properties();
3144 let common_props = borrow!(common_props);
3145 let cp_width = common_props.width.clone();
3146 let cp_height = common_props.height.clone();
3147 let cp_transform = common_props.transform.clone();
3148 let cp_anchor_x = common_props.anchor_x.clone();
3149 let cp_anchor_y = common_props.anchor_y.clone();
3150 let cp_scale_x = common_props.scale_x.clone();
3151 let cp_scale_y = common_props.scale_y.clone();
3152 let cp_skew_x = common_props.skew_x.clone();
3153 let cp_skew_y = common_props.skew_y.clone();
3154 let cp_rotate = common_props.rotate.clone();
3155 let cp_x = common_props.x.clone();
3156 let cp_y = common_props.y.clone();
3157 let measured_size = self.measured_size.clone();
3158 let deps = [
3159 cp_width.untyped(),
3160 cp_height.untyped(),
3161 cp_transform.untyped(),
3162 cp_anchor_x.untyped(),
3163 cp_anchor_y.untyped(),
3164 cp_scale_x.untyped(),
3165 cp_scale_y.untyped(),
3166 cp_skew_x.untyped(),
3167 cp_skew_y.untyped(),
3168 cp_rotate.untyped(),
3169 cp_x.untyped(),
3170 cp_y.untyped(),
3171 measured_size.untyped(),
3172 ];
3173
3174 Property::computed(
3175 move || {
3176 let fallback = measured_size.get();
3178 let (w_fallback, h_fallback) = match fallback {
3179 Some((wf, hf)) => (Some(wf), Some(hf)),
3180 None => (None, None),
3181 };
3182
3183 LayoutProperties {
3184 x: cp_x.get(),
3185 y: cp_y.get(),
3186 width: cp_width
3187 .get()
3188 .or(w_fallback.map(|v| Size::Pixels(v.into()))),
3189 height: cp_height
3190 .get()
3191 .or(h_fallback.map(|v| Size::Pixels(v.into()))),
3192 rotate: cp_rotate.get(),
3193 scale_x: cp_scale_x
3195 .get()
3196 .map(|v| Percent((100.0 * v.expect_percent()).into())),
3197 scale_y: cp_scale_y
3198 .get()
3199 .map(|v| Percent((100.0 * v.expect_percent()).into())),
3200 anchor_x: cp_anchor_x.get(),
3201 anchor_y: cp_anchor_y.get(),
3202 skew_x: cp_skew_x.get(),
3203 skew_y: cp_skew_y.get(),
3204 }
3205 },
3206 &deps,
3207 )
3208 }
3209}
3210
3211fn layout_axis_can_contribute(layout: &LayoutProperties, axis: Axis) -> bool {
3212 let size = match axis {
3213 Axis::X => layout.width,
3214 Axis::Y => layout.height,
3215 };
3216 let position = match axis {
3217 Axis::X => layout.x,
3218 Axis::Y => layout.y,
3219 };
3220
3221 size.is_some_and(|size| !size_depends_on_parent(size))
3222 && !position.is_some_and(size_depends_on_parent)
3223}
3224
3225fn layout_axis_can_contribute_from_parts(size: Option<Size>, position: Option<Size>) -> bool {
3226 size.is_some_and(|size| !size_depends_on_parent(size))
3227 && !position.is_some_and(size_depends_on_parent)
3228}
3229
3230fn size_depends_on_parent(size: Size) -> bool {
3231 match size {
3232 Size::Pixels(_) => false,
3233 Size::Percent(percent) => percent.to_float().abs() > f64::EPSILON,
3234 Size::Combined(_, percent) => percent.to_float().abs() > f64::EPSILON,
3235 }
3236}
3237
3238fn flatten_expanded_nodes_for_projection(nodes: &[Rc<ExpandedNode>]) -> Vec<Rc<ExpandedNode>> {
3241 let mut result: Vec<Rc<ExpandedNode>> = vec![];
3242 for node in nodes {
3243 if borrow!(node.instance_node).base().flags().invisible_to_slot {
3244 result.extend(flatten_expanded_nodes_for_projection(
3245 node.children
3246 .get()
3247 .clone()
3248 .into_iter()
3249 .collect::<Vec<_>>()
3250 .as_slice(),
3251 ));
3252 } else {
3253 result.push(Rc::clone(&node))
3254 }
3255 }
3256 result
3257}
3258
3259impl std::fmt::Debug for ExpandedNode {
3260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3261 pub struct Fmt<F>(pub F)
3264 where
3265 F: Fn(&mut fmt::Formatter) -> fmt::Result;
3266
3267 impl<F> fmt::Debug for Fmt<F>
3268 where
3269 F: Fn(&mut fmt::Formatter) -> fmt::Result,
3270 {
3271 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3272 (self.0)(f)
3273 }
3274 }
3275
3276 f.debug_struct("ExpandedNode")
3277 .field(
3278 "instance_node",
3279 &Fmt(|f| borrow!(self.instance_node).resolve_debug(f, Some(self))),
3280 )
3281 .field("id", &self.id)
3282 .field("common_properties", &borrow!(self.common_properties))
3283 .field("transform_and_bounds", &self.transform_and_bounds)
3284 .field("children", &self.children.get().iter().collect::<Vec<_>>())
3285 .field(
3286 "projected_children",
3287 &self
3288 .expanded_and_flattened_projected_children
3289 .get()
3290 .iter()
3291 .map(|v| v.id)
3292 .collect::<Vec<_>>(),
3293 )
3294 .field("render_placement", &self.occlusion.get())
3295 .field(
3296 "containing_component",
3297 &self.containing_component.upgrade().map(|v| v.id.clone()),
3298 )
3299 .finish()
3300 }
3301}