1
0
зеркало из https://github.com/glebtv/tui-textarea.git synced 2026-09-03 14:26:17 +03:00

handle horizontal/vertical scrolls

Этот коммит содержится в:
rhysd
2022-06-10 13:53:05 +09:00
родитель bac44ae105
Коммит f4ee02d858

Просмотреть файл

@@ -2,6 +2,9 @@ use crate::cursor::CursorMove;
use crate::edit::{Edit, EditKind};
use crate::history::EditHistory;
use crate::input::{Input, Key};
use std::sync::atomic::{AtomicU16, Ordering};
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::style::{Modifier, Style};
use tui::text::{Span, Spans, Text};
use tui::widgets::{Block, Paragraph, Widget};
@@ -14,6 +17,7 @@ pub struct TextArea<'a> {
tab: &'a str,
history: EditHistory,
cursor_line_style: Style,
scroll_top: (AtomicU16, AtomicU16),
}
impl<'a> Default for TextArea<'a> {
@@ -26,6 +30,7 @@ impl<'a> Default for TextArea<'a> {
tab: " ",
history: EditHistory::new(50),
cursor_line_style: Style::default().add_modifier(Modifier::UNDERLINED),
scroll_top: (AtomicU16::new(0), AtomicU16::new(0)),
}
}
}
@@ -204,11 +209,13 @@ impl<'a> TextArea<'a> {
lines.push(Spans::from(l.as_str()));
}
}
let mut p = Paragraph::new(Text::from(lines)).style(self.style);
if let Some(b) = &self.block {
p = p.block(b.clone());
let inner = Paragraph::new(Text::from(lines)).style(self.style);
TextAreaWidget {
scroll_top: &self.scroll_top,
cursor: (self.cursor.0 as u16, self.cursor.1 as u16),
block: self.block.clone(),
inner,
}
p
}
pub fn set_style(&mut self, style: Style) {
@@ -249,3 +256,51 @@ impl<'a> TextArea<'a> {
self.cursor
}
}
struct TextAreaWidget<'a> {
// &mut 'a (u16, u16) is not available since TextAreaWidget instance takes over the ownership of TextArea instance.
// In the case the TextArea instance cannot be accessed from any other objects.
scroll_top: &'a (AtomicU16, AtomicU16),
cursor: (u16, u16),
block: Option<Block<'a>>,
inner: Paragraph<'a>,
}
impl<'a> Widget for TextAreaWidget<'a> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
let inner_area = if let Some(b) = self.block.take() {
let area = b.inner(area);
self.inner = self.inner.block(b);
area
} else {
area
};
let top_row = self.scroll_top.0.load(Ordering::Relaxed);
let top_col = self.scroll_top.1.load(Ordering::Relaxed);
fn next_scroll_top(prev_top: u16, cursor: u16, width: u16) -> u16 {
if cursor < prev_top {
cursor
} else if prev_top + width <= cursor {
cursor + 1 - width
} else {
prev_top
}
}
let row = next_scroll_top(top_row, self.cursor.0, inner_area.height);
let col = next_scroll_top(top_col, self.cursor.1, inner_area.width);
let scroll = (row, col);
if scroll != (0, 0) {
self.inner = self.inner.scroll(scroll);
}
// Store scroll top position for rendering on the next tick
self.scroll_top.0.store(row, Ordering::Relaxed);
self.scroll_top.1.store(col, Ordering::Relaxed);
self.inner.render(area, buf);
}
}