1use std::io;
2use std::io::Write;
3use termion::{event::Key, input::TermRead, raw::IntoRawMode};
4
5#[derive(Debug)]
6pub struct Picker<'a> {
7 pub available_options: &'a Vec<&'a str>,
8 pub selected_options_indexes: Option<Vec<usize>>,
9}
10
11impl<'a> Picker<'a> {
12 pub fn new(options: &'a Vec<&str>) -> Picker<'a> {
13 Picker {
14 available_options: options,
15 selected_options_indexes: None,
16 }
17 }
18
19 pub fn select(&mut self) {
20 if self.selected_options_indexes.is_none() {
21 let mut selected: Vec<usize> = vec![];
22 let mut current_index: usize = 0;
23
24 let stdin = io::stdin();
25 let mut input = stdin.lock().keys();
26 let mut stdout = io::stdout().into_raw_mode().unwrap();
27 loop {
28 write!(
29 stdout,
30 "{} {}",
31 termion::cursor::Goto(0, 2),
32 termion::clear::AfterCursor
33 )
34 .unwrap();
35 write!(stdout, "Available options:\r\n").unwrap();
36 for (index, value) in self.available_options.iter().enumerate() {
37 let is_selected = selected.contains(&index);
38 let is_focused = index == current_index;
39 let selected_indicator = if is_selected { "[x]" } else { "[ ]" };
40 let focused_indicator = if is_focused { "<" } else { "" };
41 write!(
42 stdout,
43 "{} {} {}\r\n",
44 selected_indicator, value, focused_indicator,
45 )
46 .unwrap();
47 }
48 write!(
49 stdout,
50 "Use K or ↑ (UP ARROW) and J or ↓ (DOWN ARROW) to select or deselect an item. Press [ENTER] to confirm your selection{}\r\n",
51 termion::cursor::Hide,
52 )
53 .unwrap();
54 stdout.flush().unwrap();
55 if let Some(Ok(key_event)) = input.next() {
56 match key_event {
57 Key::Char('j') | Key::Down => {
58 if current_index < self.available_options.len() - 1 {
59 current_index += 1;
60 }
61 }
62 Key::Char('k') | Key::Up => {
63 if current_index > 0 {
64 current_index = current_index.saturating_sub(1);
65 }
66 }
67 Key::Char(' ') => {
68 let is_selected = selected.contains(¤t_index);
69 if is_selected {
70 if let Some(to_remove) =
71 selected.iter().position(|x| x == ¤t_index)
72 {
73 selected.remove(to_remove);
74 }
75 } else {
76 selected.push(current_index);
77 }
78 }
79 Key::Char('\n') => {
80 self.selected_options_indexes = Some(selected.clone());
81 break;
82 },
83 _ => {}
84 }
85 }
86 }
87 }
88 }
89}