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
use crate::logging::{Logger, LoggerRefreshItemKind, LoggerTextItem};
use crate::types::ErrBox;
use crate::terminal::read_terminal_event;
use crossterm::event::{Event, KeyCode};
struct MultiSelectData<'a> {
prompt: &'a str,
item_hanging_indent: u16,
items: Vec<(bool, &'a String)>,
active_index: usize,
}
pub fn show_multi_select(
logger: &Logger,
context_name: &str,
prompt: &str,
item_hanging_indent: u16,
items: Vec<(bool, &String)>
) -> Result<Vec<usize>, ErrBox> {
let mut data = MultiSelectData {
prompt,
items,
item_hanging_indent,
active_index: 0,
};
loop {
let text_items = render_multi_select(&data);
logger.set_refresh_item(LoggerRefreshItemKind::Selection, text_items);
match read_terminal_event()? {
Event::Key(key_event) => {
match &key_event.code {
KeyCode::Up => {
if data.active_index == 0 {
data.active_index = data.items.len() - 1;
} else {
data.active_index -= 1;
}
},
KeyCode::Down => {
data.active_index = (data.active_index + 1) % data.items.len();
},
KeyCode::Char(' ') => {
let mut current_item = data.items.get_mut(data.active_index).unwrap();
current_item.0 = !current_item.0;
},
KeyCode::Enter => {
break;
},
KeyCode::Esc => {
logger.remove_refresh_item(LoggerRefreshItemKind::Selection);
return err!("Selection cancelled.");
}
_ => {}
}
},
_ => {
}
}
}
logger.remove_refresh_item(LoggerRefreshItemKind::Selection);
logger.log_text_items(&render_complete(&data), context_name, crate::terminal::get_terminal_width());
let mut result = Vec::new();
for (i, (is_selected, _)) in data.items.iter().enumerate() {
if *is_selected { result.push(i); }
}
Ok(result)
}
fn render_multi_select(data: &MultiSelectData) -> Vec<LoggerTextItem> {
let mut result = Vec::new();
result.push(LoggerTextItem::Text(data.prompt.to_string()));
for (i, (is_selected, item_text)) in data.items.iter().enumerate() {
let mut text = String::new();
text.push_str(if i == data.active_index {
">"
} else {
" "
});
text.push_str(" [");
text.push_str(if *is_selected {
"x"
} else {
" "
});
text.push_str("] ");
text.push_str(item_text);
result.push(LoggerTextItem::HangingText {
text,
indent: 7 + data.item_hanging_indent,
});
}
result
}
fn render_complete(data: &MultiSelectData) -> Vec<LoggerTextItem> {
let mut result = Vec::new();
if data.items.iter().any(|(is_selected, _)| *is_selected) {
result.push(LoggerTextItem::Text(data.prompt.to_string()));
for (is_selected, item_text) in data.items.iter() {
if *is_selected {
result.push(LoggerTextItem::HangingText {
text: format!(" * {}", item_text),
indent: 3 + data.item_hanging_indent,
});
}
}
}
result
}