vtui_core/
runtime.rs

1use std::sync::mpsc::{Receiver, Sender};
2
3use ratatui::Frame;
4
5use crate::{component::Component, events::Message, listeners::DrawContext};
6
7pub struct Runtime {
8    root: Component,
9}
10
11impl Runtime {
12    pub fn new(root: Component) -> Self {
13        Self { root }
14    }
15
16    pub fn draw(&self, frame: &mut Frame) {
17        let mut ctx = DrawContext {
18            rect: frame.area(),
19            buf: frame.buffer_mut(),
20        };
21
22        self.root.render(&mut ctx)
23    }
24
25    pub fn update(&mut self, msg: Message) {
26        self.root.update(&msg)
27    }
28
29    pub fn should_exit(&self) -> bool {
30        false
31    }
32}
33
34pub struct EventSource {
35    tx: Sender<Message>,
36    rx: Receiver<Message>,
37}
38
39impl Default for EventSource {
40    fn default() -> Self {
41        let (tx, rx) = std::sync::mpsc::channel();
42        Self { tx, rx }
43    }
44}
45
46impl EventSource {
47    pub fn new() -> Self {
48        Self::default()
49    }
50
51    pub fn recv(&self) -> Message {
52        self.rx.recv().unwrap()
53    }
54
55    pub fn subscribe(&self, producer: &impl EventProducer) {
56        producer.spawn(self.tx.clone());
57    }
58}
59
60pub trait EventProducer {
61    fn subscribe(tx: Sender<Message>);
62
63    fn spawn(&self, tx: Sender<Message>) {
64        std::thread::spawn(move || {
65            Self::subscribe(tx);
66        });
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use std::{any::Any, cell::RefCell, rc::Rc};
73
74    use crate::{
75        component::Component,
76        events::{Message, Tick},
77        runtime::Runtime,
78    };
79
80    #[test]
81    fn test_event_listen() {
82        let mut root = Component::default();
83        let state = Rc::new(RefCell::new(false));
84        let state_c = state.clone();
85
86        root.listen::<Tick>(move |_| {
87            *state_c.borrow_mut() = true;
88        });
89
90        let mut runtime = Runtime::new(root);
91        let event = Tick {};
92        let message = Message {
93            type_id: event.type_id(),
94            event: Box::new(event),
95        };
96
97        assert!(!*state.borrow());
98
99        runtime.update(message);
100
101        assert!(*state.borrow());
102    }
103}