Implement Display for KeyCode and KeyModifiers (#862)
Partially addresses #792 * Implement Display for KeyCode and KeyModifiers * Add demo for Display implementation
This commit is contained in:
parent
7efe19da28
commit
39ef1e42ba
49
examples/key-display.rs
Normal file
49
examples/key-display.rs
Normal file
@ -0,0 +1,49 @@
|
||||
//! Demonstrates the display format of key events.
|
||||
//!
|
||||
//! This example demonstrates the display format of key events, which is useful for displaying in
|
||||
//! the help section of a terminal application.
|
||||
//!
|
||||
//! cargo run --example key-display
|
||||
|
||||
use std::io;
|
||||
|
||||
use crossterm::event::{KeyEventKind, KeyModifiers};
|
||||
use crossterm::{
|
||||
event::{read, Event, KeyCode},
|
||||
terminal::{disable_raw_mode, enable_raw_mode},
|
||||
};
|
||||
|
||||
const HELP: &str = r#"Key display
|
||||
- Press any key to see its display format
|
||||
- Use Esc to quit
|
||||
"#;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
println!("{}", HELP);
|
||||
enable_raw_mode()?;
|
||||
if let Err(e) = print_events() {
|
||||
println!("Error: {:?}\r", e);
|
||||
}
|
||||
disable_raw_mode()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_events() -> io::Result<()> {
|
||||
loop {
|
||||
let event = read()?;
|
||||
match event {
|
||||
Event::Key(event) if event.kind == KeyEventKind::Press => {
|
||||
print!("Key pressed: ");
|
||||
if event.modifiers != KeyModifiers::NONE {
|
||||
print!("{}+", event.modifiers);
|
||||
}
|
||||
println!("{}\r", event.code);
|
||||
if event.code == KeyCode::Esc {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
315
src/event.rs
315
src/event.rs
@ -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;
|
||||
@ -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)]
|
||||
@ -801,17 +850,37 @@ 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,
|
||||
@ -819,11 +888,11 @@ pub enum ModifierKeyCode {
|
||||
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,
|
||||
@ -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,
|
||||
@ -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,
|
||||
@ -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
|
||||
@ -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() {
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user