1use std::cell::Ref;
2use std::collections::HashMap;
3use std::iter;
4use std::rc::Rc;
5
6use pax_runtime_api::{
7 borrow, borrow_mut, use_RefCell, Interpolatable, LocalProperty, PaxValue, ToPaxValue, Variable,
8};
9
10use_RefCell!();
11use crate::api::{Layer, Timeline};
12use crate::{
13 BaseInstance, ExpandedNode, InstanceFlags, InstanceNode, InstanceNodePtrList,
14 InstantiationArgs, RuntimeContext,
15};
16
17pub struct ComponentInstance {
24 pub template: InstanceNodePtrList,
25 pub timelines: Vec<Rc<RefCell<Timeline>>>,
26 base: BaseInstance,
27}
28
29impl InstanceNode for ComponentInstance {
35 fn instantiate(mut args: InstantiationArgs) -> Rc<Self> {
36 let component_template = args.component_template.take();
37 let template = component_template.unwrap_or_default();
38 let base = BaseInstance::new(
39 args,
40 InstanceFlags {
41 invisible_to_slot: false,
42 invisible_to_raycasting: true,
43 layer: Layer::DontCare,
44 is_component: true,
45 is_slot: false,
46 },
47 );
48 Rc::new(ComponentInstance {
49 base,
50 template,
51 timelines: vec![],
52 })
53 }
54
55 fn handle_setup_projected_children(
56 self: Rc<Self>,
57 expanded_node: &Rc<ExpandedNode>,
58 context: &Rc<RuntimeContext>,
59 ) {
60 if let Some(containing_component) = expanded_node.containing_component.upgrade() {
61 let env = if let Some(stack_frame) =
62 ScrollPosition::create_builtin_if_exists(borrow!(expanded_node.properties_scope))
63 {
64 expanded_node.stack.push(stack_frame)
65 } else {
66 Rc::clone(&expanded_node.stack)
67 };
68 let children = borrow!(self.base().get_instance_children());
69 let children_with_env = children.iter().cloned().zip(iter::repeat(env));
70 let new_projected_children = containing_component.create_children_detached(
71 children_with_env,
72 context,
73 &Rc::downgrade(expanded_node),
74 );
75 *borrow_mut!(expanded_node.expanded_projected_children) = Some(new_projected_children);
76 }
77 }
78
79 fn handle_mount(
80 self: Rc<Self>,
81 expanded_node: &Rc<ExpandedNode>,
82 context: &Rc<RuntimeContext>,
83 ) {
84 let new_env = {
85 let mut properties_scope = borrow_mut!(expanded_node.properties_scope);
86 properties_scope.insert(
87 "$suspended".to_string(),
88 Variable::new_from_typed_property(expanded_node.suspended.clone()),
89 );
90 expanded_node.stack.push(properties_scope.clone())
91 };
92 let children = borrow!(self.template);
93 let children_with_envs = children.iter().cloned().zip(iter::repeat(new_env));
94 expanded_node
95 .children
96 .replace_with(LocalProperty::new_with_name(
97 expanded_node.generate_children(
98 children_with_envs,
99 context,
100 &expanded_node.parent_frame,
101 true,
102 ),
103 &format!("component (node id: {})", expanded_node.id.0),
104 ));
105 drop(children);
106 expanded_node.sync_imported_settings(context);
110 }
111
112 fn handle_unmount(&self, expanded_node: &Rc<ExpandedNode>, context: &Rc<RuntimeContext>) {
113 if let Some(projected_children) =
114 borrow_mut!(expanded_node.expanded_projected_children).take()
115 {
116 for projected_child in projected_children {
117 projected_child.recurse_unmount(context);
118 }
119 }
120 }
121
122 fn resolve_debug(
123 &self,
124 f: &mut std::fmt::Formatter,
125 _expanded_node: Option<&ExpandedNode>,
126 ) -> std::fmt::Result {
127 f.debug_struct("Component").finish()
128 }
129
130 fn base(&self) -> &BaseInstance {
131 &self.base
132 }
133
134 fn property_requires_occlusion_recompute(&self, _property_name: &str) -> bool {
135 false
137 }
138
139 fn get_template(&self) -> Option<&InstanceNodePtrList> {
140 Some(&self.template)
141 }
142}
143
144#[derive(Debug, Clone, Default)]
145pub struct ScrollPosition {
147 pub x: f64,
148 pub y: f64,
149}
150
151impl Interpolatable for ScrollPosition {
152 fn interpolate(&self, other: &Self, t: f64) -> Self {
153 ScrollPosition {
154 x: self.x + (other.x - self.x) * t,
155 y: self.y + (other.y - self.y) * t,
156 }
157 }
158}
159
160impl ToPaxValue for ScrollPosition {
161 fn to_pax_value(self) -> PaxValue {
162 PaxValue::Object(
163 vec![
164 ("x".to_string(), self.x.to_pax_value()),
165 ("y".to_string(), self.y.to_pax_value()),
166 ]
167 .into_iter()
168 .collect(),
169 )
170 }
171}
172
173impl ScrollPosition {
174 pub fn create_builtin_if_exists(
176 property_scope: Ref<HashMap<String, Variable>>,
177 ) -> Option<HashMap<String, Variable>> {
178 let scroll_pos_x: LocalProperty<f64> = LocalProperty::new_from_untyped(
179 property_scope
180 .get("scroll_pos_x")?
181 .get_untyped_property()
182 .clone(),
183 );
184 let scroll_pos_y: LocalProperty<f64> = LocalProperty::new_from_untyped(
185 property_scope
186 .get("scroll_pos_y")?
187 .get_untyped_property()
188 .clone(),
189 );
190 let deps = [scroll_pos_x.untyped(), scroll_pos_y.untyped()];
191 let scroll_position = LocalProperty::computed(
192 move || ScrollPosition {
193 x: scroll_pos_x.get(),
194 y: scroll_pos_y.get(),
195 },
196 &deps,
197 );
198
199 let scroll_position_var = Variable::new_from_typed_property(scroll_position);
200 let stack_frame = vec![("$scroll_position".to_string(), scroll_position_var)]
201 .into_iter()
202 .collect();
203 Some(stack_frame)
204 }
205}