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
use std::io;
use std::ops::Rem;
use std::iter::repeat;
use console::{Key, Term};
/// Renders a multi select checkbox menu.
pub struct Checkbox {
items: Vec<String>,
default_selected: bool,
clear: bool,
}
impl Checkbox {
/// Creates the prompt with a specific text.
pub fn new() -> Self {
Checkbox {
items: vec!["ALL".to_owned()],
default_selected: false,
clear: true,
}
}
/// Sets the clear behavior of the checkbox menu.
///
/// The default is to clear the checkbox menu.
pub fn clear(&mut self, val: bool) -> &mut Self {
self.clear = val;
self
}
/// The default is select the checkbox
pub fn default(&mut self, selected: bool) -> &mut Self {
self.default_selected = selected;
self
}
/// Add a single item to the selector.
pub fn item(&mut self, item: &str) -> &mut Self {
self.items.push(item.to_string());
self
}
/// Adds multiple items to the selector.
pub fn items(&mut self, items: &[&str]) -> &mut Self {
for item in items {
self.items.push(item.to_string());
}
self
}
/// Enables user interaction and returns the result.
///
/// The user can select the items with the space bar and on enter
/// the selected items will be returned.
pub fn interact(&self) -> io::Result<Vec<usize>> {
self.interact_on(&Term::stderr())
}
/// Like `interact` but allows a specific terminal to be set.
pub fn interact_on(&self, term: &Term) -> io::Result<Vec<usize>> {
let mut sel = 0;
let mut selected: Vec<_> = repeat(self.default_selected)
.take(self.items.len())
.collect();
loop {
for (idx, item) in self.items.iter().enumerate() {
term.write_line(&format!(
"{} [{}] {}",
if sel == idx { ">" } else { " " },
if selected[idx] { "x" } else { " " },
item,
))?;
}
match term.read_key()? {
Key::ArrowDown | Key::Char('j') => {
if sel == !0 {
sel = 0;
} else {
sel = (sel as u64 + 1).rem(self.items.len() as u64) as usize;
}
}
Key::ArrowUp | Key::Char('k') => {
if sel == !0 {
sel = self.items.len() - 1;
} else {
sel = ((sel as i64 - 1 + self.items.len() as i64) %
(self.items.len() as i64)) as usize;
}
}
Key::Char(' ') => {
selected[sel] = !selected[sel];
if sel == 0 {
let result = selected[0];
selected.iter_mut().skip(1).for_each(|x| {
*x = result;
});
}
if selected.iter().skip(1).position(|x| !x).is_some() {
selected[0] = false;
}
if selected.iter().skip(1).all(|x| *x) {
selected[0] = true;
}
}
Key::Escape => {
if self.clear {
term.clear_last_lines(self.items.len())?;
}
return Ok(vec![]);
},
Key::Enter => {
if self.clear {
term.clear_last_lines(self.items.len())?;
}
return Ok(
selected.into_iter()
.skip(1)
.enumerate()
.filter_map(|(idx, selected)| if selected { Some(idx) } else { None })
.collect()
);
}
_ => {}
}
term.clear_last_lines(self.items.len())?;
}
}
}