use crossterm::event::{Event, KeyEvent};
use tui_input::{backend::crossterm::EventHandler, Input};
pub trait InputManager: Send + Sync {
fn get_text(&self) -> String;
fn set_text(&mut self, text: String);
fn get_cursor_position(&self) -> usize;
fn set_cursor_position(&mut self, position: usize);
fn handle_key_event(&mut self, event: KeyEvent) -> bool;
fn clear(&mut self);
fn is_empty(&self) -> bool;
fn get_visual_cursor(&self) -> (u16, u16);
fn is_multiline(&self) -> bool;
fn line_count(&self) -> usize;
fn get_line(&self, index: usize) -> Option<String>;
fn clone_box(&self) -> Box<dyn InputManager>;
fn set_history(&mut self, history: Vec<String>);
fn history_previous(&mut self) -> bool;
fn history_next(&mut self) -> bool;
fn get_history_index(&self) -> Option<usize>;
fn reset_history_position(&mut self);
}
pub struct SingleLineInput {
input: Input,
history: Vec<String>,
history_index: Option<usize>,
temp_storage: Option<String>, }
impl SingleLineInput {
#[must_use]
pub fn new(text: String) -> Self {
let input = Input::new(text.clone()).with_cursor(text.len());
Self {
input,
history: Vec::new(),
history_index: None,
temp_storage: None,
}
}
#[must_use]
pub fn from_input(input: Input) -> Self {
Self {
input,
history: Vec::new(),
history_index: None,
temp_storage: None,
}
}
#[must_use]
pub fn as_input(&self) -> &Input {
&self.input
}
pub fn as_input_mut(&mut self) -> &mut Input {
&mut self.input
}
}
impl InputManager for SingleLineInput {
fn get_text(&self) -> String {
self.input.value().to_string()
}
fn set_text(&mut self, text: String) {
let cursor_pos = text.len();
self.input = Input::new(text).with_cursor(cursor_pos);
}
fn get_cursor_position(&self) -> usize {
self.input.visual_cursor()
}
fn set_cursor_position(&mut self, position: usize) {
let text = self.input.value().to_string();
self.input = Input::new(text).with_cursor(position);
}
fn handle_key_event(&mut self, event: KeyEvent) -> bool {
let crossterm_event = Event::Key(event);
self.input.handle_event(&crossterm_event);
true
}
fn clear(&mut self) {
self.input = Input::default();
}
fn is_empty(&self) -> bool {
self.input.value().is_empty()
}
fn get_visual_cursor(&self) -> (u16, u16) {
(0, self.input.visual_cursor() as u16)
}
fn is_multiline(&self) -> bool {
false
}
fn line_count(&self) -> usize {
1
}
fn get_line(&self, index: usize) -> Option<String> {
if index == 0 {
Some(self.get_text())
} else {
None
}
}
fn clone_box(&self) -> Box<dyn InputManager> {
Box::new(SingleLineInput {
input: self.input.clone(),
history: self.history.clone(),
history_index: self.history_index,
temp_storage: self.temp_storage.clone(),
})
}
fn set_history(&mut self, history: Vec<String>) {
self.history = history;
self.history_index = None;
self.temp_storage = None;
}
fn history_previous(&mut self) -> bool {
if self.history.is_empty() {
return false;
}
match self.history_index {
None => {
self.temp_storage = Some(self.input.value().to_string());
self.history_index = Some(self.history.len() - 1);
}
Some(0) => return false, Some(idx) => {
self.history_index = Some(idx - 1);
}
}
if let Some(idx) = self.history_index {
if let Some(entry) = self.history.get(idx) {
let len = entry.len();
self.input = Input::new(entry.clone()).with_cursor(len);
return true;
}
}
false
}
fn history_next(&mut self) -> bool {
match self.history_index {
None => false, Some(idx) => {
if idx >= self.history.len() - 1 {
if let Some(temp) = &self.temp_storage {
let len = temp.len();
self.input = Input::new(temp.clone()).with_cursor(len);
}
self.history_index = None;
self.temp_storage = None;
true
} else {
self.history_index = Some(idx + 1);
if let Some(entry) = self.history.get(idx + 1) {
let len = entry.len();
self.input = Input::new(entry.clone()).with_cursor(len);
true
} else {
false
}
}
}
}
}
fn get_history_index(&self) -> Option<usize> {
self.history_index
}
fn reset_history_position(&mut self) {
self.history_index = None;
self.temp_storage = None;
}
}
#[must_use]
pub fn create_single_line(text: String) -> Box<dyn InputManager> {
Box::new(SingleLineInput::new(text))
}
#[must_use]
pub fn create_from_input(input: Input) -> Box<dyn InputManager> {
Box::new(SingleLineInput::from_input(input))
}