use {
crate::{
Area,
CompoundStyle,
Error,
Event,
fit,
},
std::io::Write,
crossterm::{
cursor,
event::{
KeyCode,
KeyEvent,
KeyModifiers,
},
queue,
style::{
Attribute,
Color,
SetBackgroundColor,
},
},
};
pub struct InputField {
content: Vec<char>,
cursor_pos: usize, pub area: Area,
normal_style: CompoundStyle,
cursor_style: CompoundStyle,
pub focused: bool,
}
impl InputField {
pub fn new(area: Area) -> Self {
debug_assert!(area.height == 1, "input area must be of height 1");
let normal_style = CompoundStyle::default();
let mut cursor_style = normal_style.clone();
cursor_style.add_attr(Attribute::Reverse);
let focused = true;
Self {
content: Vec::new(),
area,
cursor_pos: 0,
normal_style,
cursor_style,
focused,
}
}
pub fn change_area(&mut self, x: u16, y: u16, w: u16) {
self.area.left = x;
self.area.top = y;
self.area.width = w;
}
pub fn set_normal_style(&mut self, style: CompoundStyle) {
self.normal_style = style;
self.cursor_style = self.normal_style.clone();
self.cursor_style.add_attr(Attribute::Reverse);
}
pub fn get_content(&self) -> String {
self.content.iter().collect()
}
pub fn is_content(&self, s: &str) -> bool {
let str_content = self.get_content();
str_content == s
}
pub fn set_content(&mut self, s: &str) {
if self.is_content(s) {
return;
}
self.content = s.chars().collect();
self.cursor_pos = self.content.len();
}
pub fn put_char(&mut self, c: char) -> bool {
self.content.insert(self.cursor_pos, c);
self.cursor_pos += 1;
true
}
pub fn del_char_left(&mut self) -> bool {
if self.cursor_pos > 0 {
self.cursor_pos -= 1;
self.content.remove(self.cursor_pos);
true
} else {
false
}
}
pub fn del_char_below(&mut self) -> bool {
if self.cursor_pos < self.content.len() {
self.content.remove(self.cursor_pos);
true
} else {
false
}
}
pub fn move_right(&mut self) -> bool {
if self.cursor_pos < self.content.len() {
self.cursor_pos += 1;
true
} else {
false
}
}
pub fn move_left(&mut self) -> bool {
if self.cursor_pos > 0 {
self.cursor_pos -= 1;
true
} else {
false
}
}
pub fn move_to_end(&mut self) -> bool {
if self.cursor_pos < self.content.len() {
self.cursor_pos = self.content.len();
true
} else {
false
}
}
pub fn move_to_start(&mut self) -> bool {
if self.cursor_pos > 0 {
self.cursor_pos = 0;
true
} else {
false
}
}
pub fn move_word_left(&mut self) -> bool {
if self.cursor_pos == 0 {
return false;
}
loop {
self.cursor_pos -= 1;
if self.cursor_pos == 0 || !self.content[self.cursor_pos-1].is_alphanumeric() {
break;
}
}
true
}
pub fn move_word_right(&mut self) -> bool {
if self.cursor_pos == self.content.len() {
return false;
}
loop {
self.cursor_pos += 1;
if self.cursor_pos >= self.content.len() - 1
|| !self.content[self.cursor_pos-1].is_alphanumeric() {
break;
}
}
true
}
pub fn del_word_left(&mut self) -> bool {
if self.cursor_pos == 0 {
return false;
}
loop {
self.content.remove(self.cursor_pos);
self.cursor_pos -= 1;
if self.cursor_pos == 0 || !self.content[self.cursor_pos-1].is_alphanumeric() {
break;
}
}
true
}
pub fn del_word_right(&mut self) -> bool {
if self.cursor_pos == self.content.len() {
return false;
}
loop {
let deleted_is_an = self.content[self.cursor_pos].is_alphanumeric();
self.content.remove(self.cursor_pos);
if self.cursor_pos >= self.content.len() - 1 || !deleted_is_an {
break;
}
}
true
}
pub fn apply_keycode_event(&mut self, code: KeyCode) -> bool {
if !self.focused {
return false;
}
match code {
KeyCode::Home => self.move_to_start(),
KeyCode::End => self.move_to_end(),
KeyCode::Char(c) => self.put_char(c),
KeyCode::Left => self.move_left(),
KeyCode::Right => self.move_right(),
KeyCode::Backspace => self.del_char_left(),
KeyCode::Delete => self.del_char_below(),
_ => false,
}
}
pub fn apply_click_event(&mut self, x: u16, y: u16) -> bool {
if self.area.contains(x, y) {
if self.focused {
let p = (x - 1 - self.area.left) as usize;
self.cursor_pos = p.min(self.content.len());
} else {
self.focused = true;
}
true
} else {
false
}
}
pub fn apply_event(&mut self, event: &Event) -> bool {
match event {
Event::Click(x, y, ..) => {
self.apply_click_event(*x, *y)
}
Event::Key(KeyEvent{code, modifiers}) if (modifiers.is_empty()||*modifiers==KeyModifiers::SHIFT) => {
self.apply_keycode_event(*code)
}
_ => false,
}
}
pub fn display_on<W>(&self, w: &mut W) -> Result<(), Error>
where
W: std::io::Write,
{
queue!(w, SetBackgroundColor(Color::Reset))?;
queue!(w, cursor::MoveTo(self.area.left, self.area.top))?;
let mut slice_start = 0;
let width = self.area.width as usize;
let mut ellipsis_at_start = false;
let mut ellipsis_at_end = false;
if self.content.len() + 1 >= width {
if self.cursor_pos <= width / 2 {
slice_start = 0;
ellipsis_at_end = true;
} else if self.cursor_pos >= self.content.len() - width / 2 {
slice_start = self.content.len() + 1 - width;
ellipsis_at_start = true;
} else {
slice_start = self.cursor_pos - width / 2;
ellipsis_at_start = true;
ellipsis_at_end = true;
}
}
for i in 0..width {
if i == 0 && ellipsis_at_start {
self.normal_style.queue(w, fit::ELLIPSIS)?;
continue;
}
if i == width-1 && ellipsis_at_end {
self.normal_style.queue(w, fit::ELLIPSIS)?;
continue;
}
let idx = i + slice_start;
if idx >= self.content.len() {
if self.focused && (idx==self.cursor_pos) && (idx==self.content.len()) {
self.cursor_style.queue(w, ' ')?;
} else {
self.normal_style.queue(w, ' ')?;
}
} else {
let c = self.content[idx];
if self.focused && (self.cursor_pos == idx) {
self.cursor_style.queue(w, c)?;
} else {
self.normal_style.queue(w, c)?;
}
}
}
Ok(())
}
pub fn display(&self) -> Result<(), Error> {
let mut w = std::io::stdout();
self.display_on(&mut w)?;
w.flush()?;
Ok(())
}
}