ratatui_kit/components/scroll_view/
state.rs

1use crossterm::event::{Event, KeyCode, KeyEventKind, MouseEventKind};
2use ratatui::layout::{Position, Size};
3
4#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
5pub struct ScrollViewState {
6    /// 偏移量是滚动视图需要移动的行数和列数。
7    pub(crate) offset: Position,
8    /// 滚动视图的尺寸。在第一次渲染调用前不会被设置。
9    pub(crate) size: Option<Size>,
10    /// 滚动视图一页的尺寸。在第一次渲染调用前不会被设置。
11    pub(crate) page_size: Option<Size>,
12}
13
14impl ScrollViewState {
15    /// 创建一个偏移量为 (0, 0) 的新滚动视图状态
16    pub fn new() -> Self {
17        Self::default()
18    }
19
20    /// 创建一个带有指定偏移量的新滚动视图状态
21    pub fn with_offset(offset: Position) -> Self {
22        Self {
23            offset,
24            ..Default::default()
25        }
26    }
27
28    /// 设置滚动视图状态的偏移量
29    pub const fn set_offset(&mut self, offset: Position) {
30        self.offset = offset;
31    }
32
33    /// 获取滚动视图状态的偏移量
34    pub const fn offset(&self) -> Position {
35        self.offset
36    }
37
38    /// 向上滚动一行
39    pub const fn scroll_up(&mut self) {
40        self.offset.y = self.offset.y.saturating_sub(1);
41    }
42
43    /// 向下滚动一行
44    pub const fn scroll_down(&mut self) {
45        self.offset.y = self.offset.y.saturating_add(1);
46    }
47
48    /// 向下滚动一页
49    pub fn scroll_page_down(&mut self) {
50        let page_size = self.page_size.map_or(1, |size| size.height);
51        // 我们减去 1 以确保页面之间有一行重叠
52        self.offset.y = self.offset.y.saturating_add(page_size).saturating_sub(1);
53    }
54
55    /// 向上滚动一页
56    pub fn scroll_page_up(&mut self) {
57        let page_size = self.page_size.map_or(1, |size| size.height);
58        // 我们加上 1 以确保页面之间有一行重叠
59        self.offset.y = self.offset.y.saturating_add(1).saturating_sub(page_size);
60    }
61
62    /// 向左滚动一列
63    pub const fn scroll_left(&mut self) {
64        self.offset.x = self.offset.x.saturating_sub(1);
65    }
66
67    /// 向右滚动一列
68    pub const fn scroll_right(&mut self) {
69        self.offset.x = self.offset.x.saturating_add(1);
70    }
71
72    /// 滚动到缓冲区顶部
73    pub const fn scroll_to_top(&mut self) {
74        self.offset = Position::ORIGIN;
75    }
76
77    /// 滚动到缓冲区底部
78    pub fn scroll_to_bottom(&mut self) {
79        // 渲染调用会调整偏移量以确保不会滚动到缓冲区末尾之后,所以这里可以将偏移量设置为最大值
80        let bottom = self
81            .size
82            .map_or(u16::MAX, |size| size.height.saturating_sub(1));
83        self.offset.y = bottom;
84    }
85
86    pub fn handle_event(&mut self, event: &Event) {
87        match event {
88            Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
89                KeyCode::Up | KeyCode::Char('k') => {
90                    self.scroll_up();
91                }
92                KeyCode::Down | KeyCode::Char('j') => {
93                    self.scroll_down();
94                }
95                KeyCode::Left | KeyCode::Char('h') => {
96                    self.scroll_left();
97                }
98                KeyCode::Right | KeyCode::Char('l') => {
99                    self.scroll_right();
100                }
101                KeyCode::PageUp => {
102                    self.scroll_page_up();
103                }
104                KeyCode::PageDown => {
105                    self.scroll_page_down();
106                }
107                KeyCode::Home => {
108                    self.scroll_to_top();
109                }
110                KeyCode::End => {
111                    self.scroll_to_bottom();
112                }
113                _ => {}
114            },
115            Event::Mouse(event) => match event.kind {
116                MouseEventKind::ScrollDown => {
117                    self.scroll_down();
118                }
119                MouseEventKind::ScrollUp => {
120                    self.scroll_up();
121                }
122                MouseEventKind::ScrollLeft => {
123                    self.scroll_left();
124                }
125                MouseEventKind::ScrollRight => {
126                    self.scroll_right();
127                }
128                _ => {}
129            },
130            _ => {}
131        }
132    }
133}