multiverse/widgets/
popup_input.rs1use crossterm::event::KeyEvent;
2use ratatui::{
3 buffer::Buffer,
4 layout::{Constraint, Flex, Layout, Rect},
5 style::{Color, Stylize, palette::tailwind},
6 widgets::{Block, Borders, Clear, Widget},
7};
8use tui_textarea::TextArea;
9
10#[derive(Default, Clone)]
11pub(crate) struct PopupInputBuilder {
12 title: String,
14
15 placeholder_text: String,
17
18 width_constraint: Constraint,
20
21 height_constraint: Constraint,
23
24 border_set: Option<ratatui::symbols::border::Set>,
26
27 borders: Option<Borders>,
29
30 bg: Option<Color>,
32
33 fg: Option<Color>,
35}
36
37impl PopupInputBuilder {
38 pub fn new<T: Into<String>>(title: T, placeholder_text: T) -> Self {
40 Self {
41 title: title.into(),
42 placeholder_text: placeholder_text.into(),
43 width_constraint: Constraint::Percentage(40),
44 height_constraint: Constraint::Length(3),
45 ..Default::default()
46 }
47 }
48
49 pub fn width_constraint(&mut self, value: Constraint) -> &mut Self {
51 self.width_constraint = value;
52 self
53 }
54
55 pub fn height_constraint(&mut self, value: Constraint) -> &mut Self {
57 self.height_constraint = value;
58 self
59 }
60
61 pub fn border_set(&mut self, set: ratatui::symbols::border::Set) -> &mut Self {
63 self.border_set = Some(set);
64 self
65 }
66
67 pub fn borders(&mut self, borders: Borders) -> &mut Self {
69 self.borders = Some(borders);
70 self
71 }
72
73 pub fn bg(&mut self, color: Color) -> &mut Self {
75 self.bg = color.into();
76 self
77 }
78
79 pub fn build(&self) -> PopupInput {
81 let mut ret = PopupInput {
82 textarea: TextArea::default(),
83 height_constraint: self.height_constraint,
84 width_constraint: self.width_constraint,
85 };
86
87 ret.textarea.set_placeholder_text(self.placeholder_text.clone());
88
89 let border_set = self.border_set.unwrap_or_default();
90 let borders = self.borders.unwrap_or_default();
91 let bg = self.bg.unwrap_or(tailwind::BLUE.c400);
92 let fg = self.fg.unwrap_or(tailwind::GRAY.c50);
93
94 let input_block = Block::new()
95 .border_set(border_set)
96 .borders(borders)
97 .bg(bg)
98 .fg(fg)
99 .title(self.title.clone());
100
101 ret.textarea.set_block(input_block);
102
103 ret
104 }
105}
106
107#[derive(Debug, Default, Clone)]
108pub(crate) struct PopupInput {
109 textarea: TextArea<'static>,
111
112 height_constraint: Constraint,
114
115 width_constraint: Constraint,
117}
118
119impl PopupInput {
120 pub fn handle_key_press(&mut self, key: KeyEvent) {
122 self.textarea.input(key);
123 }
124
125 pub fn get_input(&self) -> String {
127 self.textarea.lines().join("\n")
128 }
129}
130
131impl Widget for &mut PopupInput {
132 fn render(self, area: Rect, buf: &mut Buffer) {
133 let vertical = Layout::vertical([self.height_constraint]).flex(Flex::Center);
134 let horizontal = Layout::horizontal([self.width_constraint]).flex(Flex::Center);
135 let [area] = vertical.areas(area);
136 let [area] = horizontal.areas(area);
137 Clear.render(area, buf);
138
139 self.textarea.render(area, buf);
140 }
141}