use std::borrow::Borrow;
use log::warn;
use crate::keyboard::{KeyCode, KeyEvent, KeyModifiers};
#[derive(Debug, Clone)]
pub struct HotKey {
pub(crate) mods: RawMods,
pub(crate) key: KeyCompare,
}
#[derive(Debug, Clone, PartialEq)]
pub enum KeyCompare {
Code(KeyCode),
Text(&'static str),
}
impl HotKey {
pub fn new(mods: impl Into<Option<RawMods>>, key: impl Into<KeyCompare>) -> Self {
HotKey {
mods: mods.into().unwrap_or(RawMods::None),
key: key.into(),
}
.warn_if_needed()
}
fn warn_if_needed(self) -> Self {
if let KeyCompare::Text(s) = self.key {
let km: KeyModifiers = self.mods.into();
if km.shift && s.chars().any(|c| c.is_uppercase()) {
warn!(
"warning: HotKey {:?} includes shift, but text is lowercase. \
Text is matched literally; this may cause problems.",
&self
);
}
}
self
}
pub fn matches(&self, event: impl Borrow<KeyEvent>) -> bool {
let event = event.borrow();
self.mods == event.mods
&& match self.key {
KeyCompare::Code(code) => code == event.key_code,
KeyCompare::Text(text) => Some(text) == event.text(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum SysMods {
None,
Cmd,
AltCmd,
CmdShift,
AltCmdShift,
}
#[derive(Debug, Clone, Copy)]
pub enum RawMods {
None,
Alt,
Ctrl,
Meta,
Shift,
AltCtrl,
AltMeta,
AltShift,
CtrlShift,
CtrlMeta,
MetaShift,
AltCtrlMeta,
AltCtrlShift,
AltMetaShift,
CtrlMetaShift,
AltCtrlMetaShift,
}
impl std::cmp::PartialEq<KeyModifiers> for RawMods {
fn eq(&self, other: &KeyModifiers) -> bool {
let mods: KeyModifiers = (*self).into();
mods == *other
}
}
impl std::cmp::PartialEq<RawMods> for KeyModifiers {
fn eq(&self, other: &RawMods) -> bool {
other == self
}
}
impl std::cmp::PartialEq<KeyModifiers> for SysMods {
fn eq(&self, other: &KeyModifiers) -> bool {
let mods: RawMods = (*self).into();
mods == *other
}
}
impl std::cmp::PartialEq<SysMods> for KeyModifiers {
fn eq(&self, other: &SysMods) -> bool {
let other: RawMods = (*other).into();
&other == self
}
}
impl From<RawMods> for KeyModifiers {
fn from(src: RawMods) -> KeyModifiers {
let (alt, ctrl, meta, shift) = match src {
RawMods::None => (false, false, false, false),
RawMods::Alt => (true, false, false, false),
RawMods::Ctrl => (false, true, false, false),
RawMods::Meta => (false, false, true, false),
RawMods::Shift => (false, false, false, true),
RawMods::AltCtrl => (true, true, false, false),
RawMods::AltMeta => (true, false, true, false),
RawMods::AltShift => (true, false, false, true),
RawMods::CtrlMeta => (false, true, true, false),
RawMods::CtrlShift => (false, true, false, true),
RawMods::MetaShift => (false, false, true, true),
RawMods::AltCtrlMeta => (true, true, true, false),
RawMods::AltMetaShift => (true, false, true, true),
RawMods::AltCtrlShift => (true, true, false, true),
RawMods::CtrlMetaShift => (false, true, true, true),
RawMods::AltCtrlMetaShift => (true, true, true, true),
};
KeyModifiers {
alt,
ctrl,
meta,
shift,
}
}
}
impl From<SysMods> for Option<RawMods> {
fn from(src: SysMods) -> Option<RawMods> {
Some(src.into())
}
}
impl From<SysMods> for RawMods {
fn from(src: SysMods) -> RawMods {
#[cfg(target_os = "macos")]
match src {
SysMods::None => RawMods::None,
SysMods::Cmd => RawMods::Meta,
SysMods::AltCmd => RawMods::AltMeta,
SysMods::CmdShift => RawMods::MetaShift,
SysMods::AltCmdShift => RawMods::AltMetaShift,
}
#[cfg(not(target_os = "macos"))]
match src {
SysMods::None => RawMods::None,
SysMods::Cmd => RawMods::Ctrl,
SysMods::AltCmd => RawMods::AltCtrl,
SysMods::CmdShift => RawMods::CtrlShift,
SysMods::AltCmdShift => RawMods::AltCtrlShift,
}
}
}
impl From<KeyCode> for KeyCompare {
fn from(src: KeyCode) -> KeyCompare {
KeyCompare::Code(src)
}
}
impl From<&'static str> for KeyCompare {
fn from(src: &'static str) -> KeyCompare {
KeyCompare::Text(src)
}
}