1use std::iter::once;
2
3use crate::Status;
4use itertools::chain;
5use ratatui::{
6 crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
7 prelude::*,
8 widgets::StatefulWidget,
9};
10
11pub trait Prompt: StatefulWidget {
13 fn draw(self, frame: &mut Frame, area: Rect, state: &mut Self::State);
21}
22
23#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Hash)]
25pub enum FocusState {
26 #[default]
27 Unfocused,
28 Focused,
29}
30
31pub trait State {
45 fn status(&self) -> Status;
47
48 fn status_mut(&mut self) -> &mut Status;
50
51 fn focus_state_mut(&mut self) -> &mut FocusState;
53
54 fn focus_state(&self) -> FocusState;
56
57 fn focus(&mut self) {
59 *self.focus_state_mut() = FocusState::Focused;
60 }
61
62 fn blur(&mut self) {
64 *self.focus_state_mut() = FocusState::Unfocused;
65 }
66
67 fn is_focused(&self) -> bool {
69 self.focus_state() == FocusState::Focused
70 }
71
72 fn position(&self) -> usize;
74
75 fn position_mut(&mut self) -> &mut usize;
77
78 fn cursor(&self) -> (u16, u16);
80
81 fn cursor_mut(&mut self) -> &mut (u16, u16);
83
84 fn value(&self) -> &str;
86
87 fn value_mut(&mut self) -> &mut String;
89
90 fn len(&self) -> usize {
91 self.value().chars().count()
92 }
93
94 fn is_empty(&self) -> bool {
95 self.value().len() == 0
96 }
97
98 fn handle_key_event(&mut self, key_event: KeyEvent) {
99 if key_event.kind == KeyEventKind::Release {
100 return;
101 }
102
103 match (key_event.code, key_event.modifiers) {
104 (KeyCode::Enter, _) => self.complete(),
105 (KeyCode::Esc, _) | (KeyCode::Char('c'), KeyModifiers::CONTROL) => self.abort(),
106 (KeyCode::Left, _) | (KeyCode::Char('b'), KeyModifiers::CONTROL) => self.move_left(),
107 (KeyCode::Right, _) | (KeyCode::Char('f'), KeyModifiers::CONTROL) => self.move_right(),
108 (KeyCode::Home, _) | (KeyCode::Char('a'), KeyModifiers::CONTROL) => self.move_start(),
109 (KeyCode::End, _) | (KeyCode::Char('e'), KeyModifiers::CONTROL) => self.move_end(),
110 (KeyCode::Backspace, _) | (KeyCode::Char('h'), KeyModifiers::CONTROL) => {
111 self.backspace();
112 }
113 (KeyCode::Delete, _) | (KeyCode::Char('d'), KeyModifiers::CONTROL) => self.delete(),
114 (KeyCode::Char('k'), KeyModifiers::CONTROL) => self.kill(),
115 (KeyCode::Char('u'), KeyModifiers::CONTROL) => self.truncate(),
116 (KeyCode::Char(c), KeyModifiers::NONE | KeyModifiers::SHIFT) => self.push(c),
117 _ => {}
118 }
119 }
120
121 fn complete(&mut self) {
122 *self.status_mut() = Status::Done;
123 }
124
125 fn abort(&mut self) {
126 *self.status_mut() = Status::Aborted;
127 }
128
129 fn delete(&mut self) {
130 let position = self.position();
131 if position == self.len() {
132 return;
133 }
134 *self.value_mut() = chain!(
135 self.value().chars().take(position),
136 self.value().chars().skip(position + 1)
137 )
138 .collect();
139 }
140
141 fn backspace(&mut self) {
142 let position = self.position();
143 if position == 0 {
144 return;
145 }
146 *self.value_mut() = chain!(
147 self.value().chars().take(position.saturating_sub(1)),
148 self.value().chars().skip(position)
149 )
150 .collect();
151 *self.position_mut() = position.saturating_sub(1);
152 }
153
154 fn move_right(&mut self) {
155 if self.position() == self.len() {
156 return;
157 }
158 *self.position_mut() = self.position().saturating_add(1);
159 }
160
161 fn move_left(&mut self) {
162 *self.position_mut() = self.position().saturating_sub(1);
163 }
164
165 fn move_end(&mut self) {
166 *self.position_mut() = self.len();
167 }
168
169 fn move_start(&mut self) {
170 *self.position_mut() = 0;
171 }
172
173 fn kill(&mut self) {
174 let position = self.position();
175 self.value_mut().truncate(position);
176 }
177
178 fn truncate(&mut self) {
179 self.value_mut().clear();
180 *self.position_mut() = 0;
181 }
182
183 fn push(&mut self, c: char) {
184 if self.position() == self.len() {
185 self.value_mut().push(c);
186 } else {
187 *self.value_mut() = chain![
191 self.value().chars().take(self.position()),
192 once(c),
193 self.value().chars().skip(self.position())
194 ]
195 .collect();
196 }
197 *self.position_mut() = self.position().saturating_add(1);
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn status_symbols() {
207 assert_eq!(Status::Pending.symbol(), "?".cyan());
208 assert_eq!(Status::Aborted.symbol(), "✘".red());
209 assert_eq!(Status::Done.symbol(), "✔".green());
210 }
211}