use std::io::stdin;
use std::io::{self, Write};
use crate::utils::Color;
pub struct InteractiveMenu {
options: Vec<String>,
selected_index: usize,
style: Style,
}
pub struct Style {
color: Color,
selected_prefix: char,
}
impl Default for Style {
fn default() -> Self {
Self {
color: Color::White,
selected_prefix: '>',
}
}
}
pub struct StyleBuilder {
color: Option<Color>,
selected_prefix: Option<char>,
}
impl StyleBuilder {
pub fn new() -> StyleBuilder {
StyleBuilder {
color: None,
selected_prefix: None,
}
}
pub fn color(mut self, color: Color) -> StyleBuilder {
self.color = Some(color);
self
}
pub fn selected_prefix(mut self, prefix: char) -> StyleBuilder {
self.selected_prefix = Some(prefix);
self
}
pub fn build(self) -> Style {
Style {
color: self.color.unwrap_or(Color::White),
selected_prefix: self.selected_prefix.unwrap_or('>'),
}
}
}
impl InteractiveMenu {
pub fn new(options: Vec<String>, style: Style) -> Self {
Self {
options,
selected_index: 0,
style,
}
}
pub fn run(&mut self) -> io::Result<usize> {
self.display()?;
loop {
let mut input = String::new();
stdin().read_line(&mut input)?;
match input.trim() {
"w" | "W" => self.previous(), "s" | "S" => self.next(), "" => break, _ => continue,
}
self.display()?;
}
Ok(self.selected_index)
}
fn display(&mut self) -> io::Result<()> {
println!("\x1B[2J\x1B[1;1H{}", self.style.color.to_ansi_code());
io::stdout().flush()?;
println!("'w' for up, 's' for down, then press Enter to select. Double Enter to submit.\n");
for (i, option) in self.options.iter().enumerate() {
if i == self.selected_index {
print!("{} ", self.style.selected_prefix);
} else {
print!(" ");
}
println!("{}", option);
}
io::stdout().flush()?;
Ok(())
}
fn previous(&mut self) {
if self.selected_index > 0 {
self.selected_index -= 1;
} else {
self.selected_index = self.options.len() - 1;
}
}
fn next(&mut self) {
self.selected_index = (self.selected_index + 1) % self.options.len();
}
}