ratatui_kit/component/
instantiated_component.rs1use 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.layout_style.get_width(),
44 Direction::Vertical => c.layout_style.get_height(),
45 })
46 .collect()
47 }
48
49 fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
50 let mut is_ready = false;
51 for component in self.components.iter_mut() {
52 if Pin::new(component).poll_change(cx).is_ready() {
53 is_ready = true;
54 }
55 }
56
57 if is_ready {
58 Poll::Ready(())
59 } else {
60 Poll::Pending
61 }
62 }
63}
64
65pub struct InstantiatedComponent {
66 key: ElementKey,
67 hooks: Vec<Box<dyn AnyHook>>,
68 component: Box<dyn AnyComponent>,
69 helper: Box<dyn ComponentHelperExt>,
70 children: Components,
71 first_update: bool,
72 layout_style: LayoutStyle,
73 has_transparent_layout: bool,
74}
75
76impl InstantiatedComponent {
77 pub fn new(key: ElementKey, mut props: AnyProps, helper: Box<dyn ComponentHelperExt>) -> Self {
78 let component = helper.new_component(props.borrow());
79 Self {
80 key,
81 hooks: Default::default(),
82 layout_style: LayoutStyle::default(),
83 component,
84 children: Components::default(),
85 helper,
86 first_update: true,
87 has_transparent_layout: false,
88 }
89 }
90
91 pub fn component(&self) -> &dyn AnyComponent {
92 &*self.component
93 }
94
95 pub fn update(
96 &mut self,
97 terminal: &mut Terminal,
98 context_stack: &mut ContextStack,
99 mut props: AnyProps,
100 ) {
101 let mut updater = ComponentUpdater::new(
102 self.key.clone(),
103 context_stack,
104 terminal,
105 &mut self.children,
106 &mut self.layout_style,
107 );
108 self.hooks.pre_component_update(&mut updater);
109 self.helper.update_component(
110 &mut self.component,
111 props.borrow(),
112 Hooks::new(&mut self.hooks, self.first_update),
113 &mut updater,
114 );
115 self.hooks.post_component_update(&mut updater);
116 self.first_update = false;
117 self.has_transparent_layout = updater.has_transparent_layout();
118 }
119
120 pub fn draw(&mut self, drawer: &mut ComponentDrawer) {
121 let layout_style = &self.layout_style;
122
123 let area = if self.has_transparent_layout {
124 drawer.area
125 } else {
126 layout_style.inner_area(drawer.area)
127 };
128
129 drawer.area = area;
130
131 self.hooks.pre_component_draw(drawer);
133
134 self.component.draw(drawer);
136 let children_areas =
138 self.component
139 .calc_children_areas(&self.children, layout_style, drawer);
140
141 for (child, area) in self
142 .children
143 .components
144 .iter_mut()
145 .zip(children_areas.iter())
146 {
147 drawer.area = *area;
148 child.draw(drawer);
149 }
150 self.hooks.post_component_draw(drawer);
151 }
152
153 pub(crate) fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
154 let component_status = Pin::new(&mut *self.component).poll_change(cx);
155 let children_status = Pin::new(&mut self.children).poll_change(cx);
156 let hooks_status = Pin::new(&mut self.hooks).poll_change(cx);
157 if component_status.is_ready() || children_status.is_ready() || hooks_status.is_ready() {
158 Poll::Ready(())
159 } else {
160 Poll::Pending
161 }
162 }
163
164 pub async fn wait(&mut self) {
165 let mut self_mut = Pin::new(self);
166 poll_fn(|cx| self_mut.as_mut().poll_change(cx)).await;
167 }
168}