tui_textarea/
scroll.rs

1use crate::widget::Viewport;
2#[cfg(feature = "serde")]
3use serde::{Deserialize, Serialize};
4
5/// Specify how to scroll the textarea.
6///
7/// This type is marked as `#[non_exhaustive]` since more variations may be supported in the future. Note that the cursor will
8/// not move until it goes out the viewport. See also: [`TextArea::scroll`]
9///
10/// [`TextArea::scroll`]: https://docs.rs/tui-textarea/latest/tui_textarea/struct.TextArea.html#method.scroll
11#[non_exhaustive]
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub enum Scrolling {
15    /// Scroll the textarea by rows (vertically) and columns (horizontally). Passing positive scroll amounts to `rows` and `cols`
16    /// scolls it to down and right. Negative integers means the opposite directions. `(i16, i16)` pair can be converted into
17    /// `Scrolling::Delta` where 1st element means rows and 2nd means columns.
18    ///
19    /// ```
20    /// # use ratatui::buffer::Buffer;
21    /// # use ratatui::layout::Rect;
22    /// # use ratatui::widgets::Widget as _;
23    /// use tui_textarea::{TextArea, Scrolling};
24    ///
25    /// // Let's say terminal height is 8.
26    ///
27    /// // Create textarea with 20 lines "0", "1", "2", "3", ...
28    /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect();
29    /// # // Call `render` at least once to populate terminal size
30    /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 };
31    /// # let mut b = Buffer::empty(r.clone());
32    /// # textarea.render(r, &mut b);
33    ///
34    /// // Scroll down by 2 lines.
35    /// textarea.scroll(Scrolling::Delta{rows: 2, cols: 0});
36    /// assert_eq!(textarea.cursor(), (2, 0));
37    ///
38    /// // (1, 0) is converted into Scrolling::Delta{rows: 1, cols: 0}
39    /// textarea.scroll((1, 0));
40    /// assert_eq!(textarea.cursor(), (3, 0));
41    /// ```
42    Delta { rows: i16, cols: i16 },
43    /// Scroll down the textarea by one page.
44    ///
45    /// ```
46    /// # use ratatui::buffer::Buffer;
47    /// # use ratatui::layout::Rect;
48    /// # use ratatui::widgets::Widget as _;
49    /// use tui_textarea::{TextArea, Scrolling};
50    ///
51    /// // Let's say terminal height is 8.
52    ///
53    /// // Create textarea with 20 lines "0", "1", "2", "3", ...
54    /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect();
55    /// # // Call `render` at least once to populate terminal size
56    /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 };
57    /// # let mut b = Buffer::empty(r.clone());
58    /// # textarea.render(r, &mut b);
59    ///
60    /// // Scroll down by one page (8 lines)
61    /// textarea.scroll(Scrolling::PageDown);
62    /// assert_eq!(textarea.cursor(), (8, 0));
63    /// textarea.scroll(Scrolling::PageDown);
64    /// assert_eq!(textarea.cursor(), (16, 0));
65    /// textarea.scroll(Scrolling::PageDown);
66    /// assert_eq!(textarea.cursor(), (19, 0)); // Reached bottom of the textarea
67    /// ```
68    PageDown,
69    /// Scroll up the textarea by one page.
70    ///
71    /// ```
72    /// # use ratatui::buffer::Buffer;
73    /// # use ratatui::layout::Rect;
74    /// # use ratatui::widgets::Widget as _;
75    /// use tui_textarea::{TextArea, Scrolling, CursorMove};
76    ///
77    /// // Let's say terminal height is 8.
78    ///
79    /// // Create textarea with 20 lines "0", "1", "2", "3", ...
80    /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect();
81    /// # // Call `render` at least once to populate terminal size
82    /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 };
83    /// # let mut b = Buffer::empty(r.clone());
84    /// # textarea.render(r.clone(), &mut b);
85    ///
86    /// // Go to the last line at first
87    /// textarea.move_cursor(CursorMove::Bottom);
88    /// assert_eq!(textarea.cursor(), (19, 0));
89    /// # // Call `render` to populate terminal size
90    /// # textarea.render(r.clone(), &mut b);
91    ///
92    /// // Scroll up by one page (8 lines)
93    /// textarea.scroll(Scrolling::PageUp);
94    /// assert_eq!(textarea.cursor(), (11, 0));
95    /// textarea.scroll(Scrolling::PageUp);
96    /// assert_eq!(textarea.cursor(), (7, 0)); // Reached top of the textarea
97    /// ```
98    PageUp,
99    /// Scroll down the textarea by half of the page.
100    ///
101    /// ```
102    /// # use ratatui::buffer::Buffer;
103    /// # use ratatui::layout::Rect;
104    /// # use ratatui::widgets::Widget as _;
105    /// use tui_textarea::{TextArea, Scrolling};
106    ///
107    /// // Let's say terminal height is 8.
108    ///
109    /// // Create textarea with 10 lines "0", "1", "2", "3", ...
110    /// let mut textarea: TextArea = (0..10).into_iter().map(|i| i.to_string()).collect();
111    /// # // Call `render` at least once to populate terminal size
112    /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 };
113    /// # let mut b = Buffer::empty(r.clone());
114    /// # textarea.render(r, &mut b);
115    ///
116    /// // Scroll down by half-page (4 lines)
117    /// textarea.scroll(Scrolling::HalfPageDown);
118    /// assert_eq!(textarea.cursor(), (4, 0));
119    /// textarea.scroll(Scrolling::HalfPageDown);
120    /// assert_eq!(textarea.cursor(), (8, 0));
121    /// textarea.scroll(Scrolling::HalfPageDown);
122    /// assert_eq!(textarea.cursor(), (9, 0)); // Reached bottom of the textarea
123    /// ```
124    HalfPageDown,
125    /// Scroll up the textarea by half of the page.
126    ///
127    /// ```
128    /// # use ratatui::buffer::Buffer;
129    /// # use ratatui::layout::Rect;
130    /// # use ratatui::widgets::Widget as _;
131    /// use tui_textarea::{TextArea, Scrolling, CursorMove};
132    ///
133    /// // Let's say terminal height is 8.
134    ///
135    /// // Create textarea with 20 lines "0", "1", "2", "3", ...
136    /// let mut textarea: TextArea = (0..20).into_iter().map(|i| i.to_string()).collect();
137    /// # // Call `render` at least once to populate terminal size
138    /// # let r = Rect { x: 0, y: 0, width: 24, height: 8 };
139    /// # let mut b = Buffer::empty(r.clone());
140    /// # textarea.render(r.clone(), &mut b);
141    ///
142    /// // Go to the last line at first
143    /// textarea.move_cursor(CursorMove::Bottom);
144    /// assert_eq!(textarea.cursor(), (19, 0));
145    /// # // Call `render` to populate terminal size
146    /// # textarea.render(r.clone(), &mut b);
147    ///
148    /// // Scroll up by half-page (4 lines)
149    /// textarea.scroll(Scrolling::HalfPageUp);
150    /// assert_eq!(textarea.cursor(), (15, 0));
151    /// textarea.scroll(Scrolling::HalfPageUp);
152    /// assert_eq!(textarea.cursor(), (11, 0));
153    /// ```
154    HalfPageUp,
155}
156
157impl Scrolling {
158    pub(crate) fn scroll(self, viewport: &mut Viewport) {
159        let (rows, cols) = match self {
160            Self::Delta { rows, cols } => (rows, cols),
161            Self::PageDown => {
162                let (_, _, _, height) = viewport.rect();
163                (height as i16, 0)
164            }
165            Self::PageUp => {
166                let (_, _, _, height) = viewport.rect();
167                (-(height as i16), 0)
168            }
169            Self::HalfPageDown => {
170                let (_, _, _, height) = viewport.rect();
171                ((height as i16) / 2, 0)
172            }
173            Self::HalfPageUp => {
174                let (_, _, _, height) = viewport.rect();
175                (-(height as i16) / 2, 0)
176            }
177        };
178        viewport.scroll(rows, cols);
179    }
180}
181
182impl From<(i16, i16)> for Scrolling {
183    fn from((rows, cols): (i16, i16)) -> Self {
184        Self::Delta { rows, cols }
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    // Separate tests for tui-rs support
193    #[test]
194    fn delta() {
195        use crate::ratatui::buffer::Buffer;
196        use crate::ratatui::layout::Rect;
197        use crate::ratatui::widgets::Widget as _;
198        use crate::TextArea;
199
200        let mut textarea: TextArea = (0..20).map(|i| i.to_string()).collect();
201        let r = Rect {
202            x: 0,
203            y: 0,
204            width: 24,
205            height: 8,
206        };
207        let mut b = Buffer::empty(r);
208        textarea.render(r, &mut b);
209
210        textarea.scroll(Scrolling::Delta { rows: 2, cols: 0 });
211        assert_eq!(textarea.cursor(), (2, 0));
212
213        textarea.scroll((1, 0));
214        assert_eq!(textarea.cursor(), (3, 0));
215    }
216}