Skip to content

Commit

Permalink
Implement Display for KeyCode and KeyModifiers
Browse files Browse the repository at this point in the history
Partially addresses crossterm-rs#792
  • Loading branch information
joshka committed Feb 17, 2024
1 parent 99fe255 commit 7b30be1
Showing 1 changed file with 305 additions and 10 deletions.
315 changes: 305 additions & 10 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ use crate::event::{
};
use crate::{csi, Command};
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
use std::fmt;
use std::fmt::{self, Display};
use std::time::Duration;

use bitflags::bitflags;
Expand Down Expand Up @@ -612,6 +612,55 @@ bitflags! {
}
}

impl Display for KeyModifiers {
/// Formats the key modifiers using the given formatter.
///
/// The key modifiers are joined by a `+` character.
///
/// # Platform-specific Notes
///
/// On macOS, the control, alt, and super keys is displayed as "Control", "Option", and
/// "Command" respectively. See
/// <https://support.apple.com/guide/applestyleguide/welcome/1.0/web>.
///
/// On Windows, the super key is displayed as "Windows" and the control key is displayed as
/// "Ctrl". See
/// <https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/keys-keyboard-shortcuts>.
///
/// On other platforms, the super key is referred to as "Super" and the control key is
/// displayed as "Ctrl".
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
for modifier in self.iter() {
if !first {
f.write_str("+")?;
first = false;
}
match modifier {
KeyModifiers::SHIFT => f.write_str("Shift")?,
#[cfg(unix)]
KeyModifiers::CONTROL => f.write_str("Control")?,
#[cfg(windows)]
KeyModifiers::CONTROL => f.write_str("Ctrl")?,
#[cfg(target_os = "macos")]
KeyModifiers::ALT => f.write_str("Option")?,
#[cfg(not(target_os = "macos"))]
KeyModifiers::ALT => f.write_str("Alt")?,
#[cfg(target_os = "macos")]
KeyModifiers::SUPER => f.write_str("Command")?,
#[cfg(target_os = "windows")]
KeyModifiers::SUPER => f.write_str("Windows")?,
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
KeyModifiers::SUPER => f.write_str("Super")?,
KeyModifiers::HYPER => f.write_str("Hyper")?,
KeyModifiers::META => f.write_str("Meta")?,
_ => unreachable!(),
}
}
Ok(())
}
}

/// Represents a keyboard event kind.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
Expand Down Expand Up @@ -801,29 +850,49 @@ pub enum MediaKeyCode {
MuteVolume,
}

impl Display for MediaKeyCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MediaKeyCode::Play => write!(f, "Play"),
MediaKeyCode::Pause => write!(f, "Pause"),
MediaKeyCode::PlayPause => write!(f, "Play/Pause"),
MediaKeyCode::Reverse => write!(f, "Reverse"),
MediaKeyCode::Stop => write!(f, "Stop"),
MediaKeyCode::FastForward => write!(f, "Fast Forward"),
MediaKeyCode::Rewind => write!(f, "Rewind"),
MediaKeyCode::TrackNext => write!(f, "Next Track"),
MediaKeyCode::TrackPrevious => write!(f, "Previous Track"),
MediaKeyCode::Record => write!(f, "Record"),
MediaKeyCode::LowerVolume => write!(f, "Lower Volume"),
MediaKeyCode::RaiseVolume => write!(f, "Raise Volume"),
MediaKeyCode::MuteVolume => write!(f, "Mute Volume"),
}
}
}

/// Represents a modifier key (as part of [`KeyCode::Modifier`]).
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ModifierKeyCode {
/// Left Shift key.
LeftShift,
/// Left Control key.
/// Left Control key. (Control on macOS, Ctrl on other platforms)
LeftControl,
/// Left Alt key.
/// Left Alt key. (Option on macOS, Alt on other platforms)
LeftAlt,
/// Left Super key.
/// Left Super key. (Command on macOS, Windows on Windows, Super on other platforms)
LeftSuper,
/// Left Hyper key.
LeftHyper,
/// Left Meta key.
LeftMeta,
/// Right Shift key.
RightShift,
/// Right Control key.
/// Right Control key. (Control on macOS, Ctrl on other platforms)
RightControl,
/// Right Alt key.
/// Right Alt key. (Option on macOS, Alt on other platforms)
RightAlt,
/// Right Super key.
/// Right Super key. (Command on macOS, Windows on Windows, Super on other platforms)
RightSuper,
/// Right Hyper key.
RightHyper,
Expand All @@ -835,11 +904,74 @@ pub enum ModifierKeyCode {
IsoLevel5Shift,
}

impl Display for ModifierKeyCode {
/// Formats the modifier key using the given formatter.
///
/// # Platform-specific Notes
///
/// On macOS, the control, alt, and super keys is displayed as "Control", "Option", and
/// "Command" respectively. See
/// <https://support.apple.com/guide/applestyleguide/welcome/1.0/web>.
///
/// On Windows, the super key is displayed as "Windows" and the control key is displayed as
/// "Ctrl". See
/// <https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/keys-keyboard-shortcuts>.
///
/// On other platforms, the super key is referred to as "Super".
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(target_os = "macos")]
match self {
ModifierKeyCode::LeftShift => write!(f, "Left Shift"),
ModifierKeyCode::LeftHyper => write!(f, "Left Hyper"),
ModifierKeyCode::LeftMeta => write!(f, "Left Meta"),
ModifierKeyCode::RightShift => write!(f, "Right Shift"),
ModifierKeyCode::RightHyper => write!(f, "Right Hyper"),
ModifierKeyCode::RightMeta => write!(f, "Right Meta"),
ModifierKeyCode::IsoLevel3Shift => write!(f, "Iso Level 3 Shift"),
ModifierKeyCode::IsoLevel5Shift => write!(f, "Iso Level 5 Shift"),

#[cfg(target_os = "macos")]
ModifierKeyCode::LeftControl => write!(f, "Left Control"),
#[cfg(not(target_os = "macos"))]
ModifierKeyCode::LeftControl => write!(f, "Left Ctrl"),

#[cfg(target_os = "macos")]
ModifierKeyCode::LeftAlt => write!(f, "Left Option"),
#[cfg(not(target_os = "macos"))]
ModifierKeyCode::LeftAlt => write!(f, "Left Alt"),

#[cfg(target_os = "macos")]
ModifierKeyCode::LeftSuper => write!(f, "Left Command"),
#[cfg(target_os = "windows")]
ModifierKeyCode::LeftSuper => write!(f, "Left Windows"),
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
ModifierKeyCode::LeftSuper => write!(f, "Left Super"),

#[cfg(target_os = "macos")]
ModifierKeyCode::RightControl => write!(f, "Right Control"),
#[cfg(not(target_os = "macos"))]
ModifierKeyCode::RightControl => write!(f, "Right Ctrl"),

#[cfg(target_os = "macos")]
ModifierKeyCode::RightAlt => write!(f, "Right Option"),
#[cfg(not(target_os = "macos"))]
ModifierKeyCode::RightAlt => write!(f, "Right Alt"),

#[cfg(target_os = "macos")]
ModifierKeyCode::RightSuper => write!(f, "Right Command"),
#[cfg(target_os = "windows")]
ModifierKeyCode::RightSuper => write!(f, "Right Windows"),
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
ModifierKeyCode::RightSuper => write!(f, "Right Super"),
}
}
}

/// Represents a key.
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum KeyCode {
/// Backspace key.
/// Backspace key (Delete on macOS, Backspace on other platforms).
Backspace,
/// Enter key.
Enter,
Expand All @@ -863,7 +995,7 @@ pub enum KeyCode {
Tab,
/// Shift + Tab key.
BackTab,
/// Delete key.
/// Delete key. (Fn+Delete on macOS, Delete on other platforms)
Delete,
/// Insert key.
Insert,
Expand Down Expand Up @@ -936,6 +1068,66 @@ pub enum KeyCode {
Modifier(ModifierKeyCode),
}

impl Display for KeyCode {
/// Formats the `KeyCode` using the given formatter.
///
/// # Platform-specific Notes
///
/// On macOS, the Backspace key is displayed as "Delete", the Delete key is displayed as "Fwd
/// Del", and the Enter key is displayed as "Return". See
/// <https://support.apple.com/guide/applestyleguide/welcome/1.0/web>.
///
/// On other platforms, the Backspace key is displayed as "Backspace", the Delete key is
/// displayed as "Del", and the Enter key is displayed as "Enter".
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
// On macOS, the Backspace key is called "Delete" and the Delete key is called "Fwd Del".
#[cfg(target_os = "macos")]
KeyCode::Backspace => write!(f, "Delete"),
#[cfg(target_os = "macos")]
KeyCode::Delete => write!(f, "Fwd Del"),

#[cfg(not(target_os = "macos"))]
KeyCode::Backspace => write!(f, "Backspace"),
#[cfg(not(target_os = "macos"))]
KeyCode::Delete => write!(f, "Del"),

#[cfg(target_os = "macos")]
KeyCode::Enter => write!(f, "Return"),
#[cfg(not(target_os = "macos"))]
KeyCode::Enter => write!(f, "Enter"),
KeyCode::Left => write!(f, "Left"),
KeyCode::Right => write!(f, "Right"),
KeyCode::Up => write!(f, "Up"),
KeyCode::Down => write!(f, "Down"),
KeyCode::Home => write!(f, "Home"),
KeyCode::End => write!(f, "End"),
KeyCode::PageUp => write!(f, "Page Up"),
KeyCode::PageDown => write!(f, "Page Down"),
KeyCode::Tab => write!(f, "Tab"),
KeyCode::BackTab => write!(f, "Back Tab"),
KeyCode::Insert => write!(f, "Insert"),
KeyCode::F(n) => write!(f, "F{}", n),
KeyCode::Char(c) => match c {
// special case for non-visible characters
' ' => write!(f, "Space"),
c => write!(f, "{}", c),
},
KeyCode::Null => write!(f, "Null"),
KeyCode::Esc => write!(f, "Esc"),
KeyCode::CapsLock => write!(f, "Caps Lock"),
KeyCode::ScrollLock => write!(f, "Scroll Lock"),
KeyCode::NumLock => write!(f, "Num Lock"),
KeyCode::PrintScreen => write!(f, "Print Screen"),
KeyCode::Pause => write!(f, "Pause"),
KeyCode::Menu => write!(f, "Menu"),
KeyCode::KeypadBegin => write!(f, "Begin"),
KeyCode::Media(media) => write!(f, "{}", media),
KeyCode::Modifier(modifier) => write!(f, "{}", modifier),
}
}
}

/// An internal event.
///
/// Encapsulates publicly available `Event` with additional internal
Expand All @@ -960,7 +1152,10 @@ mod tests {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

use super::{KeyCode, KeyEvent, KeyModifiers};
use super::*;
use KeyCode::*;
use MediaKeyCode::*;
use ModifierKeyCode::*;

#[test]
fn test_equality() {
Expand Down Expand Up @@ -991,4 +1186,104 @@ mod tests {
assert_eq!(lowercase_d_with_shift_hash, uppercase_d_with_shift_hash);
assert_eq!(uppercase_d_hash, uppercase_d_with_shift_hash);
}

#[test]
fn keycode_display() {
#[cfg(target_os = "macos")]
{
assert_eq!(format!("{}", Backspace), "Delete");
assert_eq!(format!("{}", Delete), "Fwd Del");
assert_eq!(format!("{}", Enter), "Return");
}
#[cfg(not(target_os = "macos"))]
{
assert_eq!(format!("{}", Backspace), "Backspace");
assert_eq!(format!("{}", Delete), "Del");
assert_eq!(format!("{}", Enter), "Enter");
}
assert_eq!(format!("{}", Left), "Left");
assert_eq!(format!("{}", Right), "Right");
assert_eq!(format!("{}", Up), "Up");
assert_eq!(format!("{}", Down), "Down");
assert_eq!(format!("{}", Home), "Home");
assert_eq!(format!("{}", End), "End");
assert_eq!(format!("{}", PageUp), "Page Up");
assert_eq!(format!("{}", PageDown), "Page Down");
assert_eq!(format!("{}", Tab), "Tab");
assert_eq!(format!("{}", BackTab), "Back Tab");
assert_eq!(format!("{}", Insert), "Insert");
assert_eq!(format!("{}", F(1)), "F1");
assert_eq!(format!("{}", Char('a')), "a");
assert_eq!(format!("{}", Null), "Null");
assert_eq!(format!("{}", Esc), "Esc");
assert_eq!(format!("{}", CapsLock), "Caps Lock");
assert_eq!(format!("{}", ScrollLock), "Scroll Lock");
assert_eq!(format!("{}", NumLock), "Num Lock");
assert_eq!(format!("{}", PrintScreen), "Print Screen");
assert_eq!(format!("{}", KeyCode::Pause), "Pause");
assert_eq!(format!("{}", Menu), "Menu");
assert_eq!(format!("{}", KeypadBegin), "Begin");
}

#[test]
fn media_keycode_display() {
assert_eq!(format!("{}", Media(Play)), "Play");
assert_eq!(format!("{}", Media(MediaKeyCode::Pause)), "Pause");
assert_eq!(format!("{}", Media(PlayPause)), "Play/Pause");
assert_eq!(format!("{}", Media(Reverse)), "Reverse");
assert_eq!(format!("{}", Media(Stop)), "Stop");
assert_eq!(format!("{}", Media(FastForward)), "Fast Forward");
assert_eq!(format!("{}", Media(Rewind)), "Rewind");
assert_eq!(format!("{}", Media(TrackNext)), "Next Track");
assert_eq!(format!("{}", Media(TrackPrevious)), "Previous Track");
assert_eq!(format!("{}", Media(Record)), "Record");
assert_eq!(format!("{}", Media(LowerVolume)), "Lower Volume");
assert_eq!(format!("{}", Media(RaiseVolume)), "Raise Volume");
assert_eq!(format!("{}", Media(MuteVolume)), "Mute Volume");
}

#[test]
fn modifier_keycode_display() {
assert_eq!(format!("{}", Modifier(LeftShift)), "Left Shift");
assert_eq!(format!("{}", Modifier(LeftHyper)), "Left Hyper");
assert_eq!(format!("{}", Modifier(LeftMeta)), "Left Meta");
assert_eq!(format!("{}", Modifier(RightShift)), "Right Shift");
assert_eq!(format!("{}", Modifier(RightHyper)), "Right Hyper");
assert_eq!(format!("{}", Modifier(RightMeta)), "Right Meta");
assert_eq!(format!("{}", Modifier(IsoLevel3Shift)), "Iso Level 3 Shift");
assert_eq!(format!("{}", Modifier(IsoLevel5Shift)), "Iso Level 5 Shift");
}

#[cfg(target_os = "macos")]
#[test]
fn modifier_keycode_display_macos() {
assert_eq!(format!("{}", Modifier(LeftControl)), "Left Control");
assert_eq!(format!("{}", Modifier(LeftAlt)), "Left Option");
assert_eq!(format!("{}", Modifier(LeftSuper)), "Left Command");
assert_eq!(format!("{}", Modifier(RightControl)), "Right Control");
assert_eq!(format!("{}", Modifier(RightAlt)), "Right Option");
assert_eq!(format!("{}", Modifier(RightSuper)), "Right Command");
}

#[cfg(target_os = "windows")]
#[test]
fn modifier_keycode_display_windows() {
assert_eq!(format!("{}", Modifier(LeftControl)), "Left Ctrl");
assert_eq!(format!("{}", Modifier(LeftAlt)), "Left Alt");
assert_eq!(format!("{}", Modifier(LeftSuper)), "Left Windows");
assert_eq!(format!("{}", Modifier(RightControl)), "Right Ctrl");
assert_eq!(format!("{}", Modifier(RightAlt)), "Right Alt");
assert_eq!(format!("{}", Modifier(RightSuper)), "Right Windows");
}

#[cfg(not(any(target_os = "macos", target_os = "windows")))]
#[test]
fn modifier_keycode_display_other() {
assert_eq!(format!("{}", Modifier(LeftControl)), "Left Ctrl");
assert_eq!(format!("{}", Modifier(LeftAlt)), "Left Alt");
assert_eq!(format!("{}", Modifier(LeftSuper)), "Left Super");
assert_eq!(format!("{}", Modifier(RightControl)), "Right Ctrl");
assert_eq!(format!("{}", Modifier(RightAlt)), "Right Alt");
assert_eq!(format!("{}", Modifier(RightSuper)), "Right Super");
}
}

0 comments on commit 7b30be1

Please sign in to comment.