ratatui_kit/component/
instantiated_component.rs

1use super::{AnyComponent, ComponentHelperExt};
2use crate::{
3    context::ContextStack,
4    element::ElementKey,
5    hooks::{AnyHook, Hook, Hooks},
6    multimap::RemoveOnlyMultimap,
7    props::AnyProps,
8    render::{ComponentDrawer, ComponentUpdater, layout_style::LayoutStyle},
9    terminal::Terminal,
10};
11use ratatui::layout::{Constraint, Direction};
12use std::{
13    future::poll_fn,
14    ops::{Deref, DerefMut},
15    pin::Pin,
16    task::{Context, Poll},
17};
18
19#[derive(Default)]
20pub struct Components {
21    pub components: RemoveOnlyMultimap<ElementKey, InstantiatedComponent>,
22}
23
24impl Deref for Components {
25    type Target = RemoveOnlyMultimap<ElementKey, InstantiatedComponent>;
26
27    fn deref(&self) -> &Self::Target {
28        &self.components
29    }
30}
31
32impl DerefMut for Components {
33    fn deref_mut(&mut self) -> &mut Self::Target {
34        &mut self.components
35    }
36}
37
38impl Components {
39    pub fn get_constraints(&self, direction: Direction) -> Vec<Constraint> {
40        self.components
41            .iter()
42            .map(|c| match direction {
43                Direction::Horizontal => c
44                    .layout_style
45                    .as_ref()
46                    .map(|style| style.get_width())
47                    .unwrap_or_default(),
48                Direction::Vertical => c
49                    .layout_style
50                    .as_ref()
51                    .map(|style| style.get_height())
52                    .unwrap_or_default(),
53            })
54            .collect()
55    }
56
57    fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
58        let mut is_ready = false;
59        for component in self.components.iter_mut() {
60            if Pin::new(component).poll_change(cx).is_ready() {
61                is_ready = true;
62            }
63        }
64
65        if is_ready {
66            Poll::Ready(())
67        } else {
68            Poll::Pending
69        }
70    }
71}
72
73pub struct InstantiatedComponent {
74    hooks: Vec<Box<dyn AnyHook>>,
75    component: Box<dyn AnyComponent>,
76    helper: Box<dyn ComponentHelperExt>,
77    children: Components,
78    first_update: bool,
79    layout_style: Option<LayoutStyle>,
80    has_transparent_layout: bool,
81}
82
83impl InstantiatedComponent {
84    pub fn new(mut props: AnyProps, helper: Box<dyn ComponentHelperExt>) -> Self {
85        let component = helper.new_component(props.borrow());
86        Self {
87            hooks: Default::default(),
88            layout_style: None,
89            component,
90            children: Components::default(),
91            helper,
92            first_update: true,
93            has_transparent_layout: false,
94        }
95    }
96
97    pub fn component(&self) -> &dyn AnyComponent {
98        &*self.component
99    }
100
101    pub fn update(
102        &mut self,
103        terminal: &mut Terminal,
104        context_stack: &mut ContextStack,
105        mut props: AnyProps,
106    ) {
107        let mut updater = ComponentUpdater::new(
108            context_stack,
109            terminal,
110            &mut self.children,
111            &mut self.layout_style,
112        );
113        self.hooks.pre_component_update(&mut updater);
114        self.helper.update_component(
115            &mut self.component,
116            props.borrow(),
117            Hooks::new(&mut self.hooks, self.first_update),
118            &mut updater,
119        );
120        self.hooks.post_component_update(&mut updater);
121        self.first_update = false;
122        self.has_transparent_layout = updater.has_transparent_layout();
123    }
124
125    pub fn draw(&mut self, drawer: &mut ComponentDrawer) {
126        let default_layout_style = LayoutStyle::default();
127        let layout_style = self.layout_style.as_ref().unwrap_or(&default_layout_style);
128
129        let area = if self.has_transparent_layout {
130            drawer.area
131        } else {
132            layout_style.inner_area(drawer.area)
133        };
134
135        drawer.area = area;
136
137        // 先渲染在计算子组件的areas
138        self.hooks.pre_component_draw(drawer);
139
140        // drawer.ares可能在组件绘制时改变
141        self.component.draw(drawer);
142
143        // 更新子组件的区域
144        self.component
145            .update_children_areas(&self.children, layout_style, drawer);
146
147        let children_areas = drawer.children_areas.clone();
148
149        for (child, area) in self
150            .children
151            .components
152            .iter_mut()
153            .zip(children_areas.iter())
154        {
155            drawer.area = *area;
156            child.draw(drawer);
157        }
158        self.hooks.post_component_draw(drawer);
159    }
160
161    pub(crate) fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
162        let component_status = Pin::new(&mut *self.component).poll_change(cx);
163        let children_status = Pin::new(&mut self.children).poll_change(cx);
164        let hooks_status = Pin::new(&mut self.hooks).poll_change(cx);
165        if component_status.is_ready() || children_status.is_ready() || hooks_status.is_ready() {
166            Poll::Ready(())
167        } else {
168            Poll::Pending
169        }
170    }
171
172    pub async fn wait(&mut self) {
173        let mut self_mut = Pin::new(self);
174        poll_fn(|cx| self_mut.as_mut().poll_change(cx)).await;
175    }
176}