pax_core/engine/
expanded_node.rs

1#[cfg(feature = "designtime")]
2use crate::constants::{
3    BUTTON_CLICK_HANDLERS, CHECKBOX_CHANGE_HANDLERS, CLAP_HANDLERS, CLICK_HANDLERS,
4    CONTEXT_MENU_HANDLERS, DOUBLE_CLICK_HANDLERS, KEY_DOWN_HANDLERS, KEY_PRESS_HANDLERS,
5    KEY_UP_HANDLERS, MOUSE_DOWN_HANDLERS, MOUSE_MOVE_HANDLERS, MOUSE_OUT_HANDLERS,
6    MOUSE_OVER_HANDLERS, MOUSE_UP_HANDLERS, SCROLL_HANDLERS, TEXTBOX_CHANGE_HANDLERS,
7    TOUCH_END_HANDLERS, TOUCH_MOVE_HANDLERS, TOUCH_START_HANDLERS, WHEEL_HANDLERS,
8};
9use crate::Globals;
10use core::fmt;
11use std::any::Any;
12use std::cell::RefCell;
13use std::rc::{Rc, Weak};
14
15use kurbo::Point;
16
17use pax_runtime_api::{
18    ArgsButtonClick, ArgsCheckboxChange, ArgsClap, ArgsClick, ArgsContextMenu, ArgsDoubleClick,
19    ArgsKeyDown, ArgsKeyPress, ArgsKeyUp, ArgsMouseDown, ArgsMouseMove, ArgsMouseOut,
20    ArgsMouseOver, ArgsMouseUp, ArgsScroll, ArgsTextboxChange, ArgsTouchEnd, ArgsTouchMove,
21    ArgsTouchStart, ArgsWheel, Axis, CommonProperties, NodeContext, RenderContext, Size,
22};
23
24use crate::{
25    compute_tab, ComponentInstance, InstanceNode, InstanceNodePtr, PropertiesComputable,
26    RuntimeContext, RuntimePropertiesStackFrame, TransformAndBounds,
27};
28
29pub struct ExpandedNode {
30    #[allow(dead_code)]
31    /// Unique ID of this expanded node, roughly encoding an address in the tree, where the first u32 is the instance ID
32    /// and the subsequent u32s represent addresses within an expanded tree via Repeat.
33    pub id_chain: Vec<u32>,
34
35    /// Pointer to the unexpanded `instance_node` underlying this ExpandedNode
36    pub instance_node: InstanceNodePtr,
37
38    /// Pointer (`Weak` to avoid Rc cycle memory leaks) to the ExpandedNode directly above
39    /// this one.  Used for e.g. event propagation.
40    pub parent_expanded_node: RefCell<Weak<ExpandedNode>>,
41
42    /// Reference to the _component for which this `ExpandedNode` is a template member._  Used at least for
43    /// getting a reference to slot_children for `slot`.  `Option`al because the very root instance node (root component, root instance node)
44    /// has a corollary "root component expanded node."  That very root expanded node _does not have_ a containing ExpandedNode component,
45    /// thus `containing_component` is `Option`al.
46    pub containing_component: Weak<ExpandedNode>,
47
48    /// 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
49    /// through the lifetime of the program after the initial expansion; however, if that constraint changes, this should be
50    /// explicitly updated to accommodate.)
51    pub stack: Rc<RuntimePropertiesStackFrame>,
52
53    /// Pointers to the ExpandedNode beneath this one.  Used for e.g. rendering recursion.
54    children: RefCell<Vec<Rc<ExpandedNode>>>,
55
56    /// Each ExpandedNode has a unique "stamp" of computed properties
57    pub properties: Rc<RefCell<dyn Any>>,
58
59    /// Each ExpandedNode has unique, computed `CommonProperties`
60    common_properties: Rc<RefCell<CommonProperties>>,
61
62    /// Properties that are currently re-computed each frame before rendering.
63    /// Only contains computed_tab atm. Might be possible to retire if tab comp
64    /// would be part of render pass?
65    pub layout_properties: RefCell<Option<LayoutProperties>>,
66
67    /// For component instances only, tracks the expanded slot_children in it's
68    /// non-collapsed form (repeat and conditionals still present). This allows
69    /// repeat/conditionals to update their children (handled in component.rs
70    /// update_children method)
71    pub expanded_slot_children: RefCell<Option<Vec<Rc<ExpandedNode>>>>,
72    /// Flattened version of the above, where repeat/conditionals are removed
73    /// recursively and replaced by their children. This is re-computed each
74    /// frame from the non-collapsed expanded_slot_children after they have
75    /// been updated.
76    pub expanded_and_flattened_slot_children: RefCell<Option<Vec<Rc<ExpandedNode>>>>,
77
78    /// Flag that is > 0 if this node is part of the root tree. If it is,
79    /// updates to this nodes children also marks them as attached (+1), triggering
80    /// mount and dismount on addition/removal. This is needed mainly for slot,
81    /// since an entire "shadow tree" needs to be expanded and updated for
82    /// each slot child, but only the ones that have a "connected" slot should
83    /// trigger mount/dismount updates
84    pub attached: RefCell<u32>,
85
86    /// Occlusion layer for this node. Used by canvas elements to decide what canvas to draw on, and
87    /// by native elements to move to the correct native layer.
88    pub occlusion_id: RefCell<u32>,
89}
90
91macro_rules! dispatch_event_handler {
92    ($fn_name:ident, $arg_type:ty, $handler_field:ident, $handler_key:ident) => {
93        pub fn $fn_name(&self, args: $arg_type, globals: &Globals) {
94            if let Some(registry) = self.instance_node.base().get_handler_registry() {
95                let component_properties = if let Some(cc) = self.containing_component.upgrade() {
96                    Rc::clone(&cc.properties)
97                } else {
98                    Rc::clone(&self.properties)
99                };
100
101                let comp_props = self.layout_properties.borrow();
102                let bounds_self = comp_props.as_ref().unwrap().computed_tab.bounds;
103                let bounds_parent = self
104                    .parent_expanded_node
105                    .borrow()
106                    .upgrade()
107                    .map(|parent| {
108                        let comp_props = parent.layout_properties.borrow();
109                        let bounds_parent = comp_props.as_ref().unwrap().computed_tab.bounds;
110                        bounds_parent
111                    })
112                    .unwrap_or(globals.viewport.bounds);
113                let context = NodeContext {
114                    bounds_self,
115                    bounds_parent,
116                    frames_elapsed: globals.frames_elapsed,
117                    #[cfg(feature = "designtime")]
118                    designtime: globals.designtime.clone(),
119                };
120
121                let borrowed_registry = &(*registry).borrow();
122
123                #[cfg(not(feature = "designtime"))]
124                {
125                    let handlers = &borrowed_registry.$handler_field;
126                    handlers.iter().for_each(|handler| {
127                        handler(Rc::clone(&component_properties), &context, args.clone());
128                    });
129                }
130                #[cfg(feature = "designtime")]
131                {
132                    if let Some(handlers) = borrowed_registry.handlers.get($handler_key) {
133                        handlers.iter().for_each(|handler| {
134                            handler(
135                                Rc::clone(&component_properties),
136                                &context,
137                                Some(Box::new(args.clone()) as Box<dyn Any>),
138                            );
139                        });
140                    };
141                }
142            }
143
144            if let Some(parent) = &self.parent_expanded_node.borrow().upgrade() {
145                parent.$fn_name(args, globals);
146            }
147        }
148    };
149}
150
151impl ExpandedNode {
152    pub fn root(template: Rc<ComponentInstance>, context: &mut RuntimeContext) -> Rc<Self> {
153        let root_env =
154            RuntimePropertiesStackFrame::new(Rc::new(RefCell::new(())) as Rc<RefCell<dyn Any>>);
155        let root_node = Self::new(template, root_env, context, Weak::new());
156        Rc::clone(&root_node).recurse_mount(context);
157        root_node
158    }
159
160    fn new(
161        template: Rc<dyn InstanceNode>,
162        env: Rc<RuntimePropertiesStackFrame>,
163        context: &mut RuntimeContext,
164        containing_component: Weak<ExpandedNode>,
165    ) -> Rc<Self> {
166        let properties = (&template.base().instance_prototypical_properties_factory)();
167        let common_properties = (&template
168            .base()
169            .instance_prototypical_common_properties_factory)();
170
171        Rc::new(ExpandedNode {
172            id_chain: vec![context.gen_uid().0],
173            instance_node: Rc::clone(&template),
174            attached: RefCell::new(0),
175            properties,
176            common_properties,
177            stack: env,
178            parent_expanded_node: Default::default(),
179            containing_component,
180
181            children: RefCell::new(Vec::new()),
182            layout_properties: RefCell::new(None),
183            expanded_slot_children: Default::default(),
184            expanded_and_flattened_slot_children: Default::default(),
185            occlusion_id: RefCell::new(0),
186        })
187    }
188
189    pub fn create_children_detached(
190        self: &Rc<Self>,
191        templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
192        context: &mut RuntimeContext,
193    ) -> Vec<Rc<ExpandedNode>> {
194        let containing_component = if self.instance_node.base().flags().is_component {
195            Rc::downgrade(&self)
196        } else {
197            Weak::clone(&self.containing_component)
198        };
199
200        let mut children = Vec::new();
201
202        for (template, env) in templates {
203            children.push(Self::new(
204                template,
205                env,
206                context,
207                Weak::clone(&containing_component),
208            ));
209        }
210        children
211    }
212
213    pub fn attach_children(
214        self: &Rc<Self>,
215        new_children: Vec<Rc<ExpandedNode>>,
216        context: &mut RuntimeContext,
217    ) {
218        let mut curr_children = self.children.borrow_mut();
219        //TODO here we could probably check intersection between old and new children (to avoid unmount + mount)
220        if *self.attached.borrow() > 0 {
221            for child in curr_children.iter() {
222                Rc::clone(child).recurse_unmount(context);
223            }
224            for child in new_children.iter() {
225                Rc::clone(child).recurse_mount(context);
226            }
227        }
228        for child in new_children.iter() {
229            *child.parent_expanded_node.borrow_mut() = Rc::downgrade(self);
230        }
231        *curr_children = new_children;
232    }
233
234    pub fn set_children(
235        self: &Rc<Self>,
236        templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
237        context: &mut RuntimeContext,
238    ) {
239        let new_children = self.create_children_detached(templates, context);
240        self.attach_children(new_children, context);
241    }
242
243    /// This method recursively updates all node properties. When dirty-dag exists, this won't
244    /// need to be here since all property dependencies can be set up and removed during mount/unmount
245    pub fn recurse_update(self: &Rc<Self>, context: &mut RuntimeContext) {
246        self.get_common_properties()
247            .borrow_mut()
248            .compute_properties(&self.stack, context.expression_table());
249
250        let viewport = self
251            .parent_expanded_node
252            .borrow()
253            .upgrade()
254            .and_then(|p| {
255                let props = p.layout_properties.borrow();
256                props.as_ref().map(|c| c.computed_tab.clone())
257            })
258            .unwrap_or(context.globals().viewport.clone());
259
260        *self.layout_properties.borrow_mut() = Some(LayoutProperties {
261            computed_tab: compute_tab(self, &viewport),
262        });
263
264        if let Some(ref registry) = self.instance_node.base().handler_registry {
265            for handler in &registry.borrow().tick_handlers {
266                handler(Rc::clone(&self.properties), &self.get_node_context(context))
267            }
268        }
269        Rc::clone(&self.instance_node).update(&self, context);
270        if *self.attached.borrow() > 0 {
271            self.instance_node.handle_native_patches(self, context);
272        }
273        if let Some(ref registry) = self.instance_node.base().handler_registry {
274            #[cfg(feature = "designtime")]
275            {
276                for handler in registry
277                    .borrow()
278                    .handlers
279                    .get("pre_render")
280                    .unwrap_or(&Vec::new())
281                {
282                    handler(
283                        Rc::clone(&self.properties),
284                        &self.get_node_context(context),
285                        None,
286                    )
287                }
288            }
289            #[cfg(not(feature = "designtime"))]
290            {
291                for handler in &registry.borrow().pre_render_handlers {
292                    handler(Rc::clone(&self.properties), &self.get_node_context(context))
293                }
294            }
295        }
296        for child in self.children.borrow().iter() {
297            child.recurse_update(context);
298        }
299    }
300
301    fn recurse_mount(self: Rc<Self>, context: &mut RuntimeContext) {
302        if *self.attached.borrow() == 0 {
303            *self.attached.borrow_mut() += 1;
304            context.lookup.insert(self.id_chain[0], Rc::clone(&self));
305            self.instance_node.handle_mount(&self, context);
306            if let Some(ref registry) = self.instance_node.base().handler_registry {
307                #[cfg(feature = "designtime")]
308                {
309                    for handler in registry
310                        .borrow()
311                        .handlers
312                        .get("mount")
313                        .unwrap_or(&Vec::new())
314                    {
315                        handler(
316                            Rc::clone(&self.properties),
317                            &self.get_node_context(context),
318                            None,
319                        )
320                    }
321                }
322                #[cfg(not(feature = "designtime"))]
323                {
324                    for handler in &registry.borrow().mount_handlers {
325                        handler(Rc::clone(&self.properties), &self.get_node_context(context))
326                    }
327                }
328            }
329        }
330        for child in self.children.borrow().iter() {
331            Rc::clone(child).recurse_mount(context);
332        }
333    }
334
335    fn recurse_unmount(self: Rc<Self>, context: &mut RuntimeContext) {
336        for child in self.children.borrow().iter() {
337            Rc::clone(child).recurse_unmount(context);
338        }
339        if *self.attached.borrow() == 1 {
340            *self.attached.borrow_mut() -= 1;
341            context.lookup.remove(&self.id_chain[0]);
342            self.instance_node.handle_unmount(&self, context);
343        }
344    }
345
346    pub fn recurse_render(&self, context: &mut RuntimeContext, rcs: &mut dyn RenderContext) {
347        for child in self.children.borrow().iter().rev() {
348            child.recurse_render(context, rcs);
349        }
350        self.instance_node.render(&self, context, rcs);
351    }
352
353    /// Manages unpacking an Rc<RefCell<dyn Any>>, downcasting into
354    /// the parameterized `target_type`, and executing a provided closure `body` in the
355    /// context of that unwrapped variant (including support for mutable operations),
356    /// the closure is executed.  Used at least by calculating properties in `expand_node` and
357    /// passing `&mut self` into event handlers (where the typed `self` is retrieved from an instance of `dyn Any`)
358    pub fn with_properties_unwrapped<T: 'static, R>(
359        &self,
360        callback: impl FnOnce(&mut T) -> R,
361    ) -> R {
362        // Borrow the contents of the RefCell mutably.
363        let mut borrowed = self.properties.borrow_mut();
364
365        // Downcast the unwrapped value to the specified `target_type` (or panic)
366        let mut unwrapped_value = if let Some(val) = borrowed.downcast_mut::<T>() {
367            val
368        } else {
369            panic!() //Failed to downcast
370        };
371        callback(&mut unwrapped_value)
372    }
373
374    pub fn recurse_visit_postorder<T>(
375        self: &Rc<Self>,
376        func: &impl Fn(&Rc<Self>, &mut T),
377        val: &mut T,
378    ) {
379        for child in self.children.borrow().iter().rev() {
380            child.recurse_visit_postorder(func, val);
381        }
382        func(self, val);
383    }
384
385    pub fn get_node_context(&self, context: &RuntimeContext) -> NodeContext {
386        let globals = context.globals();
387        let computed_props = self.layout_properties.borrow();
388        let bounds_self = computed_props
389            .as_ref()
390            .map(|v| v.computed_tab.bounds)
391            .unwrap_or(globals.viewport.bounds);
392        let parent = self.parent_expanded_node.borrow().upgrade();
393        let bounds_parent = parent
394            .as_ref()
395            .and_then(|p| {
396                let props = p.layout_properties.borrow();
397                props.as_ref().map(|v| v.computed_tab.bounds)
398            })
399            .unwrap_or(globals.viewport.bounds);
400        NodeContext {
401            frames_elapsed: globals.frames_elapsed,
402            bounds_self,
403            bounds_parent,
404            #[cfg(feature = "designtime")]
405            designtime: globals.designtime.clone(),
406        }
407    }
408
409    pub fn get_common_properties(&self) -> Rc<RefCell<CommonProperties>> {
410        Rc::clone(&self.common_properties)
411    }
412
413    /// Determines whether the provided ray, orthogonal to the view plane,
414    /// intersects this `ExpandedNode`.
415    pub fn ray_cast_test(&self, ray: &(f64, f64)) -> bool {
416        // Don't vacuously hit for `invisible_to_raycasting` nodes
417        if self.instance_node.base().flags().invisible_to_raycasting {
418            return false;
419        }
420
421        let props = self.layout_properties.borrow();
422        let computed_tab = &props.as_ref().unwrap().computed_tab;
423
424        let inverted_transform = computed_tab.transform.inverse();
425        let transformed_ray = inverted_transform * Point { x: ray.0, y: ray.1 };
426
427        let relevant_bounds = computed_tab.bounds;
428
429        //Default implementation: rectilinear bounding hull
430        let res = transformed_ray.x > 0.0
431            && transformed_ray.y > 0.0
432            && transformed_ray.x < relevant_bounds.0
433            && transformed_ray.y < relevant_bounds.1;
434
435        res
436    }
437
438    /// Returns the size of this node, or `None` if this node
439    /// doesn't have a size (e.g. `Group`)
440    pub fn get_size(&self) -> (Size, Size) {
441        self.instance_node.get_size(self)
442    }
443
444    /// Returns the size of this node in pixels, requiring this node's containing bounds
445    /// for calculation of `Percent` values
446    pub fn get_size_computed(&self, bounds: (f64, f64)) -> (f64, f64) {
447        let size = self.get_size();
448        (
449            size.0.evaluate(bounds, Axis::X),
450            size.1.evaluate(bounds, Axis::Y),
451        )
452    }
453
454    /// Used at least by ray-casting; only nodes that clip content (and thus should
455    /// not allow outside content to respond to ray-casting) should return a value
456    pub fn get_clipping_size(&self) -> Option<(Size, Size)> {
457        None
458    }
459
460    /// Returns the clipping bounds of this node in pixels, requiring
461    /// parent bounds for calculation of `Percent` values
462    pub fn get_clipping_size_computed(&self, bounds: (f64, f64)) -> (f64, f64) {
463        match self.get_clipping_size() {
464            None => bounds,
465            Some(size_raw) => (
466                size_raw.0.evaluate(bounds, Axis::X),
467                size_raw.1.evaluate(bounds, Axis::Y),
468            ),
469        }
470    }
471
472    /// Returns the scroll offset from a Scroller component
473    /// Used by the engine to transform its children
474    pub fn get_scroll_offset(&mut self) -> (f64, f64) {
475        // (0.0, 0.0)
476        todo!("patch into an ExpandedNode-friendly way to track this state");
477    }
478
479    pub fn compute_flattened_slot_children(&self) {
480        if let Some(slot_children) = self.expanded_slot_children.borrow().as_ref() {
481            *self.expanded_and_flattened_slot_children.borrow_mut() =
482                Some(flatten_expanded_nodes_for_slot(&slot_children));
483        }
484    }
485
486    dispatch_event_handler!(
487        dispatch_scroll,
488        ArgsScroll,
489        scroll_handlers,
490        SCROLL_HANDLERS
491    );
492    dispatch_event_handler!(dispatch_clap, ArgsClap, clap_handlers, CLAP_HANDLERS);
493    dispatch_event_handler!(
494        dispatch_touch_start,
495        ArgsTouchStart,
496        touch_start_handlers,
497        TOUCH_START_HANDLERS
498    );
499
500    dispatch_event_handler!(
501        dispatch_touch_move,
502        ArgsTouchMove,
503        touch_move_handlers,
504        TOUCH_MOVE_HANDLERS
505    );
506    dispatch_event_handler!(
507        dispatch_touch_end,
508        ArgsTouchEnd,
509        touch_end_handlers,
510        TOUCH_END_HANDLERS
511    );
512    dispatch_event_handler!(
513        dispatch_key_down,
514        ArgsKeyDown,
515        key_down_handlers,
516        KEY_DOWN_HANDLERS
517    );
518    dispatch_event_handler!(dispatch_key_up, ArgsKeyUp, key_up_handlers, KEY_UP_HANDLERS);
519    dispatch_event_handler!(
520        dispatch_key_press,
521        ArgsKeyPress,
522        key_press_handlers,
523        KEY_PRESS_HANDLERS
524    );
525    dispatch_event_handler!(
526        dispatch_checkbox_change,
527        ArgsCheckboxChange,
528        checkbox_change_handlers,
529        CHECKBOX_CHANGE_HANDLERS
530    );
531    dispatch_event_handler!(
532        dispatch_textbox_change,
533        ArgsTextboxChange,
534        textbox_change_handlers,
535        TEXTBOX_CHANGE_HANDLERS
536    );
537    dispatch_event_handler!(
538        dispatch_button_click,
539        ArgsButtonClick,
540        button_click_handlers,
541        BUTTON_CLICK_HANDLERS
542    );
543    dispatch_event_handler!(
544        dispatch_mouse_down,
545        ArgsMouseDown,
546        mouse_down_handlers,
547        MOUSE_DOWN_HANDLERS
548    );
549    dispatch_event_handler!(
550        dispatch_mouse_up,
551        ArgsMouseUp,
552        mouse_up_handlers,
553        MOUSE_UP_HANDLERS
554    );
555    dispatch_event_handler!(
556        dispatch_mouse_move,
557        ArgsMouseMove,
558        mouse_move_handlers,
559        MOUSE_MOVE_HANDLERS
560    );
561    dispatch_event_handler!(
562        dispatch_mouse_over,
563        ArgsMouseOver,
564        mouse_over_handlers,
565        MOUSE_OVER_HANDLERS
566    );
567    dispatch_event_handler!(
568        dispatch_mouse_out,
569        ArgsMouseOut,
570        mouse_out_handlers,
571        MOUSE_OUT_HANDLERS
572    );
573    dispatch_event_handler!(
574        dispatch_double_click,
575        ArgsDoubleClick,
576        double_click_handlers,
577        DOUBLE_CLICK_HANDLERS
578    );
579    dispatch_event_handler!(
580        dispatch_context_menu,
581        ArgsContextMenu,
582        context_menu_handlers,
583        CONTEXT_MENU_HANDLERS
584    );
585    dispatch_event_handler!(dispatch_click, ArgsClick, click_handlers, CLICK_HANDLERS);
586    dispatch_event_handler!(dispatch_wheel, ArgsWheel, wheel_handlers, WHEEL_HANDLERS);
587}
588
589/// Properties that are currently re-computed each frame before rendering.
590#[cfg_attr(debug_assertions, derive(Debug))]
591pub struct LayoutProperties {
592    /// Computed transform and size of this ExpandedNode
593    pub computed_tab: TransformAndBounds,
594}
595
596/// Given some InstanceNodePtrList, distill away all "slot-invisible" nodes (namely, `if` and `for`)
597/// and return another InstanceNodePtrList with a flattened top-level list of nodes.
598fn flatten_expanded_nodes_for_slot(nodes: &[Rc<ExpandedNode>]) -> Vec<Rc<ExpandedNode>> {
599    let mut result = vec![];
600    for node in nodes {
601        if node.instance_node.base().flags().invisible_to_slot {
602            result.extend(flatten_expanded_nodes_for_slot(
603                node.children
604                    .borrow()
605                    .clone()
606                    .into_iter()
607                    .collect::<Vec<_>>()
608                    .as_slice(),
609            ));
610        } else {
611            result.push(Rc::clone(&node))
612        }
613    }
614    result
615}
616
617#[cfg(debug_assertions)]
618impl std::fmt::Debug for ExpandedNode {
619    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
620        //see: https://users.rust-lang.org/t/reusing-an-fmt-formatter/8531/4
621        //maybe this utility should be moved to a more accessible place?
622        pub struct Fmt<F>(pub F)
623        where
624            F: Fn(&mut fmt::Formatter) -> fmt::Result;
625
626        impl<F> fmt::Debug for Fmt<F>
627        where
628            F: Fn(&mut fmt::Formatter) -> fmt::Result,
629        {
630            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
631                (self.0)(f)
632            }
633        }
634
635        f.debug_struct("ExpandedNode")
636            .field(
637                "instance_node",
638                &Fmt(|f| self.instance_node.resolve_debug(f, Some(self))),
639            )
640            .field("id_chain", &self.id_chain)
641            // .field("common_properties", &self.common_properties.try_borrow())
642            // .field(
643            //     "computed_expanded_properties",
644            //     &self.computed_expanded_properties.try_borrow(),
645            // )
646            .field(
647                "children",
648                &self.children.try_borrow().iter().collect::<Vec<_>>(),
649            )
650            .field(
651                "parent",
652                &self
653                    .parent_expanded_node
654                    .borrow()
655                    .upgrade()
656                    .map(|v| v.id_chain.clone()),
657            )
658            // .field(
659            //     "slot_children",
660            //     &self.expanded_and_flattened_slot_children.as_ref().map(|o| {
661            //         o.iter()
662            //             .map(|v| v.borrow().id_chain.clone())
663            //             .collect::<Vec<_>>()
664            //     }),
665            // )
666            .field("occlusion_id", &self.occlusion_id.borrow())
667            .field(
668                "containing_component",
669                &self
670                    .containing_component
671                    .upgrade()
672                    .map(|v| v.id_chain.clone()),
673            )
674            .finish()
675    }
676}