pax_runtime/engine/
expanded_node.rs

1use crate::api::TextInput;
2use crate::node_interface::NodeLocal;
3use pax_runtime_api::pax_value::{ImplToFromPaxAny, PaxAny, ToFromPaxAny};
4use pax_runtime_api::{
5    borrow, borrow_mut, use_RefCell, Focus, Interpolatable, Layer, Percent, Property, SelectStart,
6    Variable,
7};
8
9use crate::api::math::Point2;
10use crate::constants::{
11    BUTTON_CLICK_HANDLERS, CHECKBOX_CHANGE_HANDLERS, CLAP_HANDLERS, CLICK_HANDLERS,
12    CONTEXT_MENU_HANDLERS, DOUBLE_CLICK_HANDLERS, DROP_HANDLERS, FOCUSED_HANDLERS,
13    KEY_DOWN_HANDLERS, KEY_PRESS_HANDLERS, KEY_UP_HANDLERS, MOUSE_DOWN_HANDLERS,
14    MOUSE_MOVE_HANDLERS, MOUSE_OUT_HANDLERS, MOUSE_OVER_HANDLERS, MOUSE_UP_HANDLERS,
15    SCROLL_HANDLERS, SELECT_START_HANDLERS, TEXTBOX_CHANGE_HANDLERS, TEXTBOX_INPUT_HANDLERS,
16    TEXT_INPUT_HANDLERS, TOUCH_END_HANDLERS, TOUCH_MOVE_HANDLERS, TOUCH_START_HANDLERS,
17    WHEEL_HANDLERS,
18};
19use_RefCell!();
20use crate::{ExpandedNodeIdentifier, Globals, LayoutProperties, TransformAndBounds};
21use core::fmt;
22use std::cell::Cell;
23use std::collections::HashMap;
24use std::rc::{Rc, Weak};
25
26use crate::api::{
27    ButtonClick, CheckboxChange, Clap, Click, CommonProperties, ContextMenu, DoubleClick, Drop,
28    Event, KeyDown, KeyPress, KeyUp, MouseDown, MouseMove, MouseOut, MouseOver, MouseUp,
29    NodeContext, RenderContext, Scroll, Size, TextboxChange, TextboxInput, TouchEnd, TouchMove,
30    TouchStart, Wheel, Window,
31};
32
33use crate::{
34    compute_tab, ComponentInstance, HandlerLocation, InstanceNode, InstanceNodePtr, RuntimeContext,
35    RuntimePropertiesStackFrame,
36};
37
38#[derive(Clone)]
39pub struct ExpandedNode {
40    #[allow(dead_code)]
41    /// Unique ID of this expanded node, roughly encoding an address in the tree, where the first u32 is the instance ID
42    /// and the subsequent u32s represent addresses within an expanded tree via Repeat.
43    pub id: ExpandedNodeIdentifier,
44
45    /// Pointer to the unexpanded `instance_node` underlying this ExpandedNode
46    pub instance_node: RefCell<InstanceNodePtr>,
47
48    /// Pointer (`Weak` to avoid Rc cycle memory leaks) to the ExpandedNode
49    /// rendered directly above this one.
50    pub render_parent: RefCell<Weak<ExpandedNode>>,
51
52    /// Pointer (`Weak` to avoid Rc cycle memory leaks) to the ExpandedNode
53    /// in the template directly above this one.
54    pub template_parent: Weak<ExpandedNode>,
55
56    /// Id of closest frame present in the node tree.
57    /// included as a parameter on AnyCreatePatch when
58    /// creating a native element to know what clipping context
59    /// to attach to
60    pub parent_frame: Property<Option<ExpandedNodeIdentifier>>,
61
62    /// Reference to the _component for which this `ExpandedNode` is a template member._  Used at least for
63    /// getting a reference to slot_children for `slot`.  `Option`al because the very root instance node (root component, root instance node)
64    /// has a corollary "root component expanded node."  That very root expanded node _does not have_ a containing ExpandedNode component,
65    /// thus `containing_component` is `Option`al.
66    pub containing_component: Weak<ExpandedNode>,
67
68    /// Persistent clone of the state of the [`PropertiesTreeShared#runtime_properties_stack`] at the time that this node was expanded (this is expected to remain immutable
69    /// through the lifetime of the program after the initial expansion; however, if that constraint changes, this should be
70    /// explicitly updated to accommodate.)
71    pub stack: Rc<RuntimePropertiesStackFrame>,
72
73    /// Pointers to the ExpandedNode beneath this one.  Used for e.g. rendering recursion.
74    pub children: Property<Vec<Rc<ExpandedNode>>>,
75
76    /// A list of mounted children that need to be dismounted when children is recalculated
77    pub mounted_children: RefCell<Vec<Rc<ExpandedNode>>>,
78
79    /// Each ExpandedNode has a unique "stamp" of computed properties
80    pub properties: RefCell<Rc<RefCell<PaxAny>>>,
81
82    /// Each ExpandedNode has unique, computed `CommonProperties`
83    pub common_properties: RefCell<Rc<RefCell<CommonProperties>>>,
84    /// Set by chassis, for for example text nodes that get resize info from an interrupt
85    /// if a node doesn't have fixed bounds(width/height specified), this value is used instead.
86    pub rendered_size: Property<Option<(f64, f64)>>,
87
88    /// The layout information (width, height, transform) used to render this node.
89    /// computed property based on parent bounds + common properties
90    pub transform_and_bounds: Property<TransformAndBounds<NodeLocal, Window>>,
91
92    /// For component instances only, tracks the expanded slot_children in its
93    /// non-collapsed form (repeat and conditionals still present). This allows
94    /// repeat/conditionals to update their children (handled in component.rs
95    /// update_children method)
96    pub expanded_slot_children: RefCell<Option<Vec<Rc<ExpandedNode>>>>,
97    /// Flattened version of the above, where repeat/conditionals are removed
98    /// recursively and replaced by their children. This is re-computed each
99    /// frame from the non-collapsed expanded_slot_children after they have
100    /// been updated.
101    pub expanded_and_flattened_slot_children: Property<Vec<Rc<ExpandedNode>>>,
102    // Number of expanded and flattened slot children
103    pub flattened_slot_children_count: Property<usize>,
104
105    /// Flag that is > 0 if this node is part of the root tree. If it is,
106    /// updates to this nodes children also marks them as attached (+1), triggering
107    /// mount and dismount on addition/removal. This is needed mainly for slot,
108    /// since an entire "shadow tree" needs to be expanded and updated for
109    /// each slot child, but only the ones that have a "connected" slot should
110    /// trigger mount/dismount updates
111    pub attached: Cell<u32>,
112
113    /// Occlusion layer for this node. Used by canvas elements to decide what canvas to draw on, and
114    /// by native elements to move to the correct native layer.
115    // occlusionID (canvas/native layer) + z-index
116    pub occlusion: Property<Occlusion>,
117
118    /// A map of all properties available on this expanded node.
119    /// Used by the RuntimePropertiesStackFrame to resolve symbols.
120    pub properties_scope: RefCell<HashMap<String, Variable>>,
121
122    /// The flattened index of this node in its container (if this container
123    /// cares about slot children, ex: component, path).
124    pub slot_index: Property<Option<usize>>,
125
126    /// property used to "freeze" (stop firing tick) on a node and all it's children
127    pub suspended: Property<bool>,
128
129    /// used by native elements to trigger sending of native messages
130    pub native_message_listener: Property<()>,
131
132    /// used to know when a slot child is attached
133    pub slot_child_attached_listener: Property<()>,
134
135    /// subscription properties: added to this expanded node by calling ctx.subscribe in a node event handler
136    pub subscriptions: RefCell<Vec<Property<()>>>,
137}
138
139#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
140pub struct Occlusion {
141    pub occlusion_layer_id: usize,
142    pub z_index: i32,
143    // this is used to perform last patches logic,
144    // and is updated to reflect this nodes parent_frame
145    // when occlusion is calculated
146    pub parent_frame: Option<u32>,
147}
148
149impl Interpolatable for Occlusion {}
150
151impl ImplToFromPaxAny for ExpandedNode {}
152impl Interpolatable for ExpandedNode {}
153
154macro_rules! dispatch_event_handler {
155    ($fn_name:ident, $arg_type:ty, $handler_key:ident, $recurse:expr) => {
156        pub fn $fn_name(
157            self: &Rc<Self>,
158            event: Event<$arg_type>,
159            globals: &Globals,
160            ctx: &Rc<RuntimeContext>,
161        ) -> bool {
162            if let Some(registry) = borrow!(self.instance_node).base().get_handler_registry() {
163                let borrowed_registry = &borrow!(*registry);
164                if let Some(handlers) = borrowed_registry.handlers.get($handler_key) {
165                    if handlers.len() > 0 {
166                        let component_properties =
167                            if let Some(cc) = self.containing_component.upgrade() {
168                                Rc::clone(&*borrow!(cc.properties))
169                            } else {
170                                Rc::clone(&*borrow!(self.properties))
171                            };
172
173                        let context = self.get_node_context(ctx);
174                        handlers.iter().for_each(|handler| {
175                            let properties = if let HandlerLocation::Component = &handler.location {
176                                Rc::clone(&*borrow!(self.properties))
177                            } else {
178                                Rc::clone(&component_properties)
179                            };
180                            (handler.function)(
181                                Rc::clone(&properties),
182                                &context,
183                                Some(event.clone().to_pax_any()),
184                            );
185                        });
186                    }
187                };
188            }
189
190            if $recurse {
191                if let Some(parent) = self.template_parent.upgrade() {
192                    return parent.$fn_name(event, globals, ctx);
193                }
194            }
195            event.cancelled()
196        }
197    };
198}
199
200impl ExpandedNode {
201    pub fn initialize_root(template: Rc<ComponentInstance>, ctx: &Rc<RuntimeContext>) -> Rc<Self> {
202        let root_node = Self::new(
203            template,
204            ctx.globals().stack_frame(),
205            ctx,
206            Weak::new(),
207            Weak::new(),
208        );
209        root_node.bind_to_parent_bounds(ctx);
210        Rc::clone(&root_node).recurse_mount(ctx);
211        root_node
212    }
213
214    fn new(
215        template: Rc<dyn InstanceNode>,
216        env: Rc<RuntimePropertiesStackFrame>,
217        context: &Rc<RuntimeContext>,
218        containing_component: Weak<ExpandedNode>,
219        parent: Weak<ExpandedNode>,
220    ) -> Rc<Self> {
221        let properties =
222            (&template.base().instance_prototypical_properties_factory)(env.clone(), None).unwrap();
223
224        let common_properties =
225            (&template
226                .base()
227                .instance_prototypical_common_properties_factory)(env.clone(), None)
228            .unwrap();
229
230        let mut property_scope = borrow!(*common_properties).retrieve_property_scope();
231
232        if let Some(scope) = &template.base().properties_scope_factory {
233            property_scope.extend(scope(properties.clone()));
234        }
235
236        let id = context.gen_uid();
237        let res = Rc::new(ExpandedNode {
238            id,
239            stack: env,
240            instance_node: RefCell::new(Rc::clone(&template)),
241            attached: Cell::new(0),
242            properties: RefCell::new(properties),
243            common_properties: RefCell::new(common_properties),
244            rendered_size: Property::default(),
245
246            // these two refer to their rendering parent, not their
247            // template parent
248            render_parent: Default::default(),
249            parent_frame: Default::default(),
250            template_parent: parent,
251
252            containing_component,
253            children: Property::new_with_name(
254                Vec::new(),
255                &format!("node children (node id: {})", id.0),
256            ),
257            mounted_children: RefCell::new(Vec::new()),
258            transform_and_bounds: Property::new(TransformAndBounds::default()),
259            expanded_slot_children: Default::default(),
260            expanded_and_flattened_slot_children: Default::default(),
261            flattened_slot_children_count: Property::new(0),
262            occlusion: Property::new(Occlusion::default()),
263            properties_scope: RefCell::new(property_scope),
264            slot_index: Property::default(),
265            suspended: Property::new(false),
266            native_message_listener: Property::default(),
267            slot_child_attached_listener: Property::default(),
268            subscriptions: Default::default(),
269        });
270        res
271    }
272
273    pub fn recreate_with_new_data(
274        self: &Rc<Self>,
275        template: Rc<dyn InstanceNode>,
276        context: &Rc<RuntimeContext>,
277    ) {
278        *borrow_mut!(self.instance_node) = Rc::clone(&template);
279        (&template
280            .base()
281            .instance_prototypical_common_properties_factory)(
282            Rc::clone(&self.stack),
283            Some(Rc::clone(&self)),
284        );
285        (&template.base().instance_prototypical_properties_factory)(
286            Rc::clone(&self.stack),
287            Some(Rc::clone(&self)),
288        );
289        self.bind_to_parent_bounds(context);
290        context.set_canvas_dirty(self.occlusion.get().occlusion_layer_id);
291    }
292
293    pub fn fully_recreate_with_new_data(
294        self: &Rc<Self>,
295        template: Rc<dyn InstanceNode>,
296        context: &Rc<RuntimeContext>,
297    ) {
298        Rc::clone(self).recurse_unmount(context);
299        let new_expanded_node = Self::new(
300            template.clone(),
301            Rc::clone(&self.stack),
302            context,
303            Weak::clone(&self.containing_component),
304            Weak::clone(&self.template_parent),
305        );
306        *borrow_mut!(self.instance_node) = Rc::clone(&*borrow!(new_expanded_node.instance_node));
307        *borrow_mut!(self.properties) = Rc::clone(&*borrow!(new_expanded_node.properties));
308        *borrow_mut!(self.properties_scope) = borrow!(new_expanded_node.properties_scope).clone();
309        *borrow_mut!(self.common_properties) =
310            Rc::clone(&*borrow!(new_expanded_node.common_properties));
311        self.occlusion.set(Default::default());
312
313        self.bind_to_parent_bounds(context);
314        Rc::clone(self).recurse_mount(context);
315        Rc::clone(self).recurse_update(context);
316    }
317
318    /// Returns whether this node is a descendant of the ExpandedNode described by `other_expanded_node_id` (id)
319    /// Currently requires traversing linked list of ancestry, incurring a O(log(n)) cost for a tree of `n` elements.
320    /// This could be mitigated with caching/memoization, perhaps by storing a HashSet on each ExpandedNode describing its ancestry chain.
321    pub fn is_descendant_of(&self, other_expanded_node_id: &ExpandedNodeIdentifier) -> bool {
322        if let Some(parent) = borrow!(self.render_parent).upgrade() {
323            // We have a parent — if it matches the ID, this node is indeed an ancestor of other_expanded_node_id.  Otherwise, recurse upward.
324            if parent.id.eq(other_expanded_node_id) {
325                true
326            } else {
327                parent.is_descendant_of(other_expanded_node_id)
328            }
329        } else {
330            false
331        }
332    }
333
334    pub fn create_children_detached(
335        self: &Rc<Self>,
336        templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
337        context: &Rc<RuntimeContext>,
338        template_parent: &Weak<ExpandedNode>,
339    ) -> Vec<Rc<ExpandedNode>> {
340        let containing_component = if borrow!(self.instance_node).base().flags().is_component {
341            Rc::downgrade(&self)
342        } else {
343            Weak::clone(&self.containing_component)
344        };
345
346        let mut children = Vec::new();
347
348        for (template, env) in templates {
349            children.push(Self::new(
350                template,
351                env,
352                context,
353                Weak::clone(&containing_component),
354                Weak::clone(&template_parent),
355            ));
356        }
357        children
358    }
359
360    pub fn attach_children(
361        self: &Rc<Self>,
362        new_children: Vec<Rc<ExpandedNode>>,
363        context: &Rc<RuntimeContext>,
364        parent_frame: &Property<Option<ExpandedNodeIdentifier>>,
365    ) -> Vec<Rc<ExpandedNode>> {
366        let mut curr_children = borrow_mut!(self.mounted_children);
367        //TODO here we could probably check intersection between old and new children (to avoid unmount + mount)
368
369        for child in new_children.iter() {
370            // set parent and connect up viewport bounds to new parent
371            *borrow_mut!(child.render_parent) = Rc::downgrade(self);
372            // set frame clipping reference
373            let parent_frame = parent_frame.clone();
374            let deps = [parent_frame.untyped()];
375            child
376                .parent_frame
377                .replace_with(Property::computed(move || parent_frame.get(), &deps));
378
379            // suspension is used in the designer to turn of/on tick/update
380            child.inherit_suspend(self);
381            child.bind_to_parent_bounds(context);
382        }
383        if self.attached.get() > 0 {
384            for child in curr_children.iter() {
385                Rc::clone(child).recurse_unmount(context);
386            }
387            for child in new_children.iter() {
388                Rc::clone(child).recurse_mount(context);
389            }
390        }
391        *curr_children = new_children.clone();
392        new_children
393    }
394
395    fn bind_to_parent_bounds(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) {
396        let parent_transform_and_bounds = borrow!(self.render_parent)
397            .upgrade()
398            .map(|n| n.transform_and_bounds.clone())
399            .unwrap_or_else(|| ctx.globals().viewport);
400        let common_props = borrow!(self.common_properties);
401        let extra_transform = borrow!(common_props).transform.clone();
402
403        let transform_and_bounds = compute_tab(
404            self.layout_properties(),
405            extra_transform,
406            parent_transform_and_bounds,
407        );
408        self.transform_and_bounds.replace_with(transform_and_bounds);
409    }
410
411    pub fn inherit_suspend(self: &Rc<Self>, node: &Rc<Self>) {
412        let cp = self.get_common_properties();
413        let self_suspended = borrow!(cp)._suspended.clone();
414        let parent_suspended = node.suspended.clone();
415        let deps = [parent_suspended.untyped(), self_suspended.untyped()];
416        self.suspended.replace_with(Property::computed(
417            move || {
418                self_suspended
419                    .get()
420                    .unwrap_or_else(|| parent_suspended.get())
421            },
422            &deps,
423        ));
424    }
425
426    pub fn generate_children(
427        self: &Rc<Self>,
428        templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
429        context: &Rc<RuntimeContext>,
430        parent_frame: &Property<Option<ExpandedNodeIdentifier>>,
431        is_mount: bool,
432    ) -> Vec<Rc<ExpandedNode>> {
433        let new_children = self.create_children_detached(templates, context, &Rc::downgrade(&self));
434        let res = if is_mount {
435            self.attach_children(new_children, context, parent_frame)
436        } else {
437            for child in new_children.iter() {
438                child.recurse_control_flow_expansion(context);
439            }
440
441            new_children
442        };
443        res
444    }
445
446    /// This method recursively updates all node properties. When dirty-dag exists, this won't
447    /// need to be here since all property dependencies can be set up and removed during mount/unmount
448    pub fn recurse_update(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
449        if let Some(ref registry) = borrow!(self.instance_node).base().handler_registry {
450            //if !self.suspended.get() {
451            for handler in borrow!(registry)
452                .handlers
453                .get("tick")
454                .unwrap_or(&Vec::new())
455            {
456                (handler.function)(
457                    Rc::clone(&*borrow!(self.properties)),
458                    &self.get_node_context(context),
459                    None,
460                )
461                // }
462            }
463        }
464        Rc::clone(&*borrow!(self.instance_node)).update(&self, context);
465        // trigger native message sending
466        self.native_message_listener.get();
467
468        if let Some(ref registry) = borrow!(self.instance_node).base().handler_registry {
469            if !self.suspended.get() {
470                for handler in borrow!(registry)
471                    .handlers
472                    .get("pre_render")
473                    .unwrap_or(&Vec::new())
474                {
475                    (handler.function)(
476                        Rc::clone(&*borrow!(self.properties)),
477                        &self.get_node_context(context),
478                        None,
479                    )
480                }
481            }
482        }
483        for subscription in &*borrow!(self.subscriptions) {
484            // fire dirty bit if present
485            subscription.get();
486        }
487        if borrow!(self.instance_node).base().flags().is_component {
488            self.compute_flattened_slot_children();
489        }
490        for child in self.children.get().iter() {
491            child.recurse_update(context);
492        }
493    }
494
495    pub fn recurse_control_flow_expansion(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
496        borrow!(self.instance_node)
497            .clone()
498            .handle_control_flow_node_expansion(&self, context);
499    }
500
501    pub fn recurse_mount(self: &Rc<Self>, context: &Rc<RuntimeContext>) {
502        if self.attached.get() == 0 {
503            // create slot children
504            borrow!(self.instance_node)
505                .clone()
506                .handle_setup_slot_children(&self, context);
507
508            // a pre-mounting pass to make sure all slot children are expanded and we compute the correct flattened slot children
509            if let Some(slot_children) = borrow!(self.expanded_slot_children).as_ref() {
510                for slot_child in slot_children {
511                    slot_child.recurse_control_flow_expansion(context);
512                }
513                // this is needed to reslove slot connections in a single tick
514                self.compute_flattened_slot_children();
515            }
516
517            self.attached.set(self.attached.get() + 1);
518            context.add_to_cache(&self);
519            if let Some(ref registry) = borrow!(self.instance_node).base().handler_registry {
520                for handler in borrow!(registry)
521                    .handlers
522                    .get("mount")
523                    .unwrap_or(&Vec::new())
524                {
525                    (handler.function)(
526                        Rc::clone(&*borrow!(self.properties)),
527                        &self.get_node_context(context),
528                        None,
529                    )
530                }
531            }
532            borrow!(self.instance_node)
533                .clone()
534                .handle_mount(&self, context);
535        }
536    }
537
538    pub fn recurse_unmount(self: Rc<Self>, context: &Rc<RuntimeContext>) {
539        // WARNING: do NOT make recurse_unmount result in expr evaluation,
540        // in this case: do not refer to self.children expression.
541        // expr evaluation in this context can trigger get's of "old data", ie try to get
542        // an index of a for loop source that doesn't exist anymore
543        if self.attached.get() == 1 {
544            self.attached.set(self.attached.get() - 1);
545            context.remove_from_cache(&self);
546            for child in borrow!(self.mounted_children).iter() {
547                Rc::clone(child).recurse_unmount(context);
548            }
549            borrow!(self.instance_node).handle_unmount(&self, context);
550            if let Some(ref registry) = borrow!(self.instance_node).base().handler_registry {
551                for handler in borrow!(registry)
552                    .handlers
553                    .get("unmount")
554                    .unwrap_or(&Vec::new())
555                {
556                    (handler.function)(
557                        Rc::clone(&*borrow!(self.properties)),
558                        &self.get_node_context(context),
559                        None,
560                    )
561                }
562            }
563
564            if self.instance_node.borrow().base().flags().layer == Layer::Canvas {
565                context.set_canvas_dirty(self.occlusion.get().occlusion_layer_id);
566            }
567
568            // Needed because occlusion updates are only sent on diffs so we reset it when unmounting
569            self.occlusion.set(Default::default());
570        }
571    }
572
573    pub fn recurse_render_queue(
574        self: &Rc<Self>,
575        ctx: &Rc<RuntimeContext>,
576        rcs: &mut dyn RenderContext,
577    ) {
578        let cp = self.get_common_properties();
579        let cp = borrow!(cp);
580        if cp.unclippable.get().unwrap_or(false) {
581            ctx.queue_render(Rc::clone(&self));
582        } else {
583            self.recurse_render(ctx, rcs);
584        }
585    }
586
587    pub fn recurse_render(self: &Rc<Self>, ctx: &Rc<RuntimeContext>, rcs: &mut dyn RenderContext) {
588        borrow!(self.instance_node).handle_pre_render(&self, ctx, rcs);
589        for child in self.children.get().iter().rev() {
590            child.recurse_render_queue(ctx, rcs);
591        }
592        borrow!(self.instance_node).render(&self, ctx, rcs);
593        borrow!(self.instance_node).handle_post_render(&self, ctx, rcs);
594    }
595
596    /// Manages unpacking an Rc<RefCell<PaxValue>>, downcasting into
597    /// the parameterized `target_type`, and executing a provided closure `body` in the
598    /// context of that unwrapped variant (including support for mutable operations),
599    /// the closure is executed.  Used at least by calculating properties in `expand_node` and
600    /// passing `&mut self` into event handlers (where the typed `self` is retrieved from an instance of `PaxValue`)
601    pub fn with_properties_unwrapped<T: ToFromPaxAny, R>(
602        &self,
603        callback: impl FnOnce(&mut T) -> R,
604    ) -> R {
605        self.try_with_properties_unwrapped(callback)
606            .expect("properties not of expected type")
607    }
608
609    pub fn try_with_properties_unwrapped<T: ToFromPaxAny, R>(
610        &self,
611        callback: impl FnOnce(&mut T) -> R,
612    ) -> Option<R> {
613        // Borrow the contents of the RefCell mutably.
614        let properties = borrow_mut!(self.properties);
615        let mut borrowed = borrow_mut!(properties);
616        // Downcast the unwrapped value to the specified `target_type` (or panic)
617        let Ok(mut val) = T::mut_from_pax_any(&mut *borrowed) else {
618            return None;
619        };
620        Some(callback(&mut val))
621    }
622
623    pub fn recurse_visit_postorder(self: &Rc<Self>, func: &mut impl FnMut(&Rc<Self>)) {
624        // NOTE: This is to make sure that the slot children list is updated before trying to access children,
625        // to make stacker/scroller behave correctly when number of children is dynamic. (ex: tree view in designer)
626        self.compute_flattened_slot_children();
627        for child in self.children.get().iter().rev() {
628            child.recurse_visit_postorder(func)
629        }
630        func(self);
631    }
632
633    pub fn get_node_context(self: &Rc<Self>, ctx: &Rc<RuntimeContext>) -> NodeContext {
634        let globals = ctx.globals();
635        let t_and_b = self.transform_and_bounds.clone();
636        let deps = [t_and_b.untyped()];
637        let bounds_self = Property::computed(move || t_and_b.get().bounds, &deps);
638        let t_and_b_parent = if let Some(parent) = borrow!(self.render_parent).upgrade() {
639            parent.transform_and_bounds.clone()
640        } else {
641            globals.viewport.clone()
642        };
643        let deps = [t_and_b_parent.untyped()];
644        let bounds_parent = Property::computed(move || t_and_b_parent.get().bounds, &deps);
645
646        let slot_children_count = if borrow!(self.instance_node).base().flags().is_component {
647            self.flattened_slot_children_count.clone()
648        } else {
649            self.containing_component
650                .upgrade()
651                .map(|v| v.flattened_slot_children_count.clone())
652                .unwrap_or_default()
653        };
654
655        let slot_children = if borrow!(self.instance_node).base().flags().is_component {
656            self.expanded_and_flattened_slot_children.clone()
657        } else {
658            self.containing_component
659                .upgrade()
660                .map(|v| v.expanded_and_flattened_slot_children.clone())
661                .unwrap_or_default()
662        };
663
664        let slot_children_attached_listener =
665            if borrow!(self.instance_node).base().flags().is_component {
666                self.slot_child_attached_listener.clone()
667            } else {
668                self.containing_component
669                    .upgrade()
670                    .map(|v| v.slot_child_attached_listener.clone())
671                    .unwrap_or_default()
672            };
673
674        let last_frame = Rc::new(RefCell::new(globals.frames_elapsed.get()));
675        let suspended = self.suspended.clone();
676        let frames_elapsed = globals.frames_elapsed.clone();
677        let deps = [frames_elapsed.untyped(), suspended.untyped()];
678        // TODO: this still triggers the dirty dag dependencies of elapsed
679        // frames even if the value is the same. Try to make it not trigger
680        // dependencides when frozen
681        let frames_elapsed_frozen_if_suspended = Property::computed(
682            move || {
683                if suspended.get() {
684                    *borrow!(last_frame)
685                } else {
686                    let val = frames_elapsed.get();
687                    *borrow_mut!(last_frame) = val;
688                    val
689                }
690            },
691            &deps,
692        );
693        // PREF: possibly change this to just take a reference to expanded node, and
694        // expose methods to retrieve these values from the expanded noe when
695        // needed, instead of re-creating/copying
696        NodeContext {
697            slot_index: self.slot_index.clone(),
698            local_stack_frame: Rc::clone(&self.stack),
699            expanded_node: Rc::downgrade(&self),
700            containing_component: Weak::clone(&self.containing_component),
701            frames_elapsed: frames_elapsed_frozen_if_suspended,
702            bounds_self,
703            bounds_parent,
704            runtime_context: ctx.clone(),
705            platform: globals.platform.clone(),
706            os: globals.os.clone(),
707            get_elapsed_millis: globals.get_elapsed_millis,
708            slot_children_count,
709            slot_children,
710            node_transform_and_bounds: self.transform_and_bounds.get(),
711            slot_children_attached_listener,
712            #[cfg(feature = "designtime")]
713            designtime: globals.designtime.clone(),
714        }
715    }
716
717    pub fn get_common_properties(&self) -> Rc<RefCell<CommonProperties>> {
718        Rc::clone(&*borrow!(self.common_properties))
719    }
720
721    /// Determines whether the provided ray, orthogonal to the view plane,
722    /// intersects this `ExpandedNode`.
723    pub fn ray_cast_test(&self, ray: Point2<Window>) -> bool {
724        let cp = borrow!(self.common_properties);
725
726        // skip raycast if false
727        if !borrow!(&*cp)._raycastable.get().unwrap_or(true) {
728            return false;
729        }
730        let t_and_b = self.transform_and_bounds.get();
731
732        let inverted_transform = t_and_b.transform.inverse();
733        let transformed_ray = inverted_transform * ray;
734        let (width, height) = t_and_b.bounds;
735        //Default implementation: rectilinear bounding hull
736        let res = transformed_ray.x > 0.0
737            && transformed_ray.y > 0.0
738            && transformed_ray.x < width
739            && transformed_ray.y < height;
740        res
741    }
742
743    pub fn compute_flattened_slot_children(&self) {
744        // All of this should ideally be reactively updated,
745        // but currently doesn't exist a way to "listen to"
746        // an entire node tree, and generate the flattened list
747        // only when changed.
748        if let Some(slot_children) = borrow!(self.expanded_slot_children).as_ref() {
749            let new_flattened = flatten_expanded_nodes_for_slot(&slot_children);
750            let old_and_new_filtered_same =
751                self.expanded_and_flattened_slot_children.read(|flattened| {
752                    flattened
753                        .iter()
754                        .map(|n| n.id)
755                        .eq(new_flattened.iter().map(|n| n.id))
756                });
757
758            if !old_and_new_filtered_same {
759                self.flattened_slot_children_count.set(new_flattened.len());
760                self.expanded_and_flattened_slot_children.set(new_flattened);
761                for (i, slot_child) in self
762                    .expanded_and_flattened_slot_children
763                    .get()
764                    .iter()
765                    .enumerate()
766                {
767                    if slot_child.slot_index.get() != Some(i) {
768                        slot_child.slot_index.set(Some(i));
769                    };
770                }
771            }
772        }
773    }
774
775    dispatch_event_handler!(dispatch_scroll, Scroll, SCROLL_HANDLERS, true);
776    dispatch_event_handler!(dispatch_clap, Clap, CLAP_HANDLERS, true);
777    dispatch_event_handler!(dispatch_touch_start, TouchStart, TOUCH_START_HANDLERS, true);
778
779    dispatch_event_handler!(dispatch_touch_move, TouchMove, TOUCH_MOVE_HANDLERS, true);
780    dispatch_event_handler!(dispatch_touch_end, TouchEnd, TOUCH_END_HANDLERS, true);
781    dispatch_event_handler!(dispatch_key_down, KeyDown, KEY_DOWN_HANDLERS, false);
782    dispatch_event_handler!(dispatch_key_up, KeyUp, KEY_UP_HANDLERS, false);
783    dispatch_event_handler!(dispatch_key_press, KeyPress, KEY_PRESS_HANDLERS, false);
784    dispatch_event_handler!(
785        dispatch_checkbox_change,
786        CheckboxChange,
787        CHECKBOX_CHANGE_HANDLERS,
788        true
789    );
790    dispatch_event_handler!(
791        dispatch_textbox_change,
792        TextboxChange,
793        TEXTBOX_CHANGE_HANDLERS,
794        true
795    );
796    dispatch_event_handler!(dispatch_text_input, TextInput, TEXT_INPUT_HANDLERS, true);
797    dispatch_event_handler!(
798        dispatch_textbox_input,
799        TextboxInput,
800        TEXTBOX_INPUT_HANDLERS,
801        true
802    );
803    dispatch_event_handler!(
804        dispatch_button_click,
805        ButtonClick,
806        BUTTON_CLICK_HANDLERS,
807        true
808    );
809    dispatch_event_handler!(dispatch_mouse_down, MouseDown, MOUSE_DOWN_HANDLERS, true);
810    dispatch_event_handler!(dispatch_mouse_up, MouseUp, MOUSE_UP_HANDLERS, true);
811    dispatch_event_handler!(dispatch_mouse_move, MouseMove, MOUSE_MOVE_HANDLERS, true);
812    dispatch_event_handler!(dispatch_mouse_over, MouseOver, MOUSE_OVER_HANDLERS, false);
813    dispatch_event_handler!(dispatch_mouse_out, MouseOut, MOUSE_OUT_HANDLERS, false);
814    dispatch_event_handler!(
815        dispatch_double_click,
816        DoubleClick,
817        DOUBLE_CLICK_HANDLERS,
818        true
819    );
820    dispatch_event_handler!(
821        dispatch_context_menu,
822        ContextMenu,
823        CONTEXT_MENU_HANDLERS,
824        true
825    );
826    dispatch_event_handler!(dispatch_click, Click, CLICK_HANDLERS, true);
827    dispatch_event_handler!(dispatch_wheel, Wheel, WHEEL_HANDLERS, true);
828    dispatch_event_handler!(dispatch_drop, Drop, DROP_HANDLERS, true);
829    dispatch_event_handler!(dispatch_focus, Focus, FOCUSED_HANDLERS, false);
830    dispatch_event_handler!(
831        dispatch_select_start,
832        SelectStart,
833        SELECT_START_HANDLERS,
834        false
835    );
836
837    pub fn dispatch_custom_event(
838        self: &Rc<Self>,
839        identifier: &str,
840        ctx: &Rc<RuntimeContext>,
841    ) -> Result<(), String> {
842        let component_origin_instance = borrow!(self.instance_node);
843        let registry = component_origin_instance
844            .base()
845            .handler_registry
846            .as_ref()
847            .ok_or_else(|| "no registry present".to_owned())?;
848
849        let parent_component = self
850            .containing_component
851            .upgrade()
852            .ok_or_else(|| "can't dispatch from root (has no parent)".to_owned())?;
853        let properties = borrow!(parent_component.properties);
854
855        for handler in borrow!(registry)
856            .handlers
857            .get(identifier)
858            .expect("presence should have been checked when added to custom_event_queue")
859        {
860            (handler.function)(Rc::clone(&*properties), &self.get_node_context(ctx), None)
861        }
862        Ok(())
863    }
864
865    // fired if the chassis thinks this element should be a different size (in pixels).
866    // usually, this is something the engine has asked for, ie. text nodes
867    // changing size.
868    pub fn chassis_resize_request(self: &Rc<ExpandedNode>, width: f64, height: f64) {
869        self.rendered_size.set(Some((width, height)));
870    }
871
872    /// Helper method that returns a collection of common properties
873    /// related to layout (position, size, scale, anchor, etc),
874    pub fn layout_properties(self: &Rc<ExpandedNode>) -> Property<LayoutProperties> {
875        let common_props = self.get_common_properties();
876        let common_props = borrow!(common_props);
877        let cp_width = common_props.width.clone();
878        let cp_height = common_props.height.clone();
879        let cp_transform = common_props.transform.clone();
880        let cp_anchor_x = common_props.anchor_x.clone();
881        let cp_anchor_y = common_props.anchor_y.clone();
882        let cp_scale_x = common_props.scale_x.clone();
883        let cp_scale_y = common_props.scale_y.clone();
884        let cp_skew_x = common_props.skew_x.clone();
885        let cp_skew_y = common_props.skew_y.clone();
886        let cp_rotate = common_props.rotate.clone();
887        let cp_x = common_props.x.clone();
888        let cp_y = common_props.y.clone();
889        let rendered_size = self.rendered_size.clone();
890        let deps = [
891            cp_width.untyped(),
892            cp_height.untyped(),
893            cp_transform.untyped(),
894            cp_anchor_x.untyped(),
895            cp_anchor_y.untyped(),
896            cp_scale_x.untyped(),
897            cp_scale_y.untyped(),
898            cp_skew_x.untyped(),
899            cp_skew_y.untyped(),
900            cp_rotate.untyped(),
901            cp_x.untyped(),
902            cp_y.untyped(),
903            rendered_size.untyped(),
904        ];
905
906        Property::computed(
907            move || {
908                // Used for auto sized text, might be used for other things later
909                let fallback = rendered_size.get();
910                let (w_fallback, h_fallback) = match fallback {
911                    Some((wf, hf)) => (Some(wf), Some(hf)),
912                    None => (None, None),
913                };
914
915                LayoutProperties {
916                    x: cp_x.get(),
917                    y: cp_y.get(),
918                    width: cp_width
919                        .get()
920                        .or(w_fallback.map(|v| Size::Pixels(v.into()))),
921                    height: cp_height
922                        .get()
923                        .or(h_fallback.map(|v| Size::Pixels(v.into()))),
924                    rotate: cp_rotate.get(),
925                    // TODO make the common prop only accept percent
926                    scale_x: cp_scale_x
927                        .get()
928                        .map(|v| Percent((100.0 * v.expect_percent()).into())),
929                    scale_y: cp_scale_y
930                        .get()
931                        .map(|v| Percent((100.0 * v.expect_percent()).into())),
932                    anchor_x: cp_anchor_x.get(),
933                    anchor_y: cp_anchor_y.get(),
934                    skew_x: cp_skew_x.get(),
935                    skew_y: cp_skew_y.get(),
936                }
937            },
938            &deps,
939        )
940    }
941}
942
943/// Given some InstanceNodePtrList, distill away all "slot-invisible" nodes (namely, `if` and `for`)
944/// and return another InstanceNodePtrList with a flattened top-level list of nodes.
945fn flatten_expanded_nodes_for_slot(nodes: &[Rc<ExpandedNode>]) -> Vec<Rc<ExpandedNode>> {
946    let mut result: Vec<Rc<ExpandedNode>> = vec![];
947    for node in nodes {
948        if borrow!(node.instance_node).base().flags().invisible_to_slot {
949            result.extend(flatten_expanded_nodes_for_slot(
950                node.children
951                    .get()
952                    .clone()
953                    .into_iter()
954                    .collect::<Vec<_>>()
955                    .as_slice(),
956            ));
957        } else {
958            result.push(Rc::clone(&node))
959        }
960    }
961    result
962}
963
964impl std::fmt::Debug for ExpandedNode {
965    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
966        //see: https://users.rust-lang.org/t/reusing-an-fmt-formatter/8531/4
967        //maybe this utility should be moved to a more accessible place?
968        pub struct Fmt<F>(pub F)
969        where
970            F: Fn(&mut fmt::Formatter) -> fmt::Result;
971
972        impl<F> fmt::Debug for Fmt<F>
973        where
974            F: Fn(&mut fmt::Formatter) -> fmt::Result,
975        {
976            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
977                (self.0)(f)
978            }
979        }
980
981        f.debug_struct("ExpandedNode")
982            .field(
983                "instance_node",
984                &Fmt(|f| borrow!(self.instance_node).resolve_debug(f, Some(self))),
985            )
986            .field("id", &self.id)
987            .field("common_properties", &borrow!(self.common_properties))
988            .field("transform_and_bounds", &self.transform_and_bounds)
989            .field("children", &self.children.get().iter().collect::<Vec<_>>())
990            .field(
991                "slot_children",
992                &self
993                    .expanded_and_flattened_slot_children
994                    .get()
995                    .iter()
996                    .map(|v| v.id)
997                    .collect::<Vec<_>>(),
998            )
999            .field("occlusion_id", &self.occlusion.get())
1000            .field(
1001                "containing_component",
1002                &self.containing_component.upgrade().map(|v| v.id.clone()),
1003            )
1004            .finish()
1005    }
1006}