pax_core/
component.rs

1use std::rc::Rc;
2use std::{cell::RefCell, iter};
3
4use crate::{
5    BaseInstance, ExpandedNode, ExpressionTable, Globals, InstanceFlags, InstanceNode,
6    InstanceNodePtrList, InstantiationArgs, RuntimeContext,
7};
8use pax_runtime_api::{Layer, Timeline};
9
10/// A render node with its own runtime context.  Will push a frame
11/// to the runtime stack including the specified `slot_children` and
12/// a `dyn Any` properties object.  `Component` is used at the root of
13/// applications, at the root of reusable components like `Stacker`, and
14/// in special applications like `Repeat` where it houses the `RepeatItem`
15/// properties attached to each of Repeat's virtual nodes.
16pub struct ComponentInstance {
17    pub template: InstanceNodePtrList,
18    pub timeline: Option<Rc<RefCell<Timeline>>>,
19    pub compute_properties_fn: Box<dyn Fn(&ExpandedNode, &ExpressionTable, &Globals)>,
20    base: BaseInstance,
21}
22
23// #[derive(Default)]
24// pub struct ComponentProperties {
25//     pub slot_children: BTreeSet<Rc<ExpandedNode>>,
26// }
27
28impl InstanceNode for ComponentInstance {
29    fn instantiate(mut args: InstantiationArgs) -> Rc<Self> {
30        let component_template = args.component_template.take();
31        let template = component_template.unwrap_or_default();
32
33        let compute_properties_fn = args.compute_properties_fn.take();
34        let base = BaseInstance::new(
35            args,
36            InstanceFlags {
37                invisible_to_slot: false,
38                invisible_to_raycasting: true,
39                layer: Layer::DontCare,
40                is_component: true,
41            },
42        );
43        Rc::new(ComponentInstance {
44            base,
45            template,
46            compute_properties_fn: compute_properties_fn
47                .expect("must pass a compute_properties_fn to a Component instance"),
48            timeline: None,
49        })
50    }
51
52    fn update(self: Rc<Self>, expanded_node: &Rc<ExpandedNode>, context: &mut RuntimeContext) {
53        // Compute properties
54        (*self.compute_properties_fn)(
55            &expanded_node,
56            context.expression_table(),
57            context.globals(),
58        );
59
60        // Update slot children. Needs to be done since a change in
61        // a repeat can trigger changes in slot references.
62        if let Some(slot_children) = expanded_node.expanded_slot_children.borrow().as_ref() {
63            for slot_child in slot_children {
64                slot_child.recurse_update(context);
65            }
66        }
67
68        expanded_node.compute_flattened_slot_children();
69    }
70
71    fn handle_mount(&self, expanded_node: &Rc<ExpandedNode>, context: &mut RuntimeContext) {
72        if let Some(containing_component) = expanded_node.containing_component.upgrade() {
73            let env = Rc::clone(&expanded_node.stack);
74            let children_with_env = self
75                .base()
76                .get_instance_children()
77                .iter()
78                .cloned()
79                .zip(iter::repeat(env));
80            *expanded_node.expanded_slot_children.borrow_mut() =
81                Some(containing_component.create_children_detached(children_with_env, context));
82        }
83
84        let new_env = expanded_node.stack.push(&expanded_node.properties);
85        let children_with_envs = self.template.iter().cloned().zip(iter::repeat(new_env));
86        expanded_node.set_children(children_with_envs, context);
87    }
88
89    #[cfg(debug_assertions)]
90    fn resolve_debug(
91        &self,
92        f: &mut std::fmt::Formatter,
93        _expanded_node: Option<&ExpandedNode>,
94    ) -> std::fmt::Result {
95        f.debug_struct("Component").finish()
96    }
97
98    fn base(&self) -> &BaseInstance {
99        &self.base
100    }
101}