mirror of
https://github.com/extrawurst/gitui.git
synced 2024-11-27 10:15:07 +03:00
Add cursor to TextInputComponent for better commit message support (#117)
see #46
This commit is contained in:
parent
0a541a398e
commit
09c3fe9cbb
@ -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);
|
||||
}
|
||||
_ => (),
|
||||
|
Loading…
Reference in New Issue
Block a user