gm_lib/tui/app/widgets/
scroll_bar.rs1use ratatui::{layout::Offset, widgets::Widget};
2
3pub struct CustomScrollBar {
4 pub cursor: usize,
5 pub total: usize,
6}
7
8impl Widget for CustomScrollBar {
9 fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
10 where
11 Self: Sized,
12 {
13 let capacity = area.height as usize;
14 let num_pages = self.total.div_ceil(capacity);
15 let current_page = self.cursor / capacity;
16
17 let top = (0..current_page)
18 .map(|i| get_page_height(i, capacity, num_pages))
19 .sum::<usize>();
20 let middle = get_page_height(current_page, capacity, num_pages);
21 let bottom = ((current_page + 1)..num_pages)
22 .map(|i| get_page_height(i, capacity, num_pages))
23 .sum::<usize>();
24 assert_eq!(
25 top + middle + bottom,
26 capacity,
27 "self.total = {}, capacity = {}, current_page = {current_page}, num_pages = {num_pages}, top = {top}, middle = {middle}, bottom = {bottom}",
28 self.total, capacity
29 );
30
31 let mut i = 0;
32 for _ in 0..top {
33 "║".render(area.offset(Offset { x: 0, y: i }), buf);
34 i += 1;
35 }
36 for _ in 0..middle {
37 "█".render(area.offset(Offset { x: 0, y: i }), buf);
38 i += 1;
39 }
40 for _ in 0..bottom {
41 "║".render(area.offset(Offset { x: 0, y: i }), buf);
42 i += 1;
43 }
44 }
45}
46
47fn get_page_height(i: usize, capacity: usize, num_pages: usize) -> usize {
48 let base = capacity / num_pages;
49 if i < capacity % num_pages {
50 base + 1
51 } else {
52 base
53 }
54}
55
56#[cfg(test)]
57mod test {
58 use super::*;
59
60 #[test]
61 fn test_get_page_height() {
62 for capacity in 10..100 {
63 for num_pages in 1..15 {
64 let mut sum = 0;
65 for i in 0..num_pages {
66 sum += get_page_height(i, capacity, num_pages);
67 }
68
69 if sum != capacity {
70 println!("capacity: {capacity}, num_pages: {num_pages}");
71 for i in 0..num_pages {
72 let h = get_page_height(i, capacity, num_pages);
73 println!("page {i}: {h}");
74 }
75 }
76
77 assert_eq!(
78 sum, capacity,
79 "capacity: {capacity}, num_pages: {num_pages}"
80 );
81 }
82 }
83 }
84}