Add scroll to textarea
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::Event;
|
||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
||||
use rat_cursor::HasScreenCursor;
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
||||
use ratatui::prelude::StatefulWidget;
|
||||
use ratatui::style::{Color, Stylize};
|
||||
use ratatui::text::Text;
|
||||
use ratatui::text::{Line, Span, Text};
|
||||
use ratatui::widgets::{Block, Borders, Paragraph, Widget};
|
||||
use tui_input::backend::crossterm::EventHandler;
|
||||
use tui_input::Input;
|
||||
@@ -16,6 +16,8 @@ pub struct TextArea {
|
||||
title: String,
|
||||
style: TextAreaStyle,
|
||||
input_area: Option<Rect>,
|
||||
scroll_offset: u16,
|
||||
auto_scroll: bool,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
@@ -41,14 +43,29 @@ impl StatefulWidget for TextArea {
|
||||
} else if matches!(self.style, TextAreaStyle::SingleLine) {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(10), Constraint::Fill(0)])
|
||||
.constraints([Constraint::Max((self.title.len() + 1) as u16), Constraint::Fill(0)])
|
||||
.split(area);
|
||||
let text = Text::from(self.title);
|
||||
let line = Paragraph::new(text);
|
||||
let input_text = Text::from(input_value).fg(Color::White);
|
||||
let paragraph = Paragraph::new(input_text).bg(Color::Green);
|
||||
state.input_area = Some(chunks[1]);
|
||||
line.render(chunks[0], buf);
|
||||
let label_text = Text::from(self.title);
|
||||
let label = Paragraph::new(label_text);
|
||||
|
||||
// let scroll_offset = if self.input.cursor() > chunks[1].width as usize {
|
||||
// self.input.cursor() - chunks[1].width as usize
|
||||
// } else { 0 };
|
||||
|
||||
let input_text = Span::from(input_value.clone()).fg(Color::White);
|
||||
let input_line = Line::from(input_text);
|
||||
let paragraph = Paragraph::new(input_line)
|
||||
.bg(Color::Green)
|
||||
.scroll((0, self.scroll_offset));
|
||||
|
||||
if self.input_area.is_none() {
|
||||
state.input_area = Some(chunks[1]);
|
||||
}
|
||||
else if let Some(area) = self.input_area && area != chunks[1] {
|
||||
state.input_area = Some(chunks[1]);
|
||||
}
|
||||
|
||||
label.render(chunks[0], buf);
|
||||
paragraph.render(chunks[1], buf);
|
||||
}
|
||||
}
|
||||
@@ -60,26 +77,44 @@ impl HasScreenCursor for TextArea {
|
||||
return None;
|
||||
}
|
||||
let area = self.input_area.unwrap();
|
||||
let width = area.width.max(3) - 3;
|
||||
let scroll = self.input.visual_scroll(width as usize);
|
||||
let x = (self.input.visual_cursor().max(scroll) - scroll) as u16;
|
||||
let scroll = self.input.visual_scroll(1);
|
||||
let x = self.input.visual_cursor().max(scroll) as u16 - self.scroll_offset;
|
||||
Some((area.x + x, area.y))
|
||||
}
|
||||
}
|
||||
|
||||
impl TextArea {
|
||||
pub fn new(title: &str, placeholder_text: &str, style: Option<TextAreaStyle>) -> Self {
|
||||
pub fn new(title: &str, placeholder_text: &str, style: Option<TextAreaStyle>, auto_scroll: Option<bool>) -> Self {
|
||||
Self {
|
||||
input: Input::new(placeholder_text.to_string()),
|
||||
title: title.to_string(),
|
||||
active: false,
|
||||
style: style.unwrap_or(TextAreaStyle::SingleLine),
|
||||
input_area: None,
|
||||
auto_scroll: auto_scroll.unwrap_or(true),
|
||||
scroll_offset: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_input(&mut self, event: &Event) -> Result<()> {
|
||||
let _ = self.input.handle_event(event);
|
||||
|
||||
if let Event::Key(key) = event &&
|
||||
!matches!(key.kind, KeyEventKind::Release) &&
|
||||
let Some(area) = self.input_area {
|
||||
|
||||
let cursor_pos = self.input.cursor() as u16;
|
||||
if self.scroll_offset > cursor_pos {
|
||||
self.scroll_offset = cursor_pos;
|
||||
} else if cursor_pos >= area.width + self.scroll_offset {
|
||||
self.scroll_offset = cursor_pos - area.width;
|
||||
} else if self.auto_scroll && self.scroll_offset > 0 && (key.code.is_delete() || key.code.is_backspace()) {
|
||||
self.scroll_offset -= 1;
|
||||
// HACK: with_cursor function requires to be owned so use handle event
|
||||
let _ = self.input.handle_event(&Event::Key(KeyEvent::new(KeyCode::Left, KeyModifiers::empty())));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ pub struct AddFolderPopup {
|
||||
|
||||
impl AddFolderPopup {
|
||||
pub fn new() -> Self {
|
||||
let mut textarea = TextArea::new("Folder Path", "", None);
|
||||
let mut textarea = TextArea::new("Folder Path", "", None, None);
|
||||
textarea.active = true;
|
||||
Self { textarea }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user