use crate::{config::KEY_BINDINGS, ui::Scroller};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use std::ops::Deref;
const DESCRIPTIONS_LEN: usize = 36;
const DESCRIPTIONS: [&str; DESCRIPTIONS_LEN] = [
"Go one line downward", "Go one line upward", "Close fold", "Open fold", "Jump to the first line", "Jump to the last line", "Jump to the folder below", "Jump to the folder above", "Jump to the parent folder", "Load the selected save file", "Load the active save file", "Mark the selected save file as active", "Import save file into the current folder", "Import save file to the top level", "Import new save file and overwrite the selected file", "Delete the selected file/folder", "Create folder", "Create folder in the top level", "Rename the selected file/folder", "Move the marked entries into the current folder", "Move the marked entries to the top level", "Swap the selected entry with its above sibling", "Swap the selected entry with its below sibling", "Open all folds", "Close all folds", "Open game selection window", "Open profile selection window", "Open help window", "Enter search pattern", "Repeat the latest search", "Repeat the latest search backward", "Open fuzzy finder", "Open global fuzzy finder", "Mark the selected entry", "Unmark all marked entries", "Quit application", ];
const GAME_SELECTION_DESCRIPTIONS_LEN: usize = 6;
const GAME_SELECTION_DESCRIPTIONS: [&str; GAME_SELECTION_DESCRIPTIONS_LEN] = [
" - Create, ",
" - Rename, ",
" - Delete, ",
" - Set savefile path, ",
" - Select, ",
" - Abort",
];
const PROFILE_SELECTION_DESCRIPTIONS_LEN: usize = 5;
const PROFILE_SELECTION_DESCRIPTIONS: [&str; PROFILE_SELECTION_DESCRIPTIONS_LEN] = [
" - Create, ",
" - Rename, ",
" - Delete, ",
" - Select, ",
" - Abort",
];
fn key_event_to_string(key_event: &KeyEvent) -> String {
let key_code = match key_event.code {
KeyCode::Backspace => "backspace",
KeyCode::Enter => "enter",
KeyCode::Left => "left",
KeyCode::Right => "right",
KeyCode::Up => "up",
KeyCode::Down => "down",
KeyCode::Home => "home",
KeyCode::End => "end",
KeyCode::PageUp => "pageup",
KeyCode::PageDown => "pagedown",
KeyCode::Tab => "tab",
KeyCode::BackTab => "backtab",
KeyCode::Delete => "delete",
KeyCode::Insert => "insert",
KeyCode::F(num) => &format!("f{num}"),
KeyCode::Char(' ') => "space",
KeyCode::Char(c) => &c.to_string(),
KeyCode::Esc => "esc",
_ => "",
};
let mut modifiers = Vec::with_capacity(3);
if key_event.modifiers.intersects(KeyModifiers::CONTROL) {
modifiers.push("ctrl");
}
if key_event.modifiers.intersects(KeyModifiers::SHIFT) {
modifiers.push("shift");
}
if key_event.modifiers.intersects(KeyModifiers::ALT) {
modifiers.push("alt");
}
let mut key = modifiers.join("-");
if !key.is_empty() {
key.push('-');
}
key.push_str(key_code);
key
}
const HELP_ENTRY: (String, &str) = (String::new(), "");
pub struct Bindings {
pub general: [(String, &'static str); DESCRIPTIONS_LEN],
pub game_selection: [(String, &'static str); GAME_SELECTION_DESCRIPTIONS_LEN],
pub profile_selection: [(String, &'static str); PROFILE_SELECTION_DESCRIPTIONS_LEN],
}
impl Default for Bindings {
fn default() -> Self {
let mut help = Self {
general: [HELP_ENTRY; DESCRIPTIONS_LEN],
game_selection: [HELP_ENTRY; GAME_SELECTION_DESCRIPTIONS_LEN],
profile_selection: [HELP_ENTRY; PROFILE_SELECTION_DESCRIPTIONS_LEN],
};
macro_rules! generate_entries {
($entries: expr, $bindings: expr, $descriptions: ident) => {
for (key, command) in &$bindings {
let idx = *command as usize;
if !$entries[idx].0.is_empty() {
$entries[idx].0.push_str(", ");
}
$entries[idx].0.push_str(&key_event_to_string(key));
}
for (idx, (_, desc)) in $entries.iter_mut().enumerate() {
*desc = $descriptions[idx];
}
};
}
generate_entries!(help.general, KEY_BINDINGS.general, DESCRIPTIONS);
generate_entries!(
help.game_selection,
KEY_BINDINGS.game_selection,
GAME_SELECTION_DESCRIPTIONS
);
generate_entries!(
help.profile_selection,
KEY_BINDINGS.profile_selection,
PROFILE_SELECTION_DESCRIPTIONS
);
for (keys, _) in &mut help.general {
*keys = format!("{keys:14} ");
}
help
}
}
impl Deref for Bindings {
type Target = [(String, &'static str); DESCRIPTIONS_LEN];
fn deref(&self) -> &Self::Target {
&self.general
}
}
#[derive(Default)]
pub struct Help {
pub bindings: Bindings,
pub visible: bool,
pub scroller: Scroller,
}
impl Help {
pub fn toggle(&mut self) {
self.visible = !self.visible;
}
}