1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crossterm::terminal;
const DEFAULT_TERMINAL_WIDTH: u16 = 50;
const DEFAULT_TERMINAL_HEIGHT: u16 = 20;
pub trait AreaContent {
fn height() -> u16;
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Area {
pub left: u16,
pub top: u16,
pub width: u16,
pub height: u16,
}
fn div_ceil(a: i32, b: i32) -> i32 {
a / b + if a % b != 0 { 1 } else { 0 }
}
impl Area {
pub fn uninitialized() -> Area {
Area {
left: 0,
top: 0,
height: 1,
width: 5,
}
}
pub fn new(left: u16, top: u16, width: u16, height: u16) -> Area {
Area {
left,
top,
width,
height,
}
}
pub fn full_screen() -> Area {
let (width, height) = terminal_size();
Area {
left: 0,
top: 0,
width,
height,
}
}
pub fn contains(&self, x: u16, y: u16) -> bool {
x >= self.left
&& x < self.left + self.width
&& y >= self.top
&& y < self.top + self.height
}
pub fn pad(&mut self, dx: u16, dy: u16) {
self.left += dx;
self.top += dy;
self.width -= 2 * dx;
self.height -= 2 * dy;
}
pub fn pad_for_max_width(&mut self, max_width: u16) {
if max_width >= self.width {
return;
}
let pw = self.width - max_width;
self.left += pw / 2;
self.width -= pw;
}
pub fn scrollbar(
&self,
scroll: i32,
content_height: i32,
) -> Option<(u16, u16)> {
compute_scrollbar(scroll, content_height, i32::from(self.height), self.top)
}
}
pub fn compute_scrollbar(
scroll: i32,
content_height: i32,
available_height: i32,
top: u16,
) -> Option<(u16, u16)> {
let h = available_height;
if content_height <= h {
return None;
}
let sc = div_ceil(scroll * h, content_height);
let hidden_tail = content_height - scroll - h;
let se = div_ceil(hidden_tail * h, content_height);
Some((
sc as u16,
if h > sc + se {
top + (h - se) as u16
} else {
top + sc as u16 + 1
},
))
}
pub fn terminal_size() -> (u16, u16) {
let size = terminal::size();
size.unwrap_or((DEFAULT_TERMINAL_WIDTH, DEFAULT_TERMINAL_HEIGHT))
}