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
use core::option::Option;
use std::rc::Rc;
use crate::api::Layer;
use crate::{
declarative_macros::handle_vtable_update, 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,
}
///Contains the index value for slot, either a literal or an expression.
#[derive(Default)]
pub struct SlotProperties {
pub index: Box<dyn crate::api::PropertyInstance<crate::api::Numeric>>,
last_index: usize,
last_node_id: Option<u32>,
}
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 update(self: Rc<Self>, expanded_node: &Rc<ExpandedNode>, context: &mut RuntimeContext) {
expanded_node.with_properties_unwrapped(|properties: &mut SlotProperties| {
handle_vtable_update(
&context.expression_table(),
&expanded_node.stack,
&mut properties.index,
);
let index: usize = properties
.index
.get()
.get_as_int()
.try_into()
.expect("Slot index must be non-negative");
let node = expanded_node
.containing_component
.upgrade()
.as_ref()
.expect("slot has containing component during create")
.expanded_and_flattened_slot_children
.borrow()
.as_ref()
.and_then(|v| v.get(index))
.map(|v| Rc::clone(&v));
let node_id = node.as_ref().map(|n| n.id_chain[0]);
let update_child = properties.last_index != index || node_id != properties.last_node_id;
properties.last_node_id = node_id;
properties.last_index = index;
if update_child {
if let Some(node) = node {
expanded_node.attach_children(vec![Rc::clone(&node)], context);
} else {
expanded_node.set_children(vec![], context);
}
}
});
}
#[cfg(debug_assertions)]
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
}
}