From 972dfead2d94b2a5bc0f375bcbfb4f91a0108c7f Mon Sep 17 00:00:00 2001 From: rhysd Date: Fri, 14 Oct 2022 21:40:47 +0900 Subject: [PATCH] separate `Scrolling` to src/scroll.rs --- src/lib.rs | 4 +- src/scroll.rs | 180 ++++++++++++++++++++++++++++++++++++++++++++++++ src/textarea.rs | 180 +----------------------------------------------- 3 files changed, 184 insertions(+), 180 deletions(-) create mode 100644 src/scroll.rs diff --git a/src/lib.rs b/src/lib.rs index 6b6640f..aa1d277 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ mod cursor; mod highlight; mod history; mod input; +mod scroll; #[cfg(feature = "search")] mod search; mod textarea; @@ -17,4 +18,5 @@ mod word; pub use cursor::CursorMove; pub use input::{Input, Key}; -pub use textarea::{Scrolling, TextArea}; +pub use scroll::Scrolling; +pub use textarea::TextArea; diff --git a/src/scroll.rs b/src/scroll.rs new file mode 100644 index 0000000..ef83abf --- /dev/null +++ b/src/scroll.rs @@ -0,0 +1,180 @@ +use crate::widget::Viewport; + +/// Specify how to scroll the textarea. +/// +/// This type is marked as `#[non_exhaustive]` since more variations may be supported in the future. Note that the cursor will +/// not move until it goes out the viewport. See also: [`TextArea::scroll`] +#[non_exhaustive] +pub enum Scrolling { + /// Scroll the textarea by rows (vertically) and columns (horizontally). Passing positive scroll amounts to `rows` and `cols` + /// scolls it to down and right. Negative integers means the opposite directions. `(i16, i16)` pair can be converted into + /// `Scrolling::Delta` where 1st element means rows and 2nd means columns. + /// + /// ``` + /// # use tui::buffer::Buffer; + /// # use tui::layout::Rect; + /// # use tui::widgets::Widget; + /// use tui_textarea::{TextArea, Scrolling}; + /// + /// // Let's say terminal height is 8. + /// + /// // Create textarea with 20 lines "0", "1", "2", "3", ... + /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect(); + /// # // Call `render` at least once to populate terminal size + /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; + /// # let mut b = Buffer::empty(r.clone()); + /// # textarea.widget().render(r, &mut b); + /// + /// // Scroll down by 2 lines. + /// textarea.scroll(Scrolling::Delta{rows: 2, cols: 0}); + /// assert_eq!(textarea.cursor(), (2, 0)); + /// + /// // (1, 0) is converted into Scrolling::Delta{rows: 1, cols: 0} + /// textarea.scroll((1, 0)); + /// assert_eq!(textarea.cursor(), (3, 0)); + /// ``` + Delta { rows: i16, cols: i16 }, + /// Scroll down the textarea by one page. + /// + /// ``` + /// # use tui::buffer::Buffer; + /// # use tui::layout::Rect; + /// # use tui::widgets::Widget; + /// use tui_textarea::{TextArea, Scrolling}; + /// + /// // Let's say terminal height is 8. + /// + /// // Create textarea with 20 lines "0", "1", "2", "3", ... + /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect(); + /// # // Call `render` at least once to populate terminal size + /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; + /// # let mut b = Buffer::empty(r.clone()); + /// # textarea.widget().render(r, &mut b); + /// + /// // Scroll down by one page (8 lines) + /// textarea.scroll(Scrolling::PageDown); + /// assert_eq!(textarea.cursor(), (8, 0)); + /// textarea.scroll(Scrolling::PageDown); + /// assert_eq!(textarea.cursor(), (16, 0)); + /// textarea.scroll(Scrolling::PageDown); + /// assert_eq!(textarea.cursor(), (19, 0)); // Reached bottom of the textarea + /// ``` + PageDown, + /// Scroll up the textarea by one page. + /// + /// ``` + /// # use tui::buffer::Buffer; + /// # use tui::layout::Rect; + /// # use tui::widgets::Widget; + /// use tui_textarea::{TextArea, Scrolling, CursorMove}; + /// + /// // Let's say terminal height is 8. + /// + /// // Create textarea with 20 lines "0", "1", "2", "3", ... + /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect(); + /// # // Call `render` at least once to populate terminal size + /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; + /// # let mut b = Buffer::empty(r.clone()); + /// # textarea.widget().render(r.clone(), &mut b); + /// + /// // Go to the last line at first + /// textarea.move_cursor(CursorMove::Bottom); + /// assert_eq!(textarea.cursor(), (19, 0)); + /// # // Call `render` to populate terminal size + /// # textarea.widget().render(r.clone(), &mut b); + /// + /// // Scroll up by one page (8 lines) + /// textarea.scroll(Scrolling::PageUp); + /// assert_eq!(textarea.cursor(), (11, 0)); + /// textarea.scroll(Scrolling::PageUp); + /// assert_eq!(textarea.cursor(), (7, 0)); // Reached top of the textarea + /// ``` + PageUp, + /// Scroll down the textarea by half of the page. + /// + /// ``` + /// # use tui::buffer::Buffer; + /// # use tui::layout::Rect; + /// # use tui::widgets::Widget; + /// use tui_textarea::{TextArea, Scrolling}; + /// + /// // Let's say terminal height is 8. + /// + /// // Create textarea with 10 lines "0", "1", "2", "3", ... + /// let mut textarea: TextArea = (0..10).into_iter().map(|i| i.to_string()).collect(); + /// # // Call `render` at least once to populate terminal size + /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; + /// # let mut b = Buffer::empty(r.clone()); + /// # textarea.widget().render(r, &mut b); + /// + /// // Scroll down by half-page (4 lines) + /// textarea.scroll(Scrolling::HalfPageDown); + /// assert_eq!(textarea.cursor(), (4, 0)); + /// textarea.scroll(Scrolling::HalfPageDown); + /// assert_eq!(textarea.cursor(), (8, 0)); + /// textarea.scroll(Scrolling::HalfPageDown); + /// assert_eq!(textarea.cursor(), (9, 0)); // Reached bottom of the textarea + /// ``` + HalfPageDown, + /// Scroll up the textarea by half of the page. + /// + /// ``` + /// # use tui::buffer::Buffer; + /// # use tui::layout::Rect; + /// # use tui::widgets::Widget; + /// use tui_textarea::{TextArea, Scrolling, CursorMove}; + /// + /// // Let's say terminal height is 8. + /// + /// // Create textarea with 20 lines "0", "1", "2", "3", ... + /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect(); + /// # // Call `render` at least once to populate terminal size + /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; + /// # let mut b = Buffer::empty(r.clone()); + /// # textarea.widget().render(r.clone(), &mut b); + /// + /// // Go to the last line at first + /// textarea.move_cursor(CursorMove::Bottom); + /// assert_eq!(textarea.cursor(), (19, 0)); + /// # // Call `render` to populate terminal size + /// # textarea.widget().render(r.clone(), &mut b); + /// + /// // Scroll up by half-page (4 lines) + /// textarea.scroll(Scrolling::HalfPageUp); + /// assert_eq!(textarea.cursor(), (15, 0)); + /// textarea.scroll(Scrolling::HalfPageUp); + /// assert_eq!(textarea.cursor(), (11, 0)); + /// ``` + HalfPageUp, +} + +impl Scrolling { + pub(crate) fn scroll(self, viewport: &mut Viewport) { + let (rows, cols) = match self { + Self::Delta { rows, cols } => (rows, cols), + Self::PageDown => { + let (_, _, _, height) = viewport.rect(); + (height as i16, 0) + } + Self::PageUp => { + let (_, _, _, height) = viewport.rect(); + (-(height as i16), 0) + } + Self::HalfPageDown => { + let (_, _, _, height) = viewport.rect(); + ((height as i16) / 2, 0) + } + Self::HalfPageUp => { + let (_, _, _, height) = viewport.rect(); + (-(height as i16) / 2, 0) + } + }; + viewport.scroll(rows, cols); + } +} + +impl From<(i16, i16)> for Scrolling { + fn from((rows, cols): (i16, i16)) -> Self { + Self::Delta { rows, cols } + } +} diff --git a/src/textarea.rs b/src/textarea.rs index 66ccceb..b055d82 100644 --- a/src/textarea.rs +++ b/src/textarea.rs @@ -2,6 +2,7 @@ use crate::cursor::CursorMove; use crate::highlight::LineHighlighter; use crate::history::{Edit, EditKind, History}; use crate::input::{Input, Key}; +use crate::scroll::Scrolling; #[cfg(feature = "search")] use crate::search::Search; use crate::util::spaces; @@ -12,185 +13,6 @@ use tui::style::{Modifier, Style}; use tui::text::Spans; use tui::widgets::{Block, Widget}; -/// Specify how to scroll the textarea. -/// -/// This type is marked as `#[non_exhaustive]` since more variations may be supported in the future. Note that the cursor will -/// not move until it goes out the viewport. See also: [`TextArea::scroll`] -#[non_exhaustive] -pub enum Scrolling { - /// Scroll the textarea by rows (vertically) and columns (horizontally). Passing positive scroll amounts to `rows` and `cols` - /// scolls it to down and right. Negative integers means the opposite directions. `(i16, i16)` pair can be converted into - /// `Scrolling::Delta` where 1st element means rows and 2nd means columns. - /// - /// ``` - /// # use tui::buffer::Buffer; - /// # use tui::layout::Rect; - /// # use tui::widgets::Widget; - /// use tui_textarea::{TextArea, Scrolling}; - /// - /// // Let's say terminal height is 8. - /// - /// // Create textarea with 20 lines "0", "1", "2", "3", ... - /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect(); - /// # // Call `render` at least once to populate terminal size - /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; - /// # let mut b = Buffer::empty(r.clone()); - /// # textarea.widget().render(r, &mut b); - /// - /// // Scroll down by 2 lines. - /// textarea.scroll(Scrolling::Delta{rows: 2, cols: 0}); - /// assert_eq!(textarea.cursor(), (2, 0)); - /// - /// // (1, 0) is converted into Scrolling::Delta{rows: 1, cols: 0} - /// textarea.scroll((1, 0)); - /// assert_eq!(textarea.cursor(), (3, 0)); - /// ``` - Delta { rows: i16, cols: i16 }, - /// Scroll down the textarea by one page. - /// - /// ``` - /// # use tui::buffer::Buffer; - /// # use tui::layout::Rect; - /// # use tui::widgets::Widget; - /// use tui_textarea::{TextArea, Scrolling}; - /// - /// // Let's say terminal height is 8. - /// - /// // Create textarea with 20 lines "0", "1", "2", "3", ... - /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect(); - /// # // Call `render` at least once to populate terminal size - /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; - /// # let mut b = Buffer::empty(r.clone()); - /// # textarea.widget().render(r, &mut b); - /// - /// // Scroll down by one page (8 lines) - /// textarea.scroll(Scrolling::PageDown); - /// assert_eq!(textarea.cursor(), (8, 0)); - /// textarea.scroll(Scrolling::PageDown); - /// assert_eq!(textarea.cursor(), (16, 0)); - /// textarea.scroll(Scrolling::PageDown); - /// assert_eq!(textarea.cursor(), (19, 0)); // Reached bottom of the textarea - /// ``` - PageDown, - /// Scroll up the textarea by one page. - /// - /// ``` - /// # use tui::buffer::Buffer; - /// # use tui::layout::Rect; - /// # use tui::widgets::Widget; - /// use tui_textarea::{TextArea, Scrolling, CursorMove}; - /// - /// // Let's say terminal height is 8. - /// - /// // Create textarea with 20 lines "0", "1", "2", "3", ... - /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect(); - /// # // Call `render` at least once to populate terminal size - /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; - /// # let mut b = Buffer::empty(r.clone()); - /// # textarea.widget().render(r.clone(), &mut b); - /// - /// // Go to the last line at first - /// textarea.move_cursor(CursorMove::Bottom); - /// assert_eq!(textarea.cursor(), (19, 0)); - /// # // Call `render` to populate terminal size - /// # textarea.widget().render(r.clone(), &mut b); - /// - /// // Scroll up by one page (8 lines) - /// textarea.scroll(Scrolling::PageUp); - /// assert_eq!(textarea.cursor(), (11, 0)); - /// textarea.scroll(Scrolling::PageUp); - /// assert_eq!(textarea.cursor(), (7, 0)); // Reached top of the textarea - /// ``` - PageUp, - /// Scroll down the textarea by half of the page. - /// - /// ``` - /// # use tui::buffer::Buffer; - /// # use tui::layout::Rect; - /// # use tui::widgets::Widget; - /// use tui_textarea::{TextArea, Scrolling}; - /// - /// // Let's say terminal height is 8. - /// - /// // Create textarea with 10 lines "0", "1", "2", "3", ... - /// let mut textarea: TextArea = (0..10).into_iter().map(|i| i.to_string()).collect(); - /// # // Call `render` at least once to populate terminal size - /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; - /// # let mut b = Buffer::empty(r.clone()); - /// # textarea.widget().render(r, &mut b); - /// - /// // Scroll down by half-page (4 lines) - /// textarea.scroll(Scrolling::HalfPageDown); - /// assert_eq!(textarea.cursor(), (4, 0)); - /// textarea.scroll(Scrolling::HalfPageDown); - /// assert_eq!(textarea.cursor(), (8, 0)); - /// textarea.scroll(Scrolling::HalfPageDown); - /// assert_eq!(textarea.cursor(), (9, 0)); // Reached bottom of the textarea - /// ``` - HalfPageDown, - /// Scroll up the textarea by half of the page. - /// - /// ``` - /// # use tui::buffer::Buffer; - /// # use tui::layout::Rect; - /// # use tui::widgets::Widget; - /// use tui_textarea::{TextArea, Scrolling, CursorMove}; - /// - /// // Let's say terminal height is 8. - /// - /// // Create textarea with 20 lines "0", "1", "2", "3", ... - /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect(); - /// # // Call `render` at least once to populate terminal size - /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 }; - /// # let mut b = Buffer::empty(r.clone()); - /// # textarea.widget().render(r.clone(), &mut b); - /// - /// // Go to the last line at first - /// textarea.move_cursor(CursorMove::Bottom); - /// assert_eq!(textarea.cursor(), (19, 0)); - /// # // Call `render` to populate terminal size - /// # textarea.widget().render(r.clone(), &mut b); - /// - /// // Scroll up by half-page (4 lines) - /// textarea.scroll(Scrolling::HalfPageUp); - /// assert_eq!(textarea.cursor(), (15, 0)); - /// textarea.scroll(Scrolling::HalfPageUp); - /// assert_eq!(textarea.cursor(), (11, 0)); - /// ``` - HalfPageUp, -} - -impl Scrolling { - fn scroll(self, viewport: &mut Viewport) { - let (rows, cols) = match self { - Self::Delta { rows, cols } => (rows, cols), - Self::PageDown => { - let (_, _, _, height) = viewport.rect(); - (height as i16, 0) - } - Self::PageUp => { - let (_, _, _, height) = viewport.rect(); - (-(height as i16), 0) - } - Self::HalfPageDown => { - let (_, _, _, height) = viewport.rect(); - ((height as i16) / 2, 0) - } - Self::HalfPageUp => { - let (_, _, _, height) = viewport.rect(); - (-(height as i16) / 2, 0) - } - }; - viewport.scroll(rows, cols); - } -} - -impl From<(i16, i16)> for Scrolling { - fn from((rows, cols): (i16, i16)) -> Self { - Self::Delta { rows, cols } - } -} - /// A type to manage state of textarea. /// /// [`TextArea::default`] creates an empty textarea. [`TextArea::new`] creates a textarea with given text lines.