ratatui_kit/component/
mod.rs

1use crate::{
2    element::ElementType,
3    hooks::Hooks,
4    layout_style::LayoutStyle,
5    props::{AnyProps, Props},
6    render::{ComponentDrawer, ComponentUpdater},
7};
8use std::{any::Any, pin::Pin, task::Context};
9
10mod component_helper;
11pub(crate) use component_helper::{ComponentHelper, ComponentHelperExt};
12
13mod instantiated_component;
14pub use instantiated_component::{Components, InstantiatedComponent};
15use ratatui::layout::{Direction, Layout};
16
17/// 组件系统核心 trait,所有自定义 UI 组件都需实现。
18///
19/// - 通过关联类型 `Props` 定义属性类型,支持生命周期。
20/// - `new` 创建组件实例。
21/// - `update` 响应 props/hook 变化,适合副作用、事件注册等。
22/// - `draw` 渲染组件内容。
23/// - `calc_children_areas` 默认 flex 布局计算子组件区域,可重写自定义布局。
24/// - `poll_change` 支持异步/响应式副作用。
25/// - `render_ref` 低级渲染接口,通常无需重写。
26///
27/// # 手动实现 Component 示例
28///
29/// ```rust
30/// use ratatui_kit::prelude::*;
31/// use ratatui::{style::Style, text::Line};
32///
33/// pub struct MyCounter;
34///
35/// impl Component for MyCounter {
36///     type Props<'a> = NoProps;
37///     fn new(_props: &Self::Props<'_>) -> Self {
38///         Self
39///     }
40///     fn update(
41///         &mut self,
42///         _props: &mut Self::Props<'_>,
43///         mut hooks: Hooks,
44///         _updater: &mut ComponentUpdater,
45///     ) {
46///         let mut state = hooks.use_state(|| 0);
47///         hooks.use_events(move |event| {
48///             // 事件处理逻辑
49///         });
50///         // ...
51///     }
52///     fn draw(&mut self, drawer: &mut ComponentDrawer<'_, '_>) {
53///         let area = drawer.area;
54///         let buf = drawer.buffer_mut();
55///         Line::styled(format!("Counter: {}", 42), Style::default()).render(area, buf);
56///     }
57/// }
58/// ```
59///
60/// > 一般用户无需手动实现,推荐使用 `#[component]` 宏自动生成。
61pub trait Component: Any + Send + Sync + Unpin {
62    type Props<'a>: Props
63    where
64        Self: 'a;
65
66    fn new(props: &Self::Props<'_>) -> Self;
67
68    fn update(
69        &mut self,
70        _props: &mut Self::Props<'_>,
71        _hooks: Hooks,
72        _updater: &mut ComponentUpdater,
73    ) {
74    }
75
76    fn draw(&mut self, drawer: &mut ComponentDrawer<'_, '_>) {
77        self.render_ref(drawer.area, drawer.buffer_mut());
78    }
79
80    // 默认使用flex布局计算子组件的area
81    fn calc_children_areas(
82        &self,
83        children: &Components,
84        layout_style: &LayoutStyle,
85        drawer: &mut ComponentDrawer<'_, '_>,
86    ) -> Vec<ratatui::prelude::Rect> {
87        let layout = layout_style
88            .get_layout()
89            .constraints(children.get_constraints(layout_style.flex_direction));
90
91        let areas = layout.split(drawer.area);
92
93        let mut children_areas: Vec<ratatui::prelude::Rect> = vec![];
94
95        let rev_direction = match layout_style.flex_direction {
96            Direction::Horizontal => Direction::Vertical,
97            Direction::Vertical => Direction::Horizontal,
98        };
99        for (area, constraint) in areas.iter().zip(children.get_constraints(rev_direction)) {
100            let area = Layout::new(rev_direction, [constraint]).split(*area)[0];
101            children_areas.push(area);
102        }
103
104        children_areas
105    }
106
107    fn poll_change(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> std::task::Poll<()> {
108        std::task::Poll::Pending
109    }
110
111    fn render_ref(&self, _area: ratatui::layout::Rect, _buf: &mut ratatui::buffer::Buffer) {}
112}
113
114pub trait AnyComponent: Any + Send + Sync + Unpin {
115    fn update(&mut self, props: AnyProps, hooks: Hooks, updater: &mut ComponentUpdater);
116
117    fn draw(&mut self, drawer: &mut ComponentDrawer);
118
119    fn calc_children_areas(
120        &self,
121        children: &Components,
122        layout_style: &LayoutStyle,
123        drawer: &mut ComponentDrawer,
124    ) -> Vec<ratatui::prelude::Rect>;
125
126    fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> std::task::Poll<()>;
127
128    fn render_ref(&self, area: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer);
129}
130
131impl<C> ElementType for C
132where
133    C: Component,
134{
135    type Props<'a> = C::Props<'a>;
136}
137
138impl<C> AnyComponent for C
139where
140    C: Any + Component,
141{
142    fn update(&mut self, mut props: AnyProps, hooks: Hooks, updater: &mut ComponentUpdater) {
143        Component::update(
144            self,
145            unsafe { props.downcast_mut_unchecked() },
146            hooks,
147            updater,
148        );
149    }
150
151    fn draw(&mut self, drawer: &mut ComponentDrawer) {
152        Component::draw(self, drawer);
153    }
154
155    fn calc_children_areas(
156        &self,
157        children: &Components,
158        layout_style: &LayoutStyle,
159        drawer: &mut ComponentDrawer,
160    ) -> Vec<ratatui::prelude::Rect> {
161        Component::calc_children_areas(self, children, layout_style, drawer)
162    }
163
164    fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> std::task::Poll<()> {
165        Component::poll_change(self, cx)
166    }
167
168    fn render_ref(&self, area: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) {
169        Component::render_ref(self, area, buf);
170    }
171}