1use std::cell::{Ref, RefCell};
2use std::rc::Rc;
3
4use platform_core::Event;
5use reactive_core::batch;
6use renderer_core::DrawCommand;
7
8use crate::component::{Component, EventResult};
9use crate::segment::{self, Segment, SegmentRoot};
10
11pub struct ComponentList {
12 root: Rc<RefCell<dyn Component>>,
14 segment_root: SegmentRoot,
15}
16
17impl ComponentList {
18 pub fn new<C: Component + 'static>(component: C) -> Self {
19 let root: Rc<RefCell<dyn Component>> = Rc::new(RefCell::new(component));
20 let seg = Segment::mount_dyn(Rc::clone(&root));
21 Self {
22 root,
23 segment_root: SegmentRoot::from_segment(seg),
24 }
25 }
26
27 pub fn generation(&self) -> u64 {
29 self.segment_root.generation()
30 }
31
32 pub fn is_dirty(&self) -> bool {
33 self.segment_root.is_dirty()
34 }
35
36 pub fn commands(&self) -> Ref<'_, Vec<DrawCommand>> {
37 self.segment_root.commands()
38 }
39
40 pub fn walk_tree(&self, out: &mut Vec<segment::SegmentNodeInfo>) {
42 self.segment_root.walk(out);
43 }
44
45 pub fn on_event(&mut self, event: &Event) -> EventResult {
46 batch(|| self.root.borrow_mut().on_event(event))
53 }
54
55 pub fn bump_force_ticks(&self) {
57 batch(segment::bump_force_ticks);
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use geometry_core::Rect;
64 use reactive_core::signal;
65 use renderer_core::{Color, RectStyle, ShapeStyle};
66 use std::sync::Arc;
67
68 use super::*;
69 use crate::render_node::RenderNode;
70
71 fn sample_rect(x: f32) -> DrawCommand {
72 DrawCommand::Rect {
73 rect: Rect::new(x, 0.0, 10.0, 10.0),
74 style: Arc::new(RectStyle::default().with_fill(Color::BLACK)),
75 }
76 }
77
78 struct Fixed;
79
80 impl Component for Fixed {
81 fn view(&self) -> RenderNode {
82 RenderNode::group([
83 RenderNode::Primitive(sample_rect(0.0)),
84 RenderNode::Primitive(sample_rect(20.0)),
85 ])
86 }
87 }
88
89 #[test]
90 fn tree_initial_render() {
91 let tree = ComponentList::new(Fixed);
92 let cmds = tree.commands();
93 assert_eq!(cmds.len(), 2);
94 }
95
96 struct Counter {
97 value: reactive_core::RwSignal<i32>,
98 }
99
100 impl Component for Counter {
101 fn view(&self) -> RenderNode {
102 let n = self.value.get();
103 RenderNode::group((0..n).map(|i| RenderNode::Primitive(sample_rect(i as f32 * 10.0))))
104 }
105 }
106
107 #[test]
108 fn tree_reactive_update() {
109 let signal = signal(2i32);
110 let tree = ComponentList::new(Counter {
111 value: signal.clone(),
112 });
113
114 assert_eq!(tree.commands().len(), 2);
115
116 signal.set(5);
117 assert_eq!(tree.commands().len(), 5);
118
119 signal.set(0);
120 assert_eq!(tree.commands().len(), 0);
121 }
122}