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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::cell::RefCell;
use std::rc::Rc;

use crate::{InstantiationArgs, RenderNode, RenderNodePtr, RenderNodePtrList, RenderTreeContext};
use pax_properties_coproduct::TypesCoproduct;
use pax_runtime_api::{CommonProperties, Layer, PropertyInstance, Size};
use piet_common::RenderContext;

/// 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<R: 'static + RenderContext> {
    pub instance_id: u32,

    pub boolean_expression: Box<dyn PropertyInstance<bool>>,
    pub true_branch_children: RenderNodePtrList<R>,
    pub false_branch_children: RenderNodePtrList<R>,
    pub cleanup_children: RenderNodePtrList<R>,
    pub common_properties: CommonProperties,
}

impl<R: 'static + RenderContext> RenderNode<R> for ConditionalInstance<R> {
    fn get_instance_id(&self) -> u32 {
        self.instance_id
    }

    fn get_common_properties(&self) -> &CommonProperties {
        &self.common_properties
    }

    fn instantiate(args: InstantiationArgs<R>) -> Rc<RefCell<Self>>
    where
        Self: Sized,
    {
        let mut instance_registry = (*args.instance_registry).borrow_mut();
        let instance_id = instance_registry.mint_id();
        let ret = Rc::new(RefCell::new(Self {
            instance_id,
            true_branch_children: match args.children {
                None => Rc::new(RefCell::new(vec![])),
                Some(children) => children,
            },
            common_properties: args.common_properties,
            boolean_expression: args
                .conditional_boolean_expression
                .expect("Conditional requires boolean_expression"),
            false_branch_children: Rc::new(RefCell::new(vec![])),
            cleanup_children: Rc::new(RefCell::new(vec![])),
        }));

        instance_registry.register(instance_id, Rc::clone(&ret) as RenderNodePtr<R>);
        ret
    }

    fn compute_properties(&mut self, rtc: &mut RenderTreeContext<R>) {
        if let Some(boolean_expression) =
            rtc.compute_vtable_value(self.boolean_expression._get_vtable_id())
        {
            let old_value = *self.boolean_expression.get();
            let new_value = if let TypesCoproduct::bool(v) = boolean_expression {
                v
            } else {
                unreachable!()
            };

            let mut instance_registry = (*rtc.engine.instance_registry).borrow_mut();
            if old_value && !new_value {
                (*self.true_branch_children)
                    .borrow_mut()
                    .iter()
                    .for_each(|child| {
                        let instance_id = (*(*child)).borrow_mut().get_instance_id();
                        instance_registry.deregister(instance_id);
                        instance_registry.mark_for_unmount(instance_id);
                    });
                self.cleanup_children = self.true_branch_children.clone();
            }
            self.boolean_expression.set(new_value);
        }
    }

    fn should_flatten(&self) -> bool {
        true
    }
    fn get_rendering_children(&self) -> RenderNodePtrList<R> {
        if *self.boolean_expression.get() {
            Rc::clone(&self.true_branch_children)
        } else {
            Rc::clone(&self.false_branch_children)
        }
    }
    fn pop_cleanup_children(&mut self) -> RenderNodePtrList<R> {
        let ret = self.cleanup_children.clone();
        self.cleanup_children = Rc::new(RefCell::new(vec![]));
        ret
    }
    fn get_size(&self) -> Option<(Size, Size)> {
        None
    }
    fn compute_size_within_bounds(&self, bounds: (f64, f64)) -> (f64, f64) {
        bounds
    }

    fn get_layer_type(&mut self) -> Layer {
        Layer::DontCare
    }
}