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}