pax_runtime/
repeat.rs

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
16/// A special "control-flow" primitive associated with the `for` statement.
17/// Repeat allows for nodes to be rendered dynamically per data specified in `source_expression`.
18/// That is: for a `source_expression` of length `n`, `Repeat` will render its
19/// template `n` times, each with an embedded component context (`RepeatItem`)
20/// with an index `i` and a pointer to that relevant datum `source_expression[i]`
21pub struct RepeatInstance {
22    pub base: BaseInstance,
23}
24
25impl ImplToFromPaxAny for RepeatProperties {}
26///Contains modal _vec_ and _range_ variants, describing whether the Repeat source
27///is encoded as a Vec<T> (where T is a `PaxValue` properties type) or as a Range<isize>
28#[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        // No-op: wait with creating child-nodes until update tick, since the
134        // condition has then been evaluated
135        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}