ratatui_kit/render/
tree.rs1use futures::{
2 FutureExt,
3 future::{Either, select},
4};
5use std::io::{self};
6
7use crate::{
8 ElementKey,
9 component::{ComponentHelperExt, InstantiatedComponent},
10 context::{ContextStack, SystemContext},
11 element::ElementRepr,
12 props::AnyProps,
13 terminal::{CrossTerminal, Terminal, TerminalImpl, UpdaterTerminal},
14};
15
16use super::ComponentDrawer;
17
18struct RestoreGuard;
19
20impl Drop for RestoreGuard {
21 fn drop(&mut self) {
22 ratatui::restore();
23 }
24}
25
26pub struct Tree<'a> {
27 root_component: InstantiatedComponent,
28 props: AnyProps<'a>,
29 system_context: SystemContext,
30}
31
32impl<'a> Tree<'a> {
33 pub(crate) fn new(mut props: AnyProps<'a>, helper: Box<dyn ComponentHelperExt>) -> Self {
34 Tree {
35 root_component: InstantiatedComponent::new(
36 ElementKey::user("_root_tree_"),
37 props.borrow(),
38 helper,
39 ),
40 props,
41 system_context: SystemContext::new(),
42 }
43 }
44
45 pub(crate) fn update_once(&mut self, terminal: &mut dyn UpdaterTerminal) {
48 self.system_context.input.begin_frame();
51 let mut component_context_stack = ContextStack::root(&mut self.system_context);
52 self.root_component
53 .update(terminal, &mut component_context_stack, self.props.borrow());
54 }
55
56 pub(crate) fn draw_root(&mut self, drawer: &mut ComponentDrawer) {
58 self.root_component.draw(drawer);
59 }
60
61 fn render(&mut self, terminal: &mut Terminal) -> io::Result<()> {
62 self.update_once(terminal);
63
64 terminal.draw(|frame| {
65 let area = frame.area();
66 let mut drawer = ComponentDrawer::new(frame, area);
67 self.draw_root(&mut drawer);
68 })?;
69
70 Ok(())
71 }
72
73 async fn render_loop(&mut self, terminal: &mut Terminal) -> io::Result<()> {
74 loop {
75 self.render(terminal)?;
76 if self.system_context.should_exit() {
77 break;
78 }
79 match select(
80 self.root_component.wait().boxed_local(),
81 terminal.next_event().boxed_local(),
82 )
83 .await
84 {
85 Either::Left(((), _)) => continue,
87 Either::Right((Some(event), _)) => {
89 if CrossTerminal::received_ctrl_c(event.clone()) {
91 break;
92 }
93 self.system_context.input.dispatch(event);
94 continue;
97 }
98 Either::Right((None, _)) => break,
100 }
101 }
102 Ok(())
103 }
104}
105
106pub(crate) async fn render_loop<E: ElementRepr>(
107 mut element: E,
108 mut terminal: Terminal,
109) -> io::Result<()> {
110 let helper = element.helper();
111 let mut tree = Tree::new(element.props_mut(), helper);
112 let _restore_guard = RestoreGuard;
113
114 tree.render_loop(&mut terminal).await
115}