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
17pub trait Component: Any + Send + Sync + Unpin {
18    type Props<'a>: Props
19    where
20        Self: 'a;
21
22    fn new(props: &Self::Props<'_>) -> Self;
23
24    fn update(
25        &mut self,
26        _props: &mut Self::Props<'_>,
27        _hooks: Hooks,
28        _updater: &mut ComponentUpdater,
29    ) {
30    }
31
32    fn draw(&mut self, drawer: &mut ComponentDrawer<'_, '_>) {
33        self.render_ref(drawer.area, drawer.buffer_mut());
34    }
35
36    // 默认使用flex布局计算子组件的area
37    fn calc_children_areas(
38        &self,
39        children: &Components,
40        layout_style: &LayoutStyle,
41        drawer: &mut ComponentDrawer<'_, '_>,
42    ) -> Vec<ratatui::prelude::Rect> {
43        let layout = layout_style
44            .get_layout()
45            .constraints(children.get_constraints(layout_style.flex_direction));
46
47        let areas = layout.split(drawer.area);
48
49        let mut children_areas: Vec<ratatui::prelude::Rect> = vec![];
50
51        let rev_direction = match layout_style.flex_direction {
52            Direction::Horizontal => Direction::Vertical,
53            Direction::Vertical => Direction::Horizontal,
54        };
55        for (area, constraint) in areas.iter().zip(children.get_constraints(rev_direction)) {
56            let area = Layout::new(rev_direction, [constraint]).split(*area)[0];
57            children_areas.push(area);
58        }
59
60        children_areas
61    }
62
63    fn poll_change(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> std::task::Poll<()> {
64        std::task::Poll::Pending
65    }
66
67    fn render_ref(&self, _area: ratatui::layout::Rect, _buf: &mut ratatui::buffer::Buffer) {}
68}
69
70pub trait AnyComponent: Any + Send + Sync + Unpin {
71    fn update(&mut self, props: AnyProps, hooks: Hooks, updater: &mut ComponentUpdater);
72
73    fn draw(&mut self, drawer: &mut ComponentDrawer);
74
75    fn calc_children_areas(
76        &self,
77        children: &Components,
78        layout_style: &LayoutStyle,
79        drawer: &mut ComponentDrawer,
80    ) -> Vec<ratatui::prelude::Rect>;
81
82    fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> std::task::Poll<()>;
83
84    fn render_ref(&self, area: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer);
85}
86
87impl<C> ElementType for C
88where
89    C: Component,
90{
91    type Props<'a> = C::Props<'a>;
92}
93
94impl<C> AnyComponent for C
95where
96    C: Any + Component,
97{
98    fn update(&mut self, mut props: AnyProps, hooks: Hooks, updater: &mut ComponentUpdater) {
99        Component::update(
100            self,
101            unsafe { props.downcast_mut_unchecked() },
102            hooks,
103            updater,
104        );
105    }
106
107    fn draw(&mut self, drawer: &mut ComponentDrawer) {
108        Component::draw(self, drawer);
109    }
110
111    fn calc_children_areas(
112        &self,
113        children: &Components,
114        layout_style: &LayoutStyle,
115        drawer: &mut ComponentDrawer,
116    ) -> Vec<ratatui::prelude::Rect> {
117        Component::calc_children_areas(self, children, layout_style, drawer)
118    }
119
120    fn poll_change(self: Pin<&mut Self>, cx: &mut Context) -> std::task::Poll<()> {
121        Component::poll_change(self, cx)
122    }
123
124    fn render_ref(&self, area: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) {
125        Component::render_ref(self, area, buf);
126    }
127}