1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::{iter, rc::Rc};

use crate::api::Layer;
use crate::{
    declarative_macros::handle_vtable_update, BaseInstance, ExpandedNode, InstanceFlags,
    InstanceNode, InstantiationArgs, RuntimeContext,
};

/// A special "control-flow" primitive, Conditional (`if`) allows for a
/// subtree of a component template to be rendered conditionally,
/// based on the value of the property `boolean_expression`.
/// The Pax compiler handles ConditionalInstance specially
/// with the `if` syntax in templates.
pub struct ConditionalInstance {
    base: BaseInstance,
}

///Contains the expression of a conditional, evaluated as an expression.
#[derive(Default)]
pub struct ConditionalProperties {
    pub boolean_expression: Box<dyn crate::api::PropertyInstance<bool>>,
    last_boolean_expression: Option<bool>,
}

impl InstanceNode for ConditionalInstance {
    fn instantiate(args: InstantiationArgs) -> Rc<Self>
    where
        Self: Sized,
    {
        Rc::new(Self {
            base: BaseInstance::new(
                args,
                InstanceFlags {
                    invisible_to_slot: true,
                    invisible_to_raycasting: true,
                    layer: Layer::DontCare,
                    is_component: false,
                },
            ),
        })
    }

    fn update(self: Rc<Self>, expanded_node: &Rc<ExpandedNode>, context: &mut RuntimeContext) {
        let (should_update, active) =
            expanded_node.with_properties_unwrapped(|properties: &mut ConditionalProperties| {
                handle_vtable_update(
                    context.expression_table(),
                    &expanded_node.stack,
                    &mut properties.boolean_expression,
                    context.globals(),
                );
                let val = Some(*properties.boolean_expression.get());
                let update_children = properties.last_boolean_expression != val;
                properties.last_boolean_expression = val;
                (update_children, *properties.boolean_expression.get())
            });

        if should_update {
            if active {
                let env = Rc::clone(&expanded_node.stack);
                let children = self.base().get_instance_children().borrow();
                let children_with_envs = children.iter().cloned().zip(iter::repeat(env));
                expanded_node.set_children(children_with_envs, context);
            } else {
                expanded_node.set_children(iter::empty(), context);
            }
        }
    }

    fn handle_mount(&self, _expanded_node: &Rc<ExpandedNode>, _context: &mut RuntimeContext) {
        // No-op: wait with creating child-nodes until update tick, since the
        // condition has then been evaluated
    }

    #[cfg(debug_assertions)]
    fn resolve_debug(
        &self,
        f: &mut std::fmt::Formatter,
        _expanded_node: Option<&ExpandedNode>,
    ) -> std::fmt::Result {
        f.debug_struct("Conditional").finish()
    }

    fn base(&self) -> &BaseInstance {
        &self.base
    }

    fn get_clipping_size(
        &self,
        _expanded_node: &ExpandedNode,
    ) -> Option<(crate::api::Size, crate::api::Size)> {
        None
    }
}