vtui_core/
runtime.rs

1use std::time::{Duration, Instant};
2
3use ratatui::prelude::Backend;
4
5use crate::{
6    arena::Arena,
7    component::{FactoryFn, Node},
8    context::Context,
9    driver::Driver,
10    error::RuntimeError,
11    events::Message,
12    transport::EventSource,
13};
14
15pub struct Runtime {
16    arena: Arena,
17    context: Context,
18    source: EventSource,
19}
20
21impl Runtime {
22    pub fn new(factory: FactoryFn<()>, source: EventSource) -> Self {
23        let context = Context::default();
24        let root = Node::from_factory(factory, ());
25        let arena = Arena::new(root);
26
27        Self {
28            arena,
29            context,
30            source,
31        }
32    }
33
34    pub fn draw<D>(&mut self, driver: &mut D) -> Result<(), RuntimeError>
35    where
36        D: Driver,
37        RuntimeError: From<<D::Backend as Backend>::Error>,
38    {
39        let terminal = driver.terminal();
40
41        terminal.draw(|f| {
42            let rect = f.area().into();
43            let buffer = f.buffer_mut();
44
45            self.arena.draw_for_each(rect, |node| {
46                node.render(buffer);
47            });
48        })?;
49
50        Ok(())
51    }
52
53    pub fn update(&mut self) {
54        let deadline = Instant::now() + Duration::from_millis(16);
55        let msg = self.source.recv();
56
57        self.dispatch(&msg);
58
59        while Instant::now() < deadline {
60            let msg = self.source.recv_timeout(deadline - Instant::now());
61
62            if let Some(msg) = msg {
63                self.dispatch(&msg);
64            }
65        }
66    }
67
68    pub fn should_exit(&self) -> bool {
69        self.context.shutdown_requested
70    }
71}
72
73impl Runtime {
74    fn dispatch(&mut self, msg: &Message) {
75        self.arena.update_for_each(|node| {
76            node.dispatch(msg, &mut self.context);
77        })
78    }
79}