1use std::collections::HashMap;
2use std::iter;
3use std::rc::Rc;
4use_RefCell!();
5
6use pax_runtime_api::CoercionRules;
7use pax_runtime_api::{
8 borrow, borrow_mut, use_RefCell, ImplToFromPaxAny, PaxValue, Property, ToPaxValue, Variable,
9};
10
11use crate::api::Layer;
12use crate::{
13 BaseInstance, ExpandedNode, InstanceFlags, InstanceNode, InstantiationArgs, RuntimeContext,
14};
15
16pub struct RepeatInstance {
22 pub base: BaseInstance,
23}
24
25impl ImplToFromPaxAny for RepeatProperties {}
26#[derive(Default)]
29pub struct RepeatProperties {
30 pub source_expression: Property<PaxValue>,
31 pub iterator_i_symbol: Property<Option<String>>,
32 pub iterator_elem_symbol: Property<Option<String>>,
33}
34
35impl ToPaxValue for RepeatProperties {
36 fn to_pax_value(self) -> PaxValue {
37 PaxValue::Object(
38 vec![
39 (
40 "source_expression".to_string(),
41 self.source_expression.to_pax_value(),
42 ),
43 (
44 "iterator_i_symbol".to_string(),
45 self.iterator_i_symbol.to_pax_value(),
46 ),
47 (
48 "iterator_elem_symbol".to_string(),
49 self.iterator_elem_symbol.to_pax_value(),
50 ),
51 ]
52 .into_iter()
53 .collect(),
54 )
55 }
56}
57
58pub struct RepeatItem {
59 pub elem: Property<PaxValue>,
60 pub i: Property<usize>,
61}
62
63impl ToPaxValue for RepeatItem {
64 fn to_pax_value(self) -> PaxValue {
65 PaxValue::Object(
66 vec![
67 ("elem".to_string(), self.elem.get().to_pax_value()),
68 ("i".to_string(), self.i.get().to_pax_value()),
69 ]
70 .into_iter()
71 .collect(),
72 )
73 }
74}
75
76impl InstanceNode for RepeatInstance {
77 fn instantiate(args: InstantiationArgs) -> Rc<Self>
78 where
79 Self: Sized,
80 {
81 Rc::new(Self {
82 base: BaseInstance::new(
83 args,
84 InstanceFlags {
85 invisible_to_slot: true,
86 invisible_to_raycasting: true,
87 layer: Layer::DontCare,
88 is_component: false,
89 is_slot: false,
90 },
91 ),
92 })
93 }
94
95 fn resolve_debug(
96 &self,
97 f: &mut std::fmt::Formatter,
98 _expanded_node: Option<&ExpandedNode>,
99 ) -> std::fmt::Result {
100 f.debug_struct("Repeat").finish()
101 }
102
103 fn base(&self) -> &BaseInstance {
104 &self.base
105 }
106
107 fn update(self: Rc<Self>, _expanded_node: &Rc<ExpandedNode>, _context: &Rc<RuntimeContext>) {}
108
109 fn handle_mount(
110 self: Rc<Self>,
111 expanded_node: &Rc<ExpandedNode>,
112 context: &Rc<RuntimeContext>,
113 ) {
114 self.handle_setup(expanded_node, context, true);
115 }
116
117 fn handle_control_flow_node_expansion(
118 self: Rc<Self>,
119 expanded_node: &Rc<ExpandedNode>,
120 context: &Rc<RuntimeContext>,
121 ) {
122 self.handle_setup(expanded_node, context, false);
123 }
124}
125
126impl RepeatInstance {
127 fn handle_setup(
128 self: Rc<Self>,
129 expanded_node: &Rc<ExpandedNode>,
130 context: &Rc<RuntimeContext>,
131 is_mount: bool,
132 ) {
133 let weak_ref_self = Rc::downgrade(expanded_node);
136 let cloned_self = Rc::clone(&self);
137 let cloned_context = Rc::clone(context);
138 let source_expression =
139 expanded_node.with_properties_unwrapped(|properties: &mut RepeatProperties| {
140 properties.source_expression.clone()
141 });
142
143 let i_symbol =
144 expanded_node.with_properties_unwrapped(|properties: &mut RepeatProperties| {
145 properties.iterator_i_symbol.clone()
146 });
147 let elem_symbol =
148 expanded_node.with_properties_unwrapped(|properties: &mut RepeatProperties| {
149 properties.iterator_elem_symbol.clone()
150 });
151
152 let deps = [
153 source_expression.untyped(),
154 i_symbol.untyped(),
155 elem_symbol.untyped(),
156 ];
157
158 let last_length = Rc::new(RefCell::new(0));
159 let last_elem_sym = Rc::new(RefCell::new(None));
160 let last_i_sym = Rc::new(RefCell::new(None));
161
162 let children = Property::computed_with_name(
163 move || {
164 let Some(cloned_expanded_node) = weak_ref_self.upgrade() else {
165 panic!("ran evaluator after expanded node dropped (repeat elem)")
166 };
167 let source = source_expression.get();
168 let source_len = if let PaxValue::Range(start, end) = source {
169 (isize::try_coerce(*end).unwrap() - isize::try_coerce(*start).unwrap()) as usize
170 } else if let PaxValue::Vec(v) = source {
171 v.len()
172 } else {
173 log::warn!("source is not a vec");
174 0
175 };
176 if source_len == *borrow!(last_length)
177 && i_symbol.read(|i| i == &*borrow!(last_i_sym))
178 && elem_symbol.read(|e| e == &*borrow!(last_elem_sym))
179 {
180 return cloned_expanded_node.children.get();
181 }
182 *borrow_mut!(last_length) = source_len;
183 *borrow_mut!(last_i_sym) = i_symbol.get();
184 *borrow_mut!(last_elem_sym) = elem_symbol.get();
185
186 let template_children = cloned_self.base().get_instance_children();
187 let children_with_envs = iter::repeat(template_children)
188 .take(source_len)
189 .enumerate()
190 .flat_map(|(i, children)| {
191 let property_i = Property::new(i);
192 let cp_source_expression = source_expression.clone();
193 let property_elem = Property::computed_with_name(
194 move || {
195 let source = cp_source_expression.get();
196 if let PaxValue::Range(start, _) = source {
197 let start = isize::try_coerce(*start).unwrap();
198 let elem = (start + i as isize).to_pax_value();
199 elem
200 } else if let PaxValue::Vec(v) = source {
201 v[i].clone()
202 } else {
203 log::warn!("source is not a vec");
204 Default::default()
205 }
206 },
207 &[source_expression.untyped()],
208 "repeat elem",
209 );
210
211 let mut scope: HashMap<String, Variable> = HashMap::new();
212 if let Some(i_symbol) = i_symbol.get() {
213 scope.insert(
214 i_symbol.clone(),
215 Variable::new_from_typed_property(property_i),
216 );
217 }
218 if let Some(elem_symbol) = elem_symbol.get() {
219 scope.insert(
220 elem_symbol,
221 Variable::new_from_typed_property(property_elem),
222 );
223 }
224
225 let new_env = cloned_expanded_node.stack.push(scope);
226 borrow!(children)
227 .clone()
228 .into_iter()
229 .zip(iter::repeat(new_env))
230 });
231 let ret = cloned_expanded_node.generate_children(
232 children_with_envs,
233 &cloned_context,
234 &cloned_expanded_node.parent_frame,
235 is_mount,
236 );
237 ret
238 },
239 &deps,
240 &format!("repeat_children (node id: {})", expanded_node.id.0),
241 );
242 expanded_node.children.replace_with(children);
243 }
244}