hefesto_widgets/popups/text_input_popup/
mod.rs1use ratatui::{
2 buffer::Buffer,
3 layout::{Constraint, Layout, Rect},
4 style::{Color, Style},
5 widgets::{Borders, StatefulWidget},
6};
7
8use crate::popup::{Popup, PopupSize};
9use crate::popups::AutoSized;
10use crate::text_input::{TextInput, TextInputState};
11use crate::{BadgeStack, BorderType, DotPattern};
12
13#[derive(Clone)]
21pub struct TextInputPopup<'a> {
22 popup: Popup<'a>,
23 text_input: TextInput<'a>,
24}
25
26impl<'a> TextInputPopup<'a> {
27 pub fn new() -> Self {
33 Self {
34 popup: Popup::new(Color::White)
35 .padding(0)
36 .border_type(BorderType::Rounded),
37 text_input: TextInput::new(),
38 }
39 }
40
41 pub fn title(mut self, title: &'a str) -> Self {
42 self.popup = self.popup.title(title);
43 self
44 }
45
46 pub fn cursor_style(mut self, style: Style) -> Self {
47 self.text_input = self.text_input.cursor_style(style);
48 self
49 }
50
51 pub fn text_style(mut self, style: Style) -> Self {
52 self.text_input = self.text_input.text_style(style);
53 self
54 }
55
56 pub fn border_style(mut self, style: Style) -> Self {
57 self.text_input = self.text_input.border_style(style);
58 self
59 }
60
61 pub fn borders(mut self, borders: Borders) -> Self {
62 self.text_input = self.text_input.borders(borders);
63 self
64 }
65
66 pub fn input_bg_color(mut self, color: Color) -> Self {
69 self.text_input = self.text_input.bg_color(color);
70 self
71 }
72
73 pub fn bg_color(mut self, color: Color) -> Self {
77 self.popup = self.popup.bg_color(color);
78 self
79 }
80
81 pub fn scroll_padding(mut self, padding: u16) -> Self {
82 self.text_input = self.text_input.scroll_padding(padding);
83 self
84 }
85
86 pub fn scroll_reserve(mut self, reserve: u16) -> Self {
87 self.text_input = self.text_input.scroll_reserve(reserve);
88 self
89 }
90
91 pub fn rows(mut self, rows: u16) -> Self {
92 self.text_input = self.text_input.rows(rows);
93 self
94 }
95
96 pub fn placeholder(mut self, placeholder: &'a str) -> Self {
97 self.text_input = self.text_input.placeholder(placeholder);
98 self
99 }
100
101 pub fn border_color(mut self, color: Color) -> Self {
102 self.popup = self.popup.border_color(color);
103 self
104 }
105
106 pub fn border_type(mut self, bt: BorderType) -> Self {
107 self.popup = self.popup.border_type(bt);
108 self
109 }
110
111 pub fn width(mut self, w: PopupSize) -> Self {
112 self.popup = self.popup.width(w);
113 self
114 }
115
116 pub fn height(mut self, h: PopupSize) -> Self {
117 self.popup = self.popup.height(h);
118 self
119 }
120
121 pub fn position(mut self, x: u16, y: u16) -> Self {
122 self.popup = self.popup.position(x, y);
123 self
124 }
125
126 pub fn origin(mut self, x: u16, y: u16) -> Self {
127 self.popup = self.popup.origin(x, y);
128 self
129 }
130
131 pub fn header(mut self) -> Self {
132 self.popup = self.popup.header();
133 self
134 }
135
136 pub fn no_background(mut self) -> Self {
137 self.popup = self.popup.no_background();
138 self
139 }
140
141 pub fn background_dots(mut self, color: Color, symbol: &str, density: u16) -> Self {
142 self.popup = self.popup.background_dots(color, symbol, density);
143 self
144 }
145
146 pub fn background_pattern(mut self, pattern: DotPattern) -> Self {
147 self.popup = self.popup.background_pattern(pattern);
148 self
149 }
150
151 pub fn background_spacing(mut self, density_x: u16, density_y: u16) -> Self {
152 self.popup = self.popup.background_spacing(density_x, density_y);
153 self
154 }
155
156 pub fn badges(mut self, badges: BadgeStack<'a>) -> Self {
157 self.popup = self.popup.badges(badges);
158 self
159 }
160}
161
162impl AutoSized for TextInputPopup<'_> {
163 type State = TextInputState;
164
165 fn auto_height(&self, _state: &Self::State, _area: Rect) -> u16 {
166 let header_extra = if self.popup.has_header() { 2 } else { 0 };
167 self.text_input.required_height() + 2 + header_extra
168 }
169}
170
171impl<'a> TextInputPopup<'a> {
172 pub fn resolve_rect(&self, area: Rect, state: &TextInputState) -> Rect {
177 if self.popup.get_height() == PopupSize::Auto {
178 self.popup
179 .clone()
180 .height(PopupSize::Fixed(self.auto_height(state, area)))
181 .resolve_rect(area)
182 } else {
183 self.popup.resolve_rect(area)
184 }
185 }
186}
187
188impl StatefulWidget for TextInputPopup<'_> {
189 type State = TextInputState;
190
191 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
192 let input_height = self.text_input.required_height();
193
194 let auto_h = if self.popup.get_height() == PopupSize::Auto {
195 Some(self.auto_height(state, area))
196 } else {
197 None
198 };
199 let mut popup = self.popup;
200 if let Some(h) = auto_h {
201 popup = popup.height(PopupSize::Fixed(h));
202 }
203 let inner = popup.render_inner(area, buf);
204
205 let chunks = Layout::vertical([
207 Constraint::Min(0),
208 Constraint::Length(input_height),
209 Constraint::Min(0),
210 ]).split(inner);
211
212 self.text_input.render(chunks[1], buf, state);
213 }
214}
215
216#[cfg(test)]
217mod tests;