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
110
111
112
113
114
115
116
117
use std::rc::{Rc, Weak};
use_RefCell!();

use pax_runtime_api::{use_RefCell, ImplToFromPaxAny, Numeric, Property};

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

/// A special "control-flow" primitive (a la `yield` or perhaps `goto`) — represents a slot into which
/// an slot_child can be rendered.  Slot relies on `slot_children` being present
/// on the [`Runtime`] stack and will not render any content if there are no `slot_children` found.
///
/// Consider a Stacker:  the owner of a Stacker passes the Stacker some nodes to render
/// inside the cells of the Stacker.  To the owner of the Stacker, those nodes might seem like
/// "children," but to the Stacker they are "slot_children" — children provided from
/// the outside.  Inside Stacker's template, there are a number of Slots — this primitive —
/// that become the final rendered home of those slot_children.  This same technique
/// is portable and applicable elsewhere via Slot.
pub struct SlotInstance {
    base: BaseInstance,
}

impl ImplToFromPaxAny for Slot {}

///Contains the index value for slot, either a literal or an expression.
#[derive(Default)]
pub struct Slot {
    // HACK: these two properties are being used in update:
    pub index: Property<Numeric>,
    pub last_node_id: Property<usize>,
    // to compute this:
    pub showing_node: Property<Weak<ExpandedNode>>,
}

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

    fn handle_mount(
        self: Rc<Self>,
        expanded_node: &Rc<ExpandedNode>,
        context: &Rc<RuntimeContext>,
    ) {
        let weak_ref_self = Rc::downgrade(expanded_node);
        let cloned_context = Rc::clone(context);

        // index should be renamed slot_node_id
        let showing_node = expanded_node
            .with_properties_unwrapped(|properties: &mut Slot| properties.showing_node.clone());
        let deps = vec![showing_node.untyped()];

        expanded_node
            .children
            .replace_with(Property::computed_with_name(
                move || {
                    let Some(cloned_expanded_node) = weak_ref_self.upgrade() else {
                        panic!("ran evaluator after expanded node dropped (repeat elem)")
                    };
                    cloned_expanded_node.attach_children(
                        showing_node.get().upgrade().as_slice().to_vec(),
                        &cloned_context,
                        &cloned_expanded_node.parent_frame,
                    )
                },
                &deps,
                &format!("slot_children (node id: {})", expanded_node.id.0),
            ));
    }

    fn update(self: Rc<Self>, expanded_node: &Rc<ExpandedNode>, _context: &Rc<RuntimeContext>) {
        let containing = expanded_node.containing_component.upgrade();
        let nodes = &containing
            .as_ref()
            .expect("slot to have a containing component")
            .expanded_and_flattened_slot_children;
        expanded_node.with_properties_unwrapped(|properties: &mut Slot| {
            let node_rc =
                nodes.read(|nodes| nodes.get(properties.index.get().to_int() as usize).cloned());
            let node = match &node_rc {
                Some(rc) => Rc::downgrade(rc),
                None => Weak::new(),
            };
            if properties.showing_node.get().upgrade().map(|v| v.id) != node.upgrade().map(|v| v.id)
            {
                properties.showing_node.set(node);
            }
        });
    }

    fn resolve_debug(
        &self,
        f: &mut std::fmt::Formatter,
        _expanded_node: Option<&ExpandedNode>,
    ) -> std::fmt::Result {
        f.debug_struct("Slot").finish()
    }

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