Skip to main content

telar_ui_tree/
render_node.rs

1use std::cell::RefCell;
2use std::sync::Arc;
3
4use geometry_core::Rect;
5use renderer_core::{
6    BorderRadius, DrawCommand, PathData, PathStyle, RectStyle, TextRun, TextStyle,
7};
8
9thread_local! {
10    static NODE_VEC_POOL: RefCell<Vec<Vec<RenderNode>>> = const { RefCell::new(Vec::new()) };
11}
12
13pub struct NodeVec(Vec<RenderNode>);
14
15impl NodeVec {
16    pub fn collect(iter: impl IntoIterator<Item = RenderNode>) -> Self {
17        let mut v = NODE_VEC_POOL
18            .with_borrow_mut(|pool| pool.pop())
19            .unwrap_or_default();
20        v.extend(iter);
21        NodeVec(v)
22    }
23}
24
25impl Drop for NodeVec {
26    fn drop(&mut self) {
27        let mut v = std::mem::take(&mut self.0);
28        v.clear();
29        NODE_VEC_POOL.with_borrow_mut(|pool| {
30            if pool.len() < 32 {
31                pool.push(v);
32            }
33        });
34    }
35}
36
37impl std::ops::Deref for NodeVec {
38    type Target = [RenderNode];
39    fn deref(&self) -> &[RenderNode] {
40        &self.0
41    }
42}
43
44impl IntoIterator for NodeVec {
45    type Item = RenderNode;
46    type IntoIter = std::vec::IntoIter<RenderNode>;
47    fn into_iter(self) -> Self::IntoIter {
48        // ManuallyDrop suppresses NodeVec's Drop (which would return the vec to the pool) so we can move the inner vec out and iterate it instead.
49        let mut md = std::mem::ManuallyDrop::new(self);
50        let v = std::mem::take(&mut md.0);
51        v.into_iter()
52    }
53}
54
55pub enum RenderNode {
56    Empty,
57    Primitive(DrawCommand),
58    Group {
59        children: NodeVec,
60    },
61    Transform {
62        matrix: [f32; 6],
63        children: NodeVec,
64    },
65    Clip {
66        rect: Rect,
67        radius: BorderRadius,
68        children: NodeVec,
69    },
70    Layer {
71        opacity: f32,
72        backdrop_blur: f32,
73        children: NodeVec,
74    },
75    // A portal: its subtree is hoisted to the top layer at compose time (drawn last, above everything, and
76    // escaping any ancestor clip/transform/layer). Used for overlays — dropdowns, modals, drawers, toasts.
77    // Positioning is the caller's job (lay the content out where it should appear, e.g. an absolute-fill box).
78    Overlay {
79        children: NodeVec,
80    },
81    // A reactive boundary: the child segment maintains its own flattened commands via its own effect, so the parent's view() references it without re-running the child's view(). Composed lazily at collect time (see segment.rs). Enables O(changed component) updates instead of O(tree).
82    Boundary {
83        child: std::rc::Rc<crate::segment::Segment>,
84    },
85}
86
87impl RenderNode {
88    pub fn group(children: impl IntoIterator<Item = RenderNode>) -> Self {
89        Self::Group {
90            children: NodeVec::collect(children),
91        }
92    }
93
94    pub fn rect(rect: Rect, style: RectStyle) -> Self {
95        Self::Primitive(DrawCommand::Rect {
96            rect,
97            style: Arc::new(style),
98        })
99    }
100
101    pub fn text(text: impl Into<Arc<str>>, rect: Rect, style: TextStyle) -> Self {
102        Self::Primitive(DrawCommand::Text {
103            text: text.into(),
104            rect,
105            style: Arc::new(style),
106        })
107    }
108
109    pub fn rich_text(runs: Arc<[TextRun]>, rect: Rect, base: TextStyle) -> Self {
110        Self::Primitive(DrawCommand::RichText {
111            runs,
112            rect,
113            base: Arc::new(base),
114        })
115    }
116
117    pub fn path(data: Arc<PathData>, style: PathStyle) -> Self {
118        Self::Primitive(DrawCommand::Path {
119            data,
120            style: Arc::new(style),
121        })
122    }
123
124    pub fn transform_with(
125        matrix: [f32; 6],
126        children: impl IntoIterator<Item = RenderNode>,
127    ) -> Self {
128        Self::Transform {
129            matrix,
130            children: NodeVec::collect(children),
131        }
132    }
133
134    pub fn layer(
135        opacity: f32,
136        backdrop_blur: f32,
137        children: impl IntoIterator<Item = RenderNode>,
138    ) -> Self {
139        Self::Layer {
140            opacity,
141            backdrop_blur,
142            children: NodeVec::collect(children),
143        }
144    }
145
146    /// A portal whose subtree is hoisted to the top layer at compose time (see [`RenderNode::Overlay`]).
147    pub fn overlay(children: impl IntoIterator<Item = RenderNode>) -> Self {
148        Self::Overlay {
149            children: NodeVec::collect(children),
150        }
151    }
152}