use std::collections::HashMap;
use anyhow::anyhow;
use gdk::ModifierType;
use gdk::keys::{self, Key};
use log::debug;
use serde::{Serialize, Deserialize};
use crate::input::MappingDefinition;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Action {
Noop,
SmallScrollUp,
SmallScrollDown,
BigScrollUp,
BigScrollDown,
ScrollToTop,
ScrollToBottom,
Quit,
LaunchEditor,
ExecEditor,
ZoomIn,
ZoomOut,
ZoomReset,
ShowHelp,
}
impl Default for Action {
fn default() -> Self {
Action::Noop
}
}
#[derive(Clone)]
pub struct Keymaps {
mappings: HashMap<(ModifierType, Key), Action>,
}
impl Default for Keymaps {
fn default() -> Self {
let mut keymaps = Self::new();
keymaps.set_action(ModifierType::empty(), keys::constants::j, Action::SmallScrollDown);
keymaps.set_action(ModifierType::empty(), keys::constants::k, Action::SmallScrollUp);
keymaps.set_action(ModifierType::SHIFT_MASK, keys::constants::j, Action::BigScrollDown);
keymaps.set_action(ModifierType::SHIFT_MASK, keys::constants::k, Action::BigScrollUp);
keymaps.set_action(ModifierType::empty(), keys::constants::g, Action::ScrollToTop);
keymaps.set_action(ModifierType::SHIFT_MASK, keys::constants::g, Action::ScrollToBottom);
keymaps.set_action(ModifierType::CONTROL_MASK, keys::constants::q, Action::Quit);
keymaps.set_action(ModifierType::empty(), keys::constants::e, Action::LaunchEditor);
keymaps.set_action(ModifierType::SHIFT_MASK, keys::constants::e, Action::ExecEditor);
keymaps.set_action(ModifierType::empty(), keys::constants::plus, Action::ZoomIn);
keymaps.set_action(ModifierType::empty(), keys::constants::minus, Action::ZoomOut);
keymaps.set_action(ModifierType::empty(), keys::constants::equal, Action::ZoomReset);
keymaps.set_action(ModifierType::empty(), keys::constants::F1, Action::ShowHelp);
keymaps
}
}
impl Keymaps {
pub fn new() -> Self {
Self { mappings: HashMap::new() }
}
pub fn add_config_mappings(&mut self, mappings: &[MappingDefinition]) -> anyhow::Result<()> {
for mapping in mappings {
let mut modifiers = ModifierType::empty();
for m in &mapping.mods {
match m.as_str() {
"control" => { modifiers |= ModifierType::CONTROL_MASK; }
"shift" => { modifiers |= ModifierType::SHIFT_MASK; }
"alt" => { modifiers |= ModifierType::MOD1_MASK; }
_ => {
{ return Err(anyhow!("Unknown modifier: {}", m)); }
},
}
}
if mapping.key_char.is_some() && mapping.key_name.is_some() {
return Err(
anyhow!("Both `key_char` or `key_name` given, please pick just one: {:?}", mapping)
);
}
let key =
if let Some(c) = mapping.key_char {
Key::from_unicode(c)
} else if let Some(name) = &mapping.key_name {
Key::from_name(name)
} else {
return Err(anyhow!("No `key_char` or `key_name` given: {:?}", mapping));
};
self.set_action(modifiers, key, mapping.action.clone());
debug!("Defined custom mapping: {:?}", mapping);
}
Ok(())
}
pub fn get_action(&self, modifiers: ModifierType, key: Key) -> Action {
let (key, modifiers) = Self::normalize_input(key, modifiers);
self.mappings.get(&(modifiers, key)).cloned().unwrap_or(Action::Noop)
}
pub fn set_action(&mut self, modifiers: ModifierType, key: Key, action: Action) {
let (key, modifiers) = Self::normalize_input(key, modifiers);
self.mappings.insert((modifiers, key), action);
}
fn normalize_input(mut key: Key, mut modifiers: ModifierType) -> (Key, ModifierType) {
if key.is_upper() {
key = key.to_lower();
modifiers.insert(ModifierType::SHIFT_MASK);
}
(key, modifiers)
}
}