use std::borrow::Borrow;
use tracing::warn;
use crate::{IntoKey, KbKey, KeyEvent, Modifiers};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HotKey {
pub(crate) mods: RawMods,
pub(crate) key: KbKey,
}
impl HotKey {
pub fn new(mods: impl Into<Option<RawMods>>, key: impl IntoKey) -> Self {
HotKey {
mods: mods.into().unwrap_or(RawMods::None),
key: key.into_key(),
}
.warn_if_needed()
}
fn warn_if_needed(self) -> Self {
if let KbKey::Character(s) = &self.key {
let km: Modifiers = self.mods.into();
if km.shift() && s.chars().any(|c| c.is_lowercase()) {
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 base_mods = Modifiers::SHIFT | Modifiers::CONTROL | Modifiers::ALT | Modifiers::META;
let event = event.borrow();
self.mods == event.mods & base_mods && self.key == event.key
}
}
#[derive(Debug, Clone, Copy)]
pub enum SysMods {
None,
Shift,
Cmd,
AltCmd,
CmdShift,
AltCmdShift,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RawMods {
None,
Alt,
Ctrl,
Meta,
Shift,
AltCtrl,
AltMeta,
AltShift,
CtrlShift,
CtrlMeta,
MetaShift,
AltCtrlMeta,
AltCtrlShift,
AltMetaShift,
CtrlMetaShift,
AltCtrlMetaShift,
}
impl std::cmp::PartialEq<Modifiers> for RawMods {
fn eq(&self, other: &Modifiers) -> bool {
let mods: Modifiers = (*self).into();
mods == *other
}
}
impl std::cmp::PartialEq<RawMods> for Modifiers {
fn eq(&self, other: &RawMods) -> bool {
other == self
}
}
impl std::cmp::PartialEq<Modifiers> for SysMods {
fn eq(&self, other: &Modifiers) -> bool {
let mods: RawMods = (*self).into();
mods == *other
}
}
impl std::cmp::PartialEq<SysMods> for Modifiers {
fn eq(&self, other: &SysMods) -> bool {
let other: RawMods = (*other).into();
&other == self
}
}
impl From<RawMods> for Modifiers {
fn from(src: RawMods) -> Modifiers {
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),
};
let mut mods = Modifiers::empty();
mods.set(Modifiers::ALT, alt);
mods.set(Modifiers::CONTROL, ctrl);
mods.set(Modifiers::META, meta);
mods.set(Modifiers::SHIFT, shift);
mods
}
}
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::Shift => RawMods::Shift,
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::Shift => RawMods::Shift,
SysMods::Cmd => RawMods::Ctrl,
SysMods::AltCmd => RawMods::AltCtrl,
SysMods::CmdShift => RawMods::CtrlShift,
SysMods::AltCmdShift => RawMods::AltCtrlShift,
}
}
}