#![allow(clippy::needless_return)]
#![allow(clippy::redundant_field_names)]
use console::{Key, Style, Term};
pub struct MenuOption {
label: String,
func: fn() -> (),
hint: Option<String>,
}
pub struct Menu {
options: Vec<MenuOption>,
selected_option: usize,
selected_style: Style,
normal_style: Style,
hint_style: Style,
action_func: fn() -> (),
}
impl MenuOption {
pub fn new(label: &str, func: fn() -> ()) -> MenuOption {
return MenuOption {
label: label.to_owned(),
func: func,
hint: None,
};
}
pub fn hint(mut self, text: &str) -> MenuOption {
self.hint = Some(text.to_owned());
return self;
}
}
impl Menu {
pub fn new(options: Vec<MenuOption>) -> Menu {
return Menu {
options: options,
selected_option: 0,
normal_style: Style::new(),
selected_style: Style::new().on_blue(),
hint_style: Style::new().color256(187),
action_func: dummy_func,
};
}
pub fn show(mut self) {
let stdout = Term::buffered_stdout();
stdout.hide_cursor().unwrap();
stdout.clear_screen().unwrap();
self.draw_menu(&stdout);
self.menu_navigation(&stdout);
stdout.clear_screen().unwrap();
stdout.flush().unwrap();
(self.action_func)();
}
fn menu_navigation(&mut self, stdout: &Term) {
let options_limit_num = self.options.len() - 1;
loop {
let key = match stdout.read_key() {
Ok(val) => val,
Err(_e) => {
println!("Error reading key");
return;
}
};
match key {
Key::ArrowUp => {
self.selected_option = match self.selected_option == 0 {
true => options_limit_num,
false => self.selected_option - 1,
}
}
Key::ArrowDown => {
self.selected_option = match self.selected_option == options_limit_num {
true => 0,
false => self.selected_option + 1,
}
}
Key::Escape => return,
Key::Enter => {
self.action_func = self.options[self.selected_option].func;
return;
}
_ => {}
}
self.draw_menu(stdout);
}
}
fn draw_menu(&self, stdout: &Term) {
stdout.clear_screen().unwrap();
for (i, option) in self.options.iter().enumerate() {
let label_style = match i == self.selected_option {
true => self.selected_style.clone(),
false => self.normal_style.clone(),
};
let label = label_style.apply_to(option.label.as_str());
let hint_str = match &self.options[i].hint {
Some(hint) => hint,
None => "",
};
let hint = self.hint_style.apply_to(hint_str);
let line = format!("- {: <25}\t{}", label, hint);
stdout.write_line(line.as_str()).unwrap();
}
stdout.flush().unwrap();
}
}
fn dummy_func() {}