From f28fbefd7926a2aa924a2eb87281f875d2ef12a1 Mon Sep 17 00:00:00 2001 From: rhysd Date: Thu, 9 Jun 2022 00:44:41 +0900 Subject: [PATCH] implement inserting tab character --- examples/minimal.rs | 4 ++-- src/lib.rs | 48 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/examples/minimal.rs b/examples/minimal.rs index a3a13b2..bf19331 100644 --- a/examples/minimal.rs +++ b/examples/minimal.rs @@ -17,8 +17,8 @@ fn main() -> io::Result<()> { let backend = CrosstermBackend::new(stdout); let mut term = Terminal::new(backend)?; - let mut textarea = - TextArea::default().block(Block::default().borders(Borders::ALL).title("EXAMPLE")); + let mut textarea = TextArea::default(); + textarea.block(Block::default().borders(Borders::ALL).title("EXAMPLE")); let layout = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Min(1)].as_ref()); diff --git a/src/lib.rs b/src/lib.rs index 98a0123..41d66a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ use tui::style::{Modifier, Style}; use tui::text::{Span, Spans, Text}; use tui::widgets::{Block, Paragraph, Widget}; +#[derive(Clone, Copy)] pub enum Key { Char(char), Backspace, @@ -68,6 +69,7 @@ pub struct TextArea<'a> { block: Option>, style: Style, cursor: (usize, usize), + tab: &'a str, } impl<'a> Default for TextArea<'a> { @@ -77,6 +79,7 @@ impl<'a> Default for TextArea<'a> { block: None, style: Style::default(), cursor: (0, 0), + tab: " ", } } } @@ -90,6 +93,7 @@ impl<'a> TextArea<'a> { match input.key { Key::Char(c) => self.insert_char(c), Key::Backspace => self.delete_char(), + Key::Tab => self.insert_tab(), _ => {} } } @@ -104,6 +108,23 @@ impl<'a> TextArea<'a> { } } + pub fn insert_str(&mut self, s: &str) { + // TODO: Consider \n in `s` + let (row, col) = self.cursor; + let line = &mut self.lines[row]; + if let Some((i, _)) = line.char_indices().nth(col) { + line.insert_str(i, s); + self.cursor.1 += s.chars().count(); + } + } + + pub fn insert_tab(&mut self) { + if !self.tab.is_empty() { + let len = self.tab.len() - self.cursor.1 % self.tab.len(); + self.insert_str(&self.tab[..len]); + } + } + pub fn delete_char(&mut self) { let (row, col) = self.cursor; let line = &mut self.lines[row]; @@ -141,13 +162,36 @@ impl<'a> TextArea<'a> { p } - pub fn style(mut self, style: Style) -> Self { + pub fn style(&mut self, style: Style) -> &mut Self { self.style = style; self } - pub fn block(mut self, block: Block<'a>) -> Self { + pub fn block(&mut self, block: Block<'a>) -> &mut Self { self.block = Some(block); self } + + pub fn remove_block(&mut self) -> &mut Self { + self.block = None; + self + } + + pub fn tab(&mut self, tab: &'a str) -> &mut Self { + assert!( + tab.chars().all(|c| c == ' '), + "tab string must consist of spaces but got {:?}", + tab, + ); + self.tab = tab; + self + } + + pub fn lines(&'a self) -> impl Iterator { + self.lines.iter().map(|l| &l[..l.len() - 1]) // Trim last whitespace + } + + pub fn cursor(&self) -> (usize, usize) { + self.cursor + } }