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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use std::cmp::{max, min};
/// Holds the information about the displayed window of lines.
/// When there's too much lines to display in one screen, we can scroll
/// and this struct is responsible for that.
/// Scrolling is done with `scroll_to`, `scroll_up_one`, `scroll_down_one`
/// methods.
#[derive(Debug, Clone)]
pub struct ContentWindow {
/// The index of the first displayed element.
pub top: usize,
/// The index of the last displayed element + 1.
pub bottom: usize,
/// The number of displayble elements.
pub len: usize,
/// The height of the rect containing the elements.
pub height: usize,
}
impl Default for ContentWindow {
fn default() -> Self {
Self::new(0, 80)
}
}
impl ContentWindow {
/// The padding around the last displayed filename
pub const WINDOW_PADDING: usize = 4;
pub const WINDOW_PADDING_FUZZY: u32 = 3;
/// The space of the top element as an u16 for convenience
pub const WINDOW_MARGIN_TOP_U16: u16 = 2;
/// The space for the bottom row
pub const WINDOW_MARGIN_BOTTOM: usize = 1;
/// How many rows are reserved for the header ?
pub const HEADER_ROWS: usize = 3;
/// How many rows are reserved for the footer ?
const FOOTER_ROWS: usize = 2;
/// How many rows could be displayed with given height ?
/// It's not the number of rows displayed since the content may
/// not be long enough to fill the window.
fn nb_displayed_rows(rect_height: usize) -> usize {
rect_height.saturating_sub(Self::HEADER_ROWS + Self::FOOTER_ROWS)
}
/// Default value for the bottom index.
/// minimum of terminal height minus reserved rows and the length of the content.
fn default_bottom(len: usize, used_height: usize) -> usize {
min(len, used_height)
}
/// Returns a new `ContentWindow` instance with values depending of
/// number of displayable elements and height of the terminal screen.
pub fn new(len: usize, rect_height: usize) -> Self {
let height = Self::nb_displayed_rows(rect_height);
let top = 0;
let bottom = Self::default_bottom(len, height);
ContentWindow {
top,
bottom,
len,
height,
}
}
/// Set the height of file window.
pub fn set_height(&mut self, terminal_height: usize) {
self.height = Self::nb_displayed_rows(terminal_height);
self.bottom = Self::default_bottom(self.len, self.height);
}
pub fn set_len(&mut self, len: usize) {
self.len = len;
self.bottom = Self::default_bottom(len, self.height);
}
/// Move the window one line up if possible.
/// Does nothing if the index can't be reached.
pub fn scroll_up_one(&mut self, index: usize) {
if (index < self.top + Self::WINDOW_PADDING || index > self.bottom) && self.top > 0 {
self.top -= 1;
self.bottom -= 1;
}
self.scroll_to(index)
}
/// Move the window one line down if possible.
/// Does nothing if the index can't be reached.
pub fn scroll_down_one(&mut self, index: usize) {
if self.len < self.height {
return;
}
if index < self.top || index + Self::WINDOW_PADDING > self.bottom {
self.top += 1;
self.bottom += 1;
}
self.scroll_to(index)
}
/// Scroll the window to this index if possible.
/// Does nothing if the index can't be reached.
pub fn scroll_to(&mut self, index: usize) {
if self.len < self.height {
return;
}
if self.is_index_outside_window(index) {
let height = max(self.bottom.saturating_sub(self.top), self.height);
self.top = index.saturating_sub(Self::WINDOW_PADDING);
self.bottom = self.top + height;
}
}
/// Reset the window to the first item of the content.
pub fn reset(&mut self, len: usize) {
self.len = len;
self.top = 0;
self.bottom = Self::default_bottom(self.len, self.height);
}
/// True iff the index is outside the displayed window or
/// too close from the border.
/// User shouldn't be able to reach the last elements
fn is_index_outside_window(&self, index: usize) -> bool {
index < self.top || index >= self.bottom
}
pub fn is_row_in_header(row: u16) -> bool {
row < Self::HEADER_ROWS as u16
}
pub fn preview_page_up(&mut self, skip: usize) {
if self.top == 0 {
return;
}
let skip = min(self.top, skip);
self.bottom -= skip;
self.top -= skip;
}
pub fn preview_page_down(&mut self, skip: usize, preview_len: usize) {
if self.bottom < preview_len {
let skip = min(preview_len - self.bottom, skip);
self.bottom += skip;
self.top += skip;
}
}
}