Skip to main content

ratatui_kit/render/
tree.rs

1use 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    // 只跑一次 update(自顶向下运行组件、协调子树)。终端以对象安全的
46    // `&mut dyn UpdaterTerminal` 传入,故渲染 harness 可用 no-op 终端驱动。
47    pub(crate) fn update_once(&mut self, terminal: &mut dyn UpdaterTerminal) {
48        // 每帧重建输入注册表(清空上一帧层/handler、铸造 root 层)。
49        // 必须在 ContextStack::root 借走 &mut system_context 之前完成,二者借用不重叠。
50        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    // 只跑一次 draw(把树绘到给定 drawer)。供渲染 harness 直接画到 TestBackend Buffer。
57    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                // 组件树/状态变更:仅回到循环顶重渲染。
86                Either::Left(((), _)) => continue,
87                // 取到一个 raw 事件。
88                Either::Right((Some(event), _)) => {
89                    // ctrl_c 先于 dispatch 判定,任何层的 Consumed 都吞不掉它。
90                    if CrossTerminal::received_ctrl_c(event.clone()) {
91                        break;
92                    }
93                    self.system_context.input.dispatch(event);
94                    // dispatch 后无条件 continue:纯副作用/退出型 handler 不写 State 不唤醒,
95                    // 仍需回到循环顶 render + 复查 should_exit,否则会在 select 永久阻塞。
96                    continue;
97                }
98                // 事件流结束。
99                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}