зеркало из
https://github.com/glebtv/tui-textarea.git
synced 2026-09-03 14:26:17 +03:00
render only displayed lines in Paragraph
by handling vertical scroll by ourselves
Этот коммит содержится в:
@@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use tui::buffer::Buffer;
|
||||
use tui::layout::Rect;
|
||||
use tui::text::Text;
|
||||
use tui::widgets::{Block, Paragraph, Widget};
|
||||
use tui::widgets::{Paragraph, Widget};
|
||||
|
||||
// &mut 'a (u16, u16) is not available since TextAreaWidget instance totally takes over the ownership of TextArea
|
||||
// instance. In the case, the TextArea instance cannot be accessed from any other objects since it is mutablly
|
||||
@@ -36,40 +36,18 @@ impl ScrollTop {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Renderer<'a> {
|
||||
scroll_top: &'a ScrollTop,
|
||||
cursor: (u16, u16),
|
||||
block: Option<Block<'a>>,
|
||||
inner: Paragraph<'a>,
|
||||
}
|
||||
pub struct Renderer<'a>(&'a TextArea<'a>);
|
||||
|
||||
impl<'a> Renderer<'a> {
|
||||
pub fn new(textarea: &'a TextArea<'a>) -> Self {
|
||||
let lnum_len = num_digits(textarea.lines().len());
|
||||
let lines: Vec<_> = textarea
|
||||
.lines()
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.enumerate()
|
||||
.map(|(row, line)| textarea.line_spans(line, row, lnum_len))
|
||||
.collect();
|
||||
let inner = Paragraph::new(Text::from(lines)).style(textarea.style());
|
||||
let cursor = textarea.cursor();
|
||||
Self {
|
||||
scroll_top: &textarea.scroll_top,
|
||||
cursor: (cursor.0 as u16, cursor.1 as u16),
|
||||
block: textarea.block().cloned(),
|
||||
inner,
|
||||
}
|
||||
Self(textarea)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Renderer<'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
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let inner_area = if let Some(b) = self.0.block() {
|
||||
b.inner(area)
|
||||
} else {
|
||||
area
|
||||
};
|
||||
@@ -84,18 +62,34 @@ impl<'a> Widget for Renderer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let (top_row, top_col) = self.scroll_top.load();
|
||||
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 cursor = self.0.cursor();
|
||||
let (top_row, top_col) = self.0.scroll_top.load();
|
||||
let row = next_scroll_top(top_row, cursor.0 as u16, inner_area.height);
|
||||
let col = next_scroll_top(top_col, cursor.1 as u16, inner_area.width);
|
||||
|
||||
let scroll = (row, col);
|
||||
if scroll != (0, 0) {
|
||||
self.inner = self.inner.scroll(scroll);
|
||||
let lnum_len = num_digits(self.0.lines().len());
|
||||
let lines: Vec<_> = self
|
||||
.0
|
||||
.lines()
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.enumerate()
|
||||
.skip(row as usize)
|
||||
.take(inner_area.height as usize)
|
||||
.map(|(row, line)| self.0.line_spans(line, row, lnum_len))
|
||||
.collect();
|
||||
|
||||
let mut inner = Paragraph::new(Text::from(lines)).style(self.0.style());
|
||||
if let Some(b) = self.0.block() {
|
||||
inner = inner.block(b.clone());
|
||||
}
|
||||
if col != 0 {
|
||||
inner = inner.scroll((0, col));
|
||||
}
|
||||
|
||||
// Store scroll top position for rendering on the next tick
|
||||
self.scroll_top.store(row, col);
|
||||
self.0.scroll_top.store(row, col);
|
||||
|
||||
self.inner.render(area, buf);
|
||||
inner.render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user