tui_dispatch_debug/debug/
scroll_state.rs1use crossterm::event::KeyCode;
4
5#[derive(Debug, Clone)]
10pub struct ScrollState {
11 pub offset: usize,
13 pub page_size: usize,
15}
16
17impl Default for ScrollState {
18 fn default() -> Self {
19 Self {
20 offset: 0,
21 page_size: 1,
22 }
23 }
24}
25
26impl ScrollState {
27 pub fn new() -> Self {
29 Self::default()
30 }
31
32 fn page_size_value(&self) -> usize {
33 self.page_size.max(1)
34 }
35
36 fn max_offset(&self, content_len: usize) -> usize {
37 content_len.saturating_sub(self.page_size_value())
38 }
39
40 pub fn scroll_up(&mut self) {
42 self.offset = self.offset.saturating_sub(1);
43 }
44
45 pub fn scroll_down(&mut self, content_len: usize) {
47 let max = self.max_offset(content_len);
48 self.offset = (self.offset + 1).min(max);
49 }
50
51 pub fn to_top(&mut self) {
53 self.offset = 0;
54 }
55
56 pub fn to_bottom(&mut self, content_len: usize) {
58 self.offset = self.max_offset(content_len);
59 }
60
61 pub fn page_up(&mut self) {
63 let ps = self.page_size_value();
64 self.offset = self.offset.saturating_sub(ps);
65 }
66
67 pub fn page_down(&mut self, content_len: usize) {
69 let ps = self.page_size_value();
70 let max = self.max_offset(content_len);
71 self.offset = (self.offset + ps).min(max);
72 }
73
74 pub fn handle_scroll_key(&mut self, key: KeyCode, content_len: usize) -> bool {
78 match key {
79 KeyCode::Char('j') | KeyCode::Down => {
80 self.scroll_down(content_len);
81 true
82 }
83 KeyCode::Char('k') | KeyCode::Up => {
84 self.scroll_up();
85 true
86 }
87 KeyCode::Char('g') | KeyCode::Home => {
88 self.to_top();
89 true
90 }
91 KeyCode::Char('G') | KeyCode::End => {
92 self.to_bottom(content_len);
93 true
94 }
95 KeyCode::PageDown => {
96 self.page_down(content_len);
97 true
98 }
99 KeyCode::PageUp => {
100 self.page_up();
101 true
102 }
103 _ => false,
104 }
105 }
106
107 pub fn reset(&mut self) {
109 self.offset = 0;
110 self.page_size = 1;
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn scroll_state_basic_navigation() {
120 let mut s = ScrollState::new();
121 s.page_size = 5;
122
123 s.scroll_down(20);
124 assert_eq!(s.offset, 1);
125
126 s.scroll_up();
127 assert_eq!(s.offset, 0);
128
129 s.scroll_up();
131 assert_eq!(s.offset, 0);
132
133 s.to_bottom(20);
134 assert_eq!(s.offset, 15); s.to_top();
137 assert_eq!(s.offset, 0);
138 }
139
140 #[test]
141 fn scroll_state_page_navigation() {
142 let mut s = ScrollState::new();
143 s.page_size = 5;
144
145 s.page_down(20);
146 assert_eq!(s.offset, 5);
147
148 s.page_down(20);
149 assert_eq!(s.offset, 10);
150
151 s.page_up();
152 assert_eq!(s.offset, 5);
153
154 s.offset = 14;
156 s.page_down(20);
157 assert_eq!(s.offset, 15);
158 }
159
160 #[test]
161 fn scroll_state_handle_key() {
162 let mut s = ScrollState::new();
163 s.page_size = 5;
164
165 assert!(s.handle_scroll_key(KeyCode::Char('j'), 10));
166 assert_eq!(s.offset, 1);
167
168 assert!(s.handle_scroll_key(KeyCode::Char('k'), 10));
169 assert_eq!(s.offset, 0);
170
171 assert!(s.handle_scroll_key(KeyCode::Char('G'), 10));
172 assert_eq!(s.offset, 5);
173
174 assert!(s.handle_scroll_key(KeyCode::Char('g'), 10));
175 assert_eq!(s.offset, 0);
176
177 assert!(!s.handle_scroll_key(KeyCode::Char('x'), 10));
178 }
179
180 #[test]
181 fn scroll_state_reset() {
182 let mut s = ScrollState {
183 offset: 5,
184 page_size: 10,
185 };
186 s.reset();
187 assert_eq!(s.offset, 0);
188 assert_eq!(s.page_size, 1);
189 }
190}