use super::*;
pub struct TextInput {
pub text: String,
pub cursor: usize,
}
impl TextInput {
pub fn new() -> Self {
Self { text: String::new(), cursor: 0 }
}
pub fn set(&mut self, value: String) {
self.cursor = value.chars().count();
self.text = value;
}
fn byte_index(&self) -> usize {
self.text.char_indices().nth(self.cursor).map(|(i, _)| i).unwrap_or(self.text.len())
}
pub fn move_left(&mut self) {
self.cursor = self.cursor.saturating_sub(1);
}
pub fn move_right(&mut self) {
let len = self.text.chars().count();
if self.cursor < len {
self.cursor += 1;
}
}
pub fn move_home(&mut self) {
self.cursor = 0;
}
pub fn move_end(&mut self) {
self.cursor = self.text.chars().count();
}
pub fn cursor_to_column(&mut self, column: usize) {
let mut used = 0;
self.cursor = self
.text
.chars()
.take_while(|&character| {
used += crate::utils::display_width(&character.to_string()).max(1);
used <= column
})
.count();
}
pub fn insert(&mut self, c: char) {
let idx = self.byte_index();
self.text.insert(idx, c);
self.cursor += 1;
}
pub fn backspace(&mut self) {
if self.cursor > 0 {
let byte_start = self.text.char_indices().nth(self.cursor - 1).map(|(i, _)| i).unwrap_or(0);
let byte_end = self.byte_index();
self.text.replace_range(byte_start..byte_end, "");
self.cursor -= 1;
}
}
pub fn delete_forward(&mut self) {
let len = self.text.chars().count();
if self.cursor < len {
let byte_start = self.byte_index();
let byte_end = self.text.char_indices().nth(self.cursor + 1).map(|(i, _)| i).unwrap_or(self.text.len());
self.text.replace_range(byte_start..byte_end, "");
}
}
fn spans_around(before: &str, under: Option<char>, after: &str, text_style: Style, cursor_style: Style) -> Vec<Span<'static>> {
let printable = |text: &str| crate::utils::printable_name(text);
match under {
Some(character) => vec![Span::styled(printable(before), text_style), Span::styled(printable(&character.to_string()), cursor_style), Span::styled(printable(after), text_style)],
None if crate::display::classic_cursor() => vec![Span::styled(printable(before), text_style), Span::styled(" ", cursor_style)],
None => match before.char_indices().last() {
Some((last, _)) => vec![Span::styled(printable(&before[..last]), text_style), Span::styled(printable(&before[last..]), text_style.add_modifier(CURSOR_AFTER))],
None => vec![Span::styled(" ", cursor_style.add_modifier(PAST_END))],
},
}
}
pub fn cursor_spans_within(&self, width: usize, text_style: Style, cursor_style: Style) -> Vec<Span<'static>> {
if width == 0 {
return Vec::new();
}
let cell = |character: char| crate::utils::display_width(&character.to_string()).max(1);
let characters: Vec<char> = self.text.chars().collect();
let cursor = self.cursor.min(characters.len());
let under = characters.get(cursor).copied();
let at_cursor = under.unwrap_or(' ');
let whole: usize = characters.iter().map(|&c| cell(c)).sum::<usize>() + 1;
let mut first = 0;
if whole > width {
let mut used = cell(at_cursor);
first = cursor;
while first > 0 && used + cell(characters[first - 1]) <= width {
first -= 1;
used += cell(characters[first]);
}
}
let before: String = characters[first..cursor].iter().collect();
let mut used = crate::utils::display_width(&before) + cell(at_cursor);
let mut after = String::new();
for &character in characters.iter().skip(cursor + 1) {
let this = cell(character);
if used + this > width {
break;
}
used += this;
after.push(character);
}
Self::spans_around(&before, under, &after, text_style, cursor_style)
}
pub fn cursor_spans(&self, text_style: Style, cursor_style: Style) -> Vec<Span<'static>> {
let byte_idx = self.byte_index();
let before = self.text[..byte_idx].to_string();
let rest = &self.text[byte_idx..];
let mut chars = rest.chars();
let under = chars.next();
let after: String = chars.collect();
Self::spans_around(&before, under, &after, text_style, cursor_style)
}
}
pub const PAST_END: Modifier = Modifier::HIDDEN;
pub const CURSOR_AFTER: Modifier = Modifier::CROSSED_OUT;