Add cursor to TextInputComponent for better commit message support (#117)

see #46
This commit is contained in:
A. Carscadden 2020-06-13 18:56:35 -04:00 committed by GitHub
parent 0a541a398e
commit 09c3fe9cbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -8,12 +8,11 @@ use crate::{
};
use anyhow::Result;
use crossterm::event::{Event, KeyCode, KeyModifiers};
use std::borrow::Cow;
use strings::commands;
use tui::{
backend::Backend,
layout::Rect,
style::Style,
style::{Modifier, Style},
widgets::{Clear, Text},
Frame,
};
@ -25,6 +24,7 @@ pub struct TextInputComponent {
msg: String,
visible: bool,
theme: Theme,
cursor_position: usize,
}
impl TextInputComponent {
@ -40,19 +40,56 @@ impl TextInputComponent {
theme: *theme,
title: title.to_string(),
default_msg: default_msg.to_string(),
cursor_position: 0,
}
}
///
/// Clear the `msg`.
pub fn clear(&mut self) {
self.msg.clear();
}
///
/// Get the `msg`.
pub const fn get_text(&self) -> &String {
&self.msg
}
/// Move the cursor right one char.
fn incr_cursor(&mut self) {
if let Some(pos) = self.next_char_position() {
self.cursor_position = pos;
}
}
/// Move the cursor left one char.
fn decr_cursor(&mut self) {
let mut new_pos: usize = 0;
for (bytes, _) in self.msg.char_indices() {
if bytes >= self.cursor_position {
break;
}
new_pos = bytes;
}
self.cursor_position = new_pos;
}
/// Get the position of the next char, or, if the cursor points
/// to the last char, the `msg.len()`.
/// Returns None when the cursor is already at `msg.len()`.
fn next_char_position(&self) -> Option<usize> {
let mut char_indices =
self.msg[self.cursor_position..].char_indices();
if char_indices.next().is_some() {
if let Some((bytes, _)) = char_indices.next() {
Some(self.cursor_position + bytes)
} else {
Some(self.msg.len())
}
} else {
None
}
}
///
pub fn set_text(&mut self, msg: String) {
self.msg = msg;
@ -71,16 +108,43 @@ impl DrawableComponent for TextInputComponent {
_rect: Rect,
) -> Result<()> {
if self.visible {
let txt = if self.msg.is_empty() {
[Text::Styled(
Cow::from(self.default_msg.as_str()),
let mut txt: Vec<tui::widgets::Text> = Vec::new();
if self.msg.is_empty() {
txt.push(Text::styled(
self.default_msg.as_str(),
self.theme.text(false, false),
)]
));
} else {
[Text::Styled(
Cow::from(self.msg.clone()),
Style::default(),
)]
// the portion of the text before the cursor is added
// if the cursor is not at the first character
if self.cursor_position > 0 {
txt.push(Text::styled(
&self.msg[..self.cursor_position],
Style::default(),
));
}
txt.push(Text::styled(
if let Some(pos) = self.next_char_position() {
&self.msg[self.cursor_position..pos]
} else {
// if the cursor is at the end of the msg
// a whitespace is used to underline
" "
},
Style::default().modifier(Modifier::UNDERLINED),
));
// the final portion of the text is added if there is
// still remaining characters
if let Some(pos) = self.next_char_position() {
if pos < self.msg.len() {
txt.push(Text::styled(
&self.msg[pos..],
Style::default(),
));
}
}
};
let area = ui::centered_rect(60, 20, f.size());
@ -128,11 +192,39 @@ impl Component for TextInputComponent {
return Ok(true);
}
KeyCode::Char(c) if !is_ctrl => {
self.msg.push(c);
self.msg.insert(self.cursor_position, c);
self.incr_cursor();
return Ok(true);
}
KeyCode::Delete => {
if self.cursor_position < self.msg.len() {
self.msg.remove(self.cursor_position);
}
return Ok(true);
}
KeyCode::Backspace => {
self.msg.pop();
if self.cursor_position > 0 {
self.decr_cursor();
if self.cursor_position < self.msg.len() {
}
self.msg.remove(self.cursor_position);
}
return Ok(true);
}
KeyCode::Left => {
self.decr_cursor();
return Ok(true);
}
KeyCode::Right => {
self.incr_cursor();
return Ok(true);
}
KeyCode::Home => {
self.cursor_position = 0;
return Ok(true);
}
KeyCode::End => {
self.cursor_position = self.msg.len();
return Ok(true);
}
_ => (),