Skip to main content

telar_ui_core/
slots.rs

1use crate::layout_item::LayoutItem;
2
3/// The children a component receives from its call site, grouped by slot. A bare child lands in the
4/// default slot (`None`); a child written with `slot:"name"` lands in that named slot. Inside the
5/// component, the `children` placeholder drains the default slot and `children name:"x"` drains the
6/// `"x"` slot — each in call-site order. Draining is one-shot: a slot placeholder consumes its
7/// children, so referencing the same slot twice yields an empty list the second time.
8#[derive(Default)]
9pub struct Slots {
10    items: Vec<(Option<&'static str>, Box<dyn LayoutItem>)>,
11}
12
13impl Slots {
14    pub fn new() -> Self {
15        Self::default()
16    }
17
18    pub fn push(&mut self, name: Option<&'static str>, item: Box<dyn LayoutItem>) {
19        self.items.push((name, item));
20    }
21
22    /// Drains the default (unnamed) children in call-site order.
23    pub fn take_default(&mut self) -> Vec<Box<dyn LayoutItem>> {
24        self.take_matching(|n| n.is_none())
25    }
26
27    /// Drains the children assigned to the named slot `name`, in call-site order.
28    pub fn take(&mut self, name: &str) -> Vec<Box<dyn LayoutItem>> {
29        self.take_matching(|n| *n == Some(name))
30    }
31
32    fn take_matching(
33        &mut self,
34        pred: impl Fn(&Option<&'static str>) -> bool,
35    ) -> Vec<Box<dyn LayoutItem>> {
36        let mut taken = Vec::new();
37        let mut rest = Vec::new();
38        for (name, item) in std::mem::take(&mut self.items) {
39            if pred(&name) {
40                taken.push(item);
41            } else {
42                rest.push((name, item));
43            }
44        }
45        self.items = rest;
46        taken
47    }
48}