зеркало из
https://github.com/glebtv/tui-textarea.git
synced 2026-09-08 00:15:50 +03:00
handle horizontal/vertical scrolls
Этот коммит содержится в:
@@ -2,6 +2,9 @@ use crate::cursor::CursorMove;
|
|||||||
use crate::edit::{Edit, EditKind};
|
use crate::edit::{Edit, EditKind};
|
||||||
use crate::history::EditHistory;
|
use crate::history::EditHistory;
|
||||||
use crate::input::{Input, Key};
|
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::style::{Modifier, Style};
|
||||||
use tui::text::{Span, Spans, Text};
|
use tui::text::{Span, Spans, Text};
|
||||||
use tui::widgets::{Block, Paragraph, Widget};
|
use tui::widgets::{Block, Paragraph, Widget};
|
||||||
@@ -14,6 +17,7 @@ pub struct TextArea<'a> {
|
|||||||
tab: &'a str,
|
tab: &'a str,
|
||||||
history: EditHistory,
|
history: EditHistory,
|
||||||
cursor_line_style: Style,
|
cursor_line_style: Style,
|
||||||
|
scroll_top: (AtomicU16, AtomicU16),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Default for TextArea<'a> {
|
impl<'a> Default for TextArea<'a> {
|
||||||
@@ -26,6 +30,7 @@ impl<'a> Default for TextArea<'a> {
|
|||||||
tab: " ",
|
tab: " ",
|
||||||
history: EditHistory::new(50),
|
history: EditHistory::new(50),
|
||||||
cursor_line_style: Style::default().add_modifier(Modifier::UNDERLINED),
|
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()));
|
lines.push(Spans::from(l.as_str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut p = Paragraph::new(Text::from(lines)).style(self.style);
|
let inner = Paragraph::new(Text::from(lines)).style(self.style);
|
||||||
if let Some(b) = &self.block {
|
TextAreaWidget {
|
||||||
p = p.block(b.clone());
|
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) {
|
pub fn set_style(&mut self, style: Style) {
|
||||||
@@ -249,3 +256,51 @@ impl<'a> TextArea<'a> {
|
|||||||
self.cursor
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user