use crossterm::event::{KeyCode, KeyEvent};
use crate::{
dependency::{Action, DependencyId, Evaluation},
step::CompoundStep,
style::{drawer_selected_style, drawer_style, help_style},
text::{DrawerContents, Segment, Text},
};
use super::Control;
pub struct SelectInput {
prompt: String,
options: Vec<SelectInputOption>,
selected_option: usize,
}
impl SelectInput {
pub fn new(prompt: &str, options: Vec<(&str, &str)>) -> Self {
Self {
prompt: prompt.to_string(),
options: options
.iter()
.map(|(value, description)| SelectInputOption::new(value, description))
.collect(),
selected_option: 0,
}
}
pub fn set_prompt(&mut self, prompt: &str) {
self.prompt = prompt.to_string();
}
pub fn add_option(&mut self, option: SelectInputOption) {
self.options.push(option);
}
pub fn set_options(&mut self, options: Vec<SelectInputOption>) {
self.options = options;
}
fn selected_option_value(&self) -> &str {
&self.options[self.selected_option].value
}
}
impl Control for SelectInput {
fn focusable(&self) -> bool {
true
}
fn update(&mut self, input: KeyEvent) {
match input.code {
KeyCode::Up => {
if self.selected_option == 0 {
self.selected_option = self.options.len() - 1;
} else {
self.selected_option -= 1;
}
}
KeyCode::Down => {
if self.selected_option + 1 == self.options.len() {
self.selected_option = 0;
} else {
self.selected_option += 1;
}
}
_ => {}
}
}
fn help(&self) -> Option<Segment> {
Some(Text::new_styled(self.prompt.clone(), help_style()).as_segment())
}
fn text(&self) -> (Segment, Option<u16>) {
let value = self.selected_option_value();
let segment = Text::new(value.to_string()).as_segment();
(segment, Some(0))
}
fn drawer(&self) -> Option<DrawerContents> {
let mut items = Vec::new();
for (option_index, option) in self.options.iter().enumerate() {
let mut text = format!(" {} - {}", option.value, option.description);
let mut style = drawer_style();
if option_index == self.selected_option {
style = drawer_selected_style();
text.replace_range(1..2, ">");
}
items.push(Text::new_styled(text, style).as_segment());
}
Some(items)
}
fn evaluation(&self) -> Option<(DependencyId, Evaluation)> {
None
}
fn dependency(&self) -> Option<(DependencyId, Action)> {
None
}
fn evaluate(&self, evaluation: &Evaluation) -> bool {
match evaluation {
Evaluation::Equal(value) => self.selected_option_value() == value,
Evaluation::NotEqual(value) => self.selected_option_value() != value,
Evaluation::IsEmpty => false,
}
}
fn add_to(self, step: &mut CompoundStep) {
step.add_control(Box::new(self))
}
}
pub struct SelectInputOption {
value: String,
description: String,
}
impl SelectInputOption {
pub fn new(value: &str, description: &str) -> Self {
Self {
value: value.to_string(),
description: description.to_string(),
}
}
pub fn value(&self) -> &str {
&self.value
}
pub fn description(&self) -> &str {
&self.description
}
}