use {
super::*,
crossterm::event::{KeyEvent, KeyEventKind, KeyEventState},
serde::{de, Deserialize, Deserializer},
std::{fmt, str::FromStr},
strict::OneToThree,
};
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct KeyCombination {
pub codes: OneToThree<KeyCode>,
pub modifiers: KeyModifiers,
}
fn normalize_key_code(code: &mut KeyCode, modifiers: KeyModifiers) -> bool {
if matches!(code, KeyCode::Char('\r') | KeyCode::Char('\n')) {
*code = KeyCode::Enter;
} else if modifiers.contains(KeyModifiers::SHIFT) {
if let KeyCode::Char(c) = code {
if c.is_ascii_lowercase() {
*code = KeyCode::Char(c.to_ascii_uppercase());
}
}
} else if let KeyCode::Char(c) = code {
if c.is_ascii_uppercase() {
return true;
}
}
false
}
impl KeyCombination {
pub fn new<C: Into<OneToThree<KeyCode>>>(codes: C, modifiers: KeyModifiers) -> Self {
let codes = codes.into().sorted();
Self { codes, modifiers }
}
pub const fn one_key(code: KeyCode, modifiers: KeyModifiers) -> Self {
let codes = OneToThree::One(code);
Self { codes, modifiers }
}
pub const fn is_ansi_compatible(self) -> bool {
matches!(self.codes, OneToThree::One(_))
}
pub fn normalized(mut self) -> Self {
let mut shift = normalize_key_code(self.codes.first_mut(), self.modifiers);
if let Some(ref mut code) = self.codes.get_mut(1) {
shift |= normalize_key_code(code, self.modifiers);
}
if let Some(ref mut code) = self.codes.get_mut(2) {
shift |= normalize_key_code(code, self.modifiers);
}
if shift {
self.modifiers |= KeyModifiers::SHIFT;
}
self
}
pub const fn as_letter(self) -> Option<char> {
match self {
Self {
codes: OneToThree::One(KeyCode::Char(l)),
modifiers: KeyModifiers::NONE,
} => Some(l),
_ => None,
}
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for KeyCombination {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(de::Error::custom)
}
}
impl FromStr for KeyCombination {
type Err = ParseKeyError;
fn from_str(s: &str) -> Result<Self, ParseKeyError> {
parse(s)
}
}
impl fmt::Display for KeyCombination {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
STANDARD_FORMAT.format(*self).fmt(f)
}
}
impl From<KeyEvent> for KeyCombination {
fn from(key_event: KeyEvent) -> Self {
let raw = Self {
codes: key_event.code.into(),
modifiers: key_event.modifiers,
};
raw.normalized()
}
}
impl TryFrom<&[KeyEvent]> for KeyCombination {
type Error = &'static str;
fn try_from(key_events: &[KeyEvent]) -> Result<Self, Self::Error> {
let mut modifiers = KeyModifiers::empty();
let mut codes = Vec::new();
for key_event in key_events {
modifiers |= key_event.modifiers;
codes.push(key_event.code);
}
let codes: OneToThree<KeyCode> = codes.try_into()?;
let raw = Self::new(codes, modifiers);
Ok(raw.normalized())
}
}
impl From<KeyCode> for KeyCombination {
fn from(key_code: KeyCode) -> Self {
Self {
codes: key_code.into(),
modifiers: KeyModifiers::empty(),
}
}
}
#[allow(clippy::from_over_into)]
impl Into<KeyEvent> for KeyCombination {
fn into(self) -> KeyEvent {
let Self { codes, modifiers } = self;
KeyEvent {
code: *codes.first(),
modifiers,
kind: KeyEventKind::Press, state: KeyEventState::empty(),
}
}
}