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
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 key: ElementKey,
75 hooks: Vec<Box<dyn AnyHook>>,
76 component: Box<dyn AnyComponent>,
77 helper: Box<dyn ComponentHelperExt>,
78 children: Components,
79 first_update: bool,
80 layout_style: Option<LayoutStyle>,
81 has_transparent_layout: bool,
82}
83
84impl InstantiatedComponent {
85 pub fn new(key: ElementKey, mut props: AnyProps, helper: Box<dyn ComponentHelperExt>) -> Self {
86 let component = helper.new_component(props.borrow());
87 Self {
88 key,
89 hooks: Default::default(),
90 layout_style: None,
91 component,
92 children: Components::default(),
93 helper,
94 first_update: true,
95 has_transparent_layout: false,
96 }
97 }
98
99 pub fn component(&self) -> &dyn AnyComponent {
100 &*self.component
101 }
102
103 pub fn update(
104 &mut self,
105 terminal: &mut Terminal,
106 context_stack: &mut ContextStack,
107 mut props: AnyProps,
108 ) {
109 let mut updater = ComponentUpdater::new(
110 self.key.clone(),
111 context_stack,
112 terminal,
113 &mut self.children,
114 &mut self.layout_style,
115 );
116 self.hooks.pre_component_update(&mut updater);
117 self.helper.update_component(
118 &mut self.component,
119 props.borrow(),
120 Hooks::new(&mut self.hooks, self.first_update),
121 &mut updater,
122 );
123 self.hooks.post_component_update(&mut updater);
124 self.first_update = false;
125 self.has_transparent_layout = updater.has_transparent_layout();
126 }
127
128 pub fn draw(&mut self, drawer: &mut ComponentDrawer) {
129 let default_layout_style = LayoutStyle::default();
130 let layout_style = self.layout_style.as_ref().unwrap_or(&default_layout_style);
131
132 let area = if self.has_transparent_layout {
133 drawer.area
134 } else {
135 layout_style.inner_area(drawer.area)
136 };
137
138 drawer.area = area;
139
140 self.hooks.pre_component_draw(drawer);
142
143 self.component.draw(drawer);
145
146 self.component
148 .update_children_areas(&self.children, layout_style, drawer);
149
150 let children_areas = drawer.children_areas.clone();
151
152 for (child, area) in self
153 .children
154 .components
155 .iter_mut()
156 .zip(children_areas.iter())
157 {
158 drawer.area = *area;
159 child.draw(drawer);
160 }
161 self.hooks.post_component_draw(drawer);
162 }
163
164 pub(crate) fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
165 let component_status = Pin::new(&mut *self.component).poll_change(cx);
166 let children_status = Pin::new(&mut self.children).poll_change(cx);
167 let hooks_status = Pin::new(&mut self.hooks).poll_change(cx);
168 if component_status.is_ready() || children_status.is_ready() || hooks_status.is_ready() {
169 Poll::Ready(())
170 } else {
171 Poll::Pending
172 }
173 }
174
175 pub async fn wait(&mut self) {
176 let mut self_mut = Pin::new(self);
177 poll_fn(|cx| self_mut.as_mut().poll_change(cx)).await;
178 }
179}