use std::iter::once;
use crate::Status;
use itertools::chain;
use ratatui::{
crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
prelude::*,
widgets::StatefulWidget,
};
pub trait Prompt: StatefulWidget {
fn draw(self, frame: &mut Frame, area: Rect, state: &mut Self::State);
}
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Hash)]
pub enum FocusState {
#[default]
Unfocused,
Focused,
}
pub trait State {
fn status(&self) -> Status;
fn status_mut(&mut self) -> &mut Status;
fn focus_state_mut(&mut self) -> &mut FocusState;
fn focus_state(&self) -> FocusState;
fn focus(&mut self) {
*self.focus_state_mut() = FocusState::Focused;
}
fn blur(&mut self) {
*self.focus_state_mut() = FocusState::Unfocused;
}
fn is_focused(&self) -> bool {
self.focus_state() == FocusState::Focused
}
fn position(&self) -> usize;
fn position_mut(&mut self) -> &mut usize;
fn cursor(&self) -> (u16, u16);
fn cursor_mut(&mut self) -> &mut (u16, u16);
fn value(&self) -> &str;
fn value_mut(&mut self) -> &mut String;
fn len(&self) -> usize {
self.value().chars().count()
}
fn is_empty(&self) -> bool {
self.value().len() == 0
}
fn handle_key_event(&mut self, key_event: KeyEvent) {
if key_event.kind == KeyEventKind::Release {
return;
}
match (key_event.code, key_event.modifiers) {
(KeyCode::Enter, _) => self.complete(),
(KeyCode::Esc, _) | (KeyCode::Char('c'), KeyModifiers::CONTROL) => self.abort(),
(KeyCode::Left, _) | (KeyCode::Char('b'), KeyModifiers::CONTROL) => self.move_left(),
(KeyCode::Right, _) | (KeyCode::Char('f'), KeyModifiers::CONTROL) => self.move_right(),
(KeyCode::Home, _) | (KeyCode::Char('a'), KeyModifiers::CONTROL) => self.move_start(),
(KeyCode::End, _) | (KeyCode::Char('e'), KeyModifiers::CONTROL) => self.move_end(),
(KeyCode::Backspace, _) | (KeyCode::Char('h'), KeyModifiers::CONTROL) => {
self.backspace();
}
(KeyCode::Delete, _) | (KeyCode::Char('d'), KeyModifiers::CONTROL) => self.delete(),
(KeyCode::Char('k'), KeyModifiers::CONTROL) => self.kill(),
(KeyCode::Char('u'), KeyModifiers::CONTROL) => self.truncate(),
(KeyCode::Char(c), KeyModifiers::NONE | KeyModifiers::SHIFT) => self.push(c),
_ => {}
}
}
fn complete(&mut self) {
*self.status_mut() = Status::Done;
}
fn abort(&mut self) {
*self.status_mut() = Status::Aborted;
}
fn delete(&mut self) {
let position = self.position();
if position == self.len() {
return;
}
*self.value_mut() = chain!(
self.value().chars().take(position),
self.value().chars().skip(position + 1)
)
.collect();
}
fn backspace(&mut self) {
let position = self.position();
if position == 0 {
return;
}
*self.value_mut() = chain!(
self.value().chars().take(position.saturating_sub(1)),
self.value().chars().skip(position)
)
.collect();
*self.position_mut() = position.saturating_sub(1);
}
fn move_right(&mut self) {
if self.position() == self.len() {
return;
}
*self.position_mut() = self.position().saturating_add(1);
}
fn move_left(&mut self) {
*self.position_mut() = self.position().saturating_sub(1);
}
fn move_end(&mut self) {
*self.position_mut() = self.len();
}
fn move_start(&mut self) {
*self.position_mut() = 0;
}
fn kill(&mut self) {
let position = self.position();
self.value_mut().truncate(position);
}
fn truncate(&mut self) {
self.value_mut().clear();
*self.position_mut() = 0;
}
fn push(&mut self, c: char) {
if self.position() == self.len() {
self.value_mut().push(c);
} else {
*self.value_mut() = chain![
self.value().chars().take(self.position()),
once(c),
self.value().chars().skip(self.position())
]
.collect();
}
*self.position_mut() = self.position().saturating_add(1);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn status_symbols() {
assert_eq!(Status::Pending.symbol(), "?".cyan());
assert_eq!(Status::Aborted.symbol(), "✘".red());
assert_eq!(Status::Done.symbol(), "✔".green());
}
}