1
0
зеркало из https://github.com/glebtv/tui-textarea.git synced 2026-09-08 16:35:49 +03:00

implement Scrolling enum to specify scrolling more flexibly

this is a breaking change. next release should be v0.2.0.
Этот коммит содержится в:
rhysd
2022-10-03 20:59:26 +09:00
родитель 2f300b9dfa
Коммит 4e1a99988f
5 изменённых файлов: 73 добавлений и 20 удалений

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

@@ -415,7 +415,7 @@ notify how to move the cursor.
| `textarea.set_search_pattern(pattern)` | Set a pattern for text search | | `textarea.set_search_pattern(pattern)` | Set a pattern for text search |
| `textarea.search_forward(match_cursor)` | Move cursor to next match of text search | | `textarea.search_forward(match_cursor)` | Move cursor to next match of text search |
| `textarea.search_back(match_cursor)` | Move cursor to previous match of text search | | `textarea.search_back(match_cursor)` | Move cursor to previous match of text search |
| `textarea.scroll(rows, cols)` | Scroll the viewport | | `textarea.scroll(scrolling)` | Scroll the viewport |
To define your own key mappings, simply call the above methods in your code instead of `TextArea::input()` method. The To define your own key mappings, simply call the above methods in your code instead of `TextArea::input()` method. The
following example defines modal key mappings like Vim. following example defines modal key mappings like Vim.
@@ -468,8 +468,8 @@ loop {
Input { key: Key::Esc, .. } => textarea.set_search_pattern("").unwrap(), Input { key: Key::Esc, .. } => textarea.set_search_pattern("").unwrap(),
Input { key: Key::Char('n'), .. } => textarea.search_forward(), Input { key: Key::Char('n'), .. } => textarea.search_forward(),
Input { key: Key::Char('N'), .. } => textarea.search_back(), Input { key: Key::Char('N'), .. } => textarea.search_back(),
Input { key: Key::Char('e'), ctrl: true .. } => textarea.scroll(1, 0), Input { key: Key::Char('e'), ctrl: true .. } => textarea.scroll((1, 0)),
Input { key: Key::Char('y'), ctrl: true .. } => textarea.scroll(-1, 0), Input { key: Key::Char('y'), ctrl: true .. } => textarea.scroll((-1, 0)),
_ => {}, _ => {},
}, },
Mode::Insert => match read()?.into() { Mode::Insert => match read()?.into() {

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

@@ -4,8 +4,9 @@ use crate::word::{find_word_start_backward, find_word_start_forward};
use arbitrary::Arbitrary; use arbitrary::Arbitrary;
use std::cmp; use std::cmp;
/// Specify how to move the cursor. This type is marked as `#[non_exhaustive]` since more variations may be supported in the /// Specify how to move the cursor.
/// future. ///
/// This type is marked as `#[non_exhaustive]` since more variations may be supported in the future.
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]

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

@@ -8,7 +8,9 @@ use crossterm::event::{
#[cfg(feature = "termion")] #[cfg(feature = "termion")]
use termion::event::{Event as TermionEvent, Key as TermionKey, MouseEvent as TermionMouseEvent}; use termion::event::{Event as TermionEvent, Key as TermionKey, MouseEvent as TermionMouseEvent};
/// Backend-agnostic key input kind. This type is marked as `#[non_exhaustive]` since more keys may be supported in the future. /// Backend-agnostic key input kind.
///
/// This type is marked as `#[non_exhaustive]` since more keys may be supported in the future.
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))]

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

@@ -17,4 +17,4 @@ mod word;
pub use cursor::CursorMove; pub use cursor::CursorMove;
pub use input::{Input, Key}; pub use input::{Input, Key};
pub use textarea::TextArea; pub use textarea::{Scrolling, TextArea};

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

@@ -12,6 +12,57 @@ use tui::style::{Modifier, Style};
use tui::text::Spans; use tui::text::Spans;
use tui::widgets::{Block, Widget}; 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 },
}
impl Scrolling {
fn scroll(self, viewport: &mut Viewport) {
let (rows, cols) = match self {
Self::Delta { rows, cols } => (rows, cols),
};
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. /// A type to manage state of textarea.
/// ///
/// [`TextArea::default`] creates an empty textarea. [`TextArea::new`] creates a textarea with given text lines. /// [`TextArea::default`] creates an empty textarea. [`TextArea::new`] creates a textarea with given text lines.
@@ -448,14 +499,14 @@ impl<'a> TextArea<'a> {
key: Key::MouseScrollDown, key: Key::MouseScrollDown,
.. ..
} => { } => {
self.scroll(1, 0); self.scroll((1, 0));
false false
} }
Input { Input {
key: Key::MouseScrollUp, key: Key::MouseScrollUp,
.. ..
} => { } => {
self.scroll(-1, 0); self.scroll((-1, 0));
false false
} }
_ => false, _ => false,
@@ -527,14 +578,14 @@ impl<'a> TextArea<'a> {
key: Key::MouseScrollDown, key: Key::MouseScrollDown,
.. ..
} => { } => {
self.scroll(1, 0); self.scroll((1, 0));
false false
} }
Input { Input {
key: Key::MouseScrollUp, key: Key::MouseScrollUp,
.. ..
} => { } => {
self.scroll(-1, 0); self.scroll((-1, 0));
false false
} }
_ => false, _ => false,
@@ -1527,10 +1578,9 @@ impl<'a> TextArea<'a> {
self.search.style = style; self.search.style = style;
} }
/// Scroll the textarea by `rows` lines (vertically) and `cols` columns (horizontally). Positive scroll amount means /// Scroll the textarea. See [`Scrolling`] for the argument.
/// scrolling down or right. And negative scroll amount means scrolling up or left. The cursor will not move until /// The cursor will not move until it goes out the viewport. When the cursor position is outside the viewport after scroll,
/// it goes out the viewport. When the cursor position is outside the viewport after scroll, the cursor position will /// the cursor position will be adjusted to stay in the viewport using the same logic as [`CursorMove::InViewport`].
/// be adjusted to stay in the viewport using the same logic as [`CursorMove::InViewport`].
/// ///
/// ``` /// ```
/// # use tui::buffer::Buffer; /// # use tui::buffer::Buffer;
@@ -1549,13 +1599,13 @@ impl<'a> TextArea<'a> {
/// ///
/// // Scroll down by 15 lines. Since terminal height is 8, cursor will go out /// // Scroll down by 15 lines. Since terminal height is 8, cursor will go out
/// // the viewport. /// // the viewport.
/// textarea.scroll(15, 0); /// textarea.scroll((15, 0));
/// // So the cursor position was updated to stay in the viewport after the scrolling. /// // So the cursor position was updated to stay in the viewport after the scrolling.
/// assert_eq!(textarea.cursor(), (15, 0)); /// assert_eq!(textarea.cursor(), (15, 0));
/// ///
/// // Scroll up by 5 lines. Since the scroll amount is smaller than the terminal /// // Scroll up by 5 lines. Since the scroll amount is smaller than the terminal
/// // height, cursor position will not be updated. /// // height, cursor position will not be updated.
/// textarea.scroll(-5, 0); /// textarea.scroll((-5, 0));
/// assert_eq!(textarea.cursor(), (15, 0)); /// assert_eq!(textarea.cursor(), (15, 0));
/// ///
/// // Scroll up by 5 lines again. The terminal height is 8. So a cursor reaches to /// // Scroll up by 5 lines again. The terminal height is 8. So a cursor reaches to
@@ -1563,11 +1613,11 @@ impl<'a> TextArea<'a> {
/// // scrolled up by 5 lines, scrolling up by 5 lines again makes the cursor overrun /// // scrolled up by 5 lines, scrolling up by 5 lines again makes the cursor overrun
/// // the viewport by 5 - 2 = 3 lines. To keep the cursor stay in the viewport, the /// // the viewport by 5 - 2 = 3 lines. To keep the cursor stay in the viewport, the
/// // cursor position will be adjusted from line 15 to line 12. /// // cursor position will be adjusted from line 15 to line 12.
/// textarea.scroll(-5, 0); /// textarea.scroll((-5, 0));
/// assert_eq!(textarea.cursor(), (12, 0)); /// assert_eq!(textarea.cursor(), (12, 0));
/// ``` /// ```
pub fn scroll(&mut self, rows: i16, cols: i16) { pub fn scroll(&mut self, scrolling: impl Into<Scrolling>) {
self.viewport.scroll(rows, cols); scrolling.into().scroll(&mut self.viewport);
self.move_cursor(CursorMove::InViewport); self.move_cursor(CursorMove::InViewport);
} }
} }