use crate::color::reset_display;
use crate::Config;
use crate::TermBuffer;
use crossterm::{
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
style::{style, Color},
};
#[derive(Debug)]
pub struct TypePrompt<'a> {
config: &'a Config,
input: String,
focused_index: u16,
}
pub enum TypePromptResult {
Type(String),
Escape,
Terminate,
}
impl<'a> TypePrompt<'a> {
pub fn new(config: &'a Config) -> Self {
TypePrompt {
config,
input: Default::default(),
focused_index: 0,
}
}
fn get_at_selected_index(&self) -> &str {
let options = self.filter_types();
options
.get(self.focused_index as usize)
.or_else(|| options.last())
.copied()
.unwrap_or("misc")
}
fn filter_types(&self) -> Vec<&str> {
self.config
.types
.iter()
.map(|x| x.as_str())
.filter(|item| {
if self.input.is_empty() {
true
} else {
item.starts_with(&self.input)
}
})
.collect()
}
pub fn run(mut self) -> TypePromptResult {
let mut buffer = TermBuffer::new();
let figlet = self
.config
.get_figlet()
.expect("Ensure figlet_file points to a valid file, or remove it.");
let mut first_iteration = true;
loop {
let event = if first_iteration {
first_iteration = false;
None
} else {
match event::read() {
Ok(Event::Key(KeyEvent { code, modifiers })) => Some((
code,
modifiers.contains(KeyModifiers::CONTROL),
modifiers.contains(KeyModifiers::SHIFT),
modifiers.contains(KeyModifiers::ALT),
)),
_ => continue,
}
};
match event {
Some((KeyCode::Char('c'), true, false, false)) => {
return TypePromptResult::Terminate;
}
Some((KeyCode::Enter, false, false, false)) => {
return TypePromptResult::Type(self.get_at_selected_index().to_string());
}
Some((KeyCode::Char(c), false, _, false)) => {
self.input.push(c.to_ascii_lowercase());
}
Some((KeyCode::Backspace, false, _, false)) => {
self.input.pop();
}
Some((KeyCode::Esc, false, _, false)) => {
return TypePromptResult::Escape;
}
Some((KeyCode::Up, false, _, false)) => {
self.focused_index = self.focused_index.saturating_sub(1);
}
Some((KeyCode::Down, false, _, false)) => {
let total = self.config.types.len() as u16;
self.focused_index += 1;
if self.focused_index >= total {
self.focused_index = total.saturating_sub(1);
}
}
None => {}
_ => continue,
};
let types = self.filter_types();
if types.len() == 1 {
return TypePromptResult::Type(types[0].to_string());
}
let mut header = figlet.create_vec();
figlet.write_to_buf_color("<glint>", header.as_mut_slice(), |s| {
style(s).with(Color::Magenta).to_string()
});
for line in header {
buffer.push_line(line);
}
let y_offset = buffer.lines() + 1;
let after_prompt_x = {
let prompt_pre = "Choose a type: ";
let prompt_post = &self.input;
let underscores = "_".repeat(6_usize.saturating_sub(self.input.len()));
buffer.push_line("");
buffer.push_line(format!(
"{}{}{}{}",
prompt_pre,
style(prompt_post).with(crate::color::theme_user_input()),
style(underscores).with(crate::color::theme_user_input()),
reset_display()
));
let x = prompt_pre.len() + prompt_post.len();
x as u16
};
let focused_color = Color::Blue;
let default_color = Color::Reset;
for (i, ty) in types.into_iter().enumerate() {
let line_content = if i as u16 == self.focused_index {
style(["*", " ", ty].concat()).with(focused_color)
} else {
style(["-", " ", ty].concat()).with(default_color)
};
let line = format!("{}{}", line_content, reset_display());
buffer.push_line(line);
}
buffer.set_next_cursor((after_prompt_x, y_offset));
buffer.render_frame();
buffer.flush();
}
}
}