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 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 self.hooks.pre_component_draw(drawer);
139
140 self.component.draw(drawer);
142
143 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}