From 40e4bfbe15bd06703b92c539f711564b6046db4e Mon Sep 17 00:00:00 2001 From: rhysd Date: Sun, 17 Jul 2022 03:22:09 +0900 Subject: [PATCH] render only displayed lines in `Paragraph` by handling vertical scroll by ourselves --- src/widget.rs | 66 +++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/src/widget.rs b/src/widget.rs index 89cf1e6..dd2bf0f 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -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>, - 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); } }