#![forbid(unsafe_code)]
use bitflags::bitflags;
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
use crossterm::event as cte;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
Key(KeyEvent),
Mouse(MouseEvent),
Resize {
width: u16,
height: u16,
},
Paste(PasteEvent),
Ime(ImeEvent),
Focus(bool),
Clipboard(ClipboardEvent),
Tick,
}
impl Event {
#[must_use]
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
pub fn from_crossterm(event: cte::Event) -> Option<Self> {
map_crossterm_event_internal(event)
}
#[must_use]
pub const fn event_type_label(&self) -> &'static str {
match self {
Event::Key(_) => "key",
Event::Mouse(_) => "mouse",
Event::Resize { .. } => "resize",
Event::Paste(_) => "paste",
Event::Ime(_) => "ime",
Event::Focus(_) => "focus",
Event::Clipboard(_) => "clipboard",
Event::Tick => "tick",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KeyEvent {
pub code: KeyCode,
pub modifiers: Modifiers,
pub kind: KeyEventKind,
}
impl KeyEvent {
#[must_use]
pub const fn new(code: KeyCode) -> Self {
Self {
code,
modifiers: Modifiers::NONE,
kind: KeyEventKind::Press,
}
}
#[must_use]
pub const fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
self.modifiers = modifiers;
self
}
#[must_use]
pub const fn with_kind(mut self, kind: KeyEventKind) -> Self {
self.kind = kind;
self
}
#[must_use]
pub fn is_char(&self, c: char) -> bool {
matches!(self.code, KeyCode::Char(ch) if ch == c)
}
#[must_use]
pub const fn ctrl(&self) -> bool {
self.modifiers.contains(Modifiers::CTRL)
}
#[must_use]
pub const fn alt(&self) -> bool {
self.modifiers.contains(Modifiers::ALT)
}
#[must_use]
pub const fn shift(&self) -> bool {
self.modifiers.contains(Modifiers::SHIFT)
}
#[must_use]
pub const fn super_key(&self) -> bool {
self.modifiers.contains(Modifiers::SUPER)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeyCode {
Char(char),
Enter,
Escape,
Backspace,
Tab,
BackTab,
Delete,
Insert,
Home,
End,
PageUp,
PageDown,
Up,
Down,
Left,
Right,
F(u8),
Null,
MediaPlayPause,
MediaStop,
MediaNextTrack,
MediaPrevTrack,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum KeyEventKind {
#[default]
Press,
Repeat,
Release,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Modifiers: u8 {
const NONE = 0b0000;
const SHIFT = 0b0001;
const ALT = 0b0010;
const CTRL = 0b0100;
const SUPER = 0b1000;
}
}
impl Default for Modifiers {
fn default() -> Self {
Self::NONE
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MouseEvent {
pub kind: MouseEventKind,
pub x: u16,
pub y: u16,
pub modifiers: Modifiers,
}
impl MouseEvent {
#[must_use]
pub const fn new(kind: MouseEventKind, x: u16, y: u16) -> Self {
Self {
kind,
x,
y,
modifiers: Modifiers::NONE,
}
}
#[must_use]
pub const fn with_modifiers(mut self, modifiers: Modifiers) -> Self {
self.modifiers = modifiers;
self
}
#[must_use]
pub const fn position(&self) -> (u16, u16) {
(self.x, self.y)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MouseEventKind {
Down(MouseButton),
Up(MouseButton),
Drag(MouseButton),
Moved,
ScrollUp,
ScrollDown,
ScrollLeft,
ScrollRight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MouseButton {
Left,
Right,
Middle,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PasteEvent {
pub text: String,
pub bracketed: bool,
}
impl PasteEvent {
#[must_use]
pub fn new(text: impl Into<String>, bracketed: bool) -> Self {
Self {
text: text.into(),
bracketed,
}
}
#[must_use]
pub fn bracketed(text: impl Into<String>) -> Self {
Self::new(text, true)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ImePhase {
Start,
Update,
Commit,
Cancel,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImeEvent {
pub phase: ImePhase,
pub text: String,
}
impl ImeEvent {
#[must_use]
pub fn new(phase: ImePhase, text: impl Into<String>) -> Self {
Self {
phase,
text: text.into(),
}
}
#[must_use]
pub fn start() -> Self {
Self::new(ImePhase::Start, "")
}
#[must_use]
pub fn update(preedit: impl Into<String>) -> Self {
Self::new(ImePhase::Update, preedit)
}
#[must_use]
pub fn commit(text: impl Into<String>) -> Self {
Self::new(ImePhase::Commit, text)
}
#[must_use]
pub fn cancel() -> Self {
Self::new(ImePhase::Cancel, "")
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClipboardEvent {
pub content: String,
pub source: ClipboardSource,
}
impl ClipboardEvent {
#[must_use]
pub fn new(content: impl Into<String>, source: ClipboardSource) -> Self {
Self {
content: content.into(),
source,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum ClipboardSource {
Osc52,
#[default]
Unknown,
}
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
fn map_crossterm_event_internal(event: cte::Event) -> Option<Event> {
match event {
cte::Event::Key(key) => map_key_event(key).map(Event::Key),
cte::Event::Mouse(mouse) => Some(Event::Mouse(map_mouse_event(mouse))),
cte::Event::Resize(width, height) => Some(Event::Resize { width, height }),
cte::Event::Paste(text) => Some(Event::Paste(PasteEvent::bracketed(text))),
cte::Event::FocusGained => Some(Event::Focus(true)),
cte::Event::FocusLost => Some(Event::Focus(false)),
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
fn map_key_event(event: cte::KeyEvent) -> Option<KeyEvent> {
let code = map_key_code(event.code)?;
let modifiers = map_modifiers(event.modifiers);
let kind = map_key_kind(event.kind);
Some(KeyEvent {
code,
modifiers,
kind,
})
}
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
fn map_key_kind(kind: cte::KeyEventKind) -> KeyEventKind {
match kind {
cte::KeyEventKind::Press => KeyEventKind::Press,
cte::KeyEventKind::Repeat => KeyEventKind::Repeat,
cte::KeyEventKind::Release => KeyEventKind::Release,
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
pub(crate) fn map_key_code(code: cte::KeyCode) -> Option<KeyCode> {
match code {
cte::KeyCode::Backspace => Some(KeyCode::Backspace),
cte::KeyCode::Enter => Some(KeyCode::Enter),
cte::KeyCode::Left => Some(KeyCode::Left),
cte::KeyCode::Right => Some(KeyCode::Right),
cte::KeyCode::Up => Some(KeyCode::Up),
cte::KeyCode::Down => Some(KeyCode::Down),
cte::KeyCode::Home => Some(KeyCode::Home),
cte::KeyCode::End => Some(KeyCode::End),
cte::KeyCode::PageUp => Some(KeyCode::PageUp),
cte::KeyCode::PageDown => Some(KeyCode::PageDown),
cte::KeyCode::Tab => Some(KeyCode::Tab),
cte::KeyCode::BackTab => Some(KeyCode::BackTab),
cte::KeyCode::Delete => Some(KeyCode::Delete),
cte::KeyCode::Insert => Some(KeyCode::Insert),
cte::KeyCode::F(n) => Some(KeyCode::F(n)),
cte::KeyCode::Char(c) => Some(KeyCode::Char(c)),
cte::KeyCode::Null => Some(KeyCode::Null),
cte::KeyCode::Esc => Some(KeyCode::Escape),
cte::KeyCode::Media(media) => map_media_key(media),
_ => None,
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
fn map_media_key(code: cte::MediaKeyCode) -> Option<KeyCode> {
match code {
cte::MediaKeyCode::Play | cte::MediaKeyCode::Pause | cte::MediaKeyCode::PlayPause => {
Some(KeyCode::MediaPlayPause)
}
cte::MediaKeyCode::Stop => Some(KeyCode::MediaStop),
cte::MediaKeyCode::TrackNext => Some(KeyCode::MediaNextTrack),
cte::MediaKeyCode::TrackPrevious => Some(KeyCode::MediaPrevTrack),
_ => None,
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
fn map_modifiers(modifiers: cte::KeyModifiers) -> Modifiers {
let mut mapped = Modifiers::NONE;
if modifiers.contains(cte::KeyModifiers::SHIFT) {
mapped |= Modifiers::SHIFT;
}
if modifiers.contains(cte::KeyModifiers::ALT) {
mapped |= Modifiers::ALT;
}
if modifiers.contains(cte::KeyModifiers::CONTROL) {
mapped |= Modifiers::CTRL;
}
if modifiers.contains(cte::KeyModifiers::SUPER)
|| modifiers.contains(cte::KeyModifiers::HYPER)
|| modifiers.contains(cte::KeyModifiers::META)
{
mapped |= Modifiers::SUPER;
}
mapped
}
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
fn map_mouse_event(event: cte::MouseEvent) -> MouseEvent {
let kind = match event.kind {
cte::MouseEventKind::Down(button) => MouseEventKind::Down(map_mouse_button(button)),
cte::MouseEventKind::Up(button) => MouseEventKind::Up(map_mouse_button(button)),
cte::MouseEventKind::Drag(button) => MouseEventKind::Drag(map_mouse_button(button)),
cte::MouseEventKind::Moved => MouseEventKind::Moved,
cte::MouseEventKind::ScrollUp => MouseEventKind::ScrollUp,
cte::MouseEventKind::ScrollDown => MouseEventKind::ScrollDown,
cte::MouseEventKind::ScrollLeft => MouseEventKind::ScrollLeft,
cte::MouseEventKind::ScrollRight => MouseEventKind::ScrollRight,
};
let (x, y) = sanitize_crossterm_mouse_coords(event.column, event.row);
MouseEvent::new(kind, x, y).with_modifiers(map_modifiers(event.modifiers))
}
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
fn map_mouse_button(button: cte::MouseButton) -> MouseButton {
match button {
cte::MouseButton::Left => MouseButton::Left,
cte::MouseButton::Right => MouseButton::Right,
cte::MouseButton::Middle => MouseButton::Middle,
}
}
#[cfg(all(not(target_arch = "wasm32"), feature = "crossterm"))]
fn sanitize_crossterm_mouse_coords(x: u16, y: u16) -> (u16, u16) {
(x, y)
}
#[cfg(all(test, not(target_arch = "wasm32"), feature = "crossterm"))]
mod tests {
use super::*;
use crossterm::event as ct_event;
#[test]
fn key_event_is_char() {
let event = KeyEvent::new(KeyCode::Char('q'));
assert!(event.is_char('q'));
assert!(!event.is_char('x'));
}
#[test]
fn key_event_modifiers() {
let event = KeyEvent::new(KeyCode::Char('c')).with_modifiers(Modifiers::CTRL);
assert!(event.ctrl());
assert!(!event.alt());
assert!(!event.shift());
assert!(!event.super_key());
}
#[test]
fn key_event_combined_modifiers() {
let event =
KeyEvent::new(KeyCode::Char('s')).with_modifiers(Modifiers::CTRL | Modifiers::SHIFT);
assert!(event.ctrl());
assert!(event.shift());
assert!(!event.alt());
}
#[test]
fn key_event_kind() {
let press = KeyEvent::new(KeyCode::Enter);
assert_eq!(press.kind, KeyEventKind::Press);
let release = press.with_kind(KeyEventKind::Release);
assert_eq!(release.kind, KeyEventKind::Release);
}
#[test]
fn mouse_event_position() {
let event = MouseEvent::new(MouseEventKind::Down(MouseButton::Left), 10, 20);
assert_eq!(event.position(), (10, 20));
assert_eq!(event.x, 10);
assert_eq!(event.y, 20);
}
#[test]
fn mouse_event_with_modifiers() {
let event = MouseEvent::new(MouseEventKind::Moved, 0, 0).with_modifiers(Modifiers::ALT);
assert_eq!(event.modifiers, Modifiers::ALT);
}
#[test]
fn paste_event_creation() {
let paste = PasteEvent::bracketed("hello world");
assert_eq!(paste.text, "hello world");
assert!(paste.bracketed);
}
#[test]
fn clipboard_event_creation() {
let clip = ClipboardEvent::new("copied text", ClipboardSource::Osc52);
assert_eq!(clip.content, "copied text");
assert_eq!(clip.source, ClipboardSource::Osc52);
}
#[test]
fn event_variants() {
let _key = Event::Key(KeyEvent::new(KeyCode::Char('a')));
let _mouse = Event::Mouse(MouseEvent::new(
MouseEventKind::Down(MouseButton::Left),
0,
0,
));
let _resize = Event::Resize {
width: 80,
height: 24,
};
let _paste = Event::Paste(PasteEvent::bracketed("test"));
let _ime = Event::Ime(ImeEvent::update("æ¼¢"));
let _focus = Event::Focus(true);
let _clipboard = Event::Clipboard(ClipboardEvent::new("test", ClipboardSource::Unknown));
let _tick = Event::Tick;
}
#[test]
fn ime_event_constructors() {
assert_eq!(ImeEvent::start().phase, ImePhase::Start);
assert_eq!(ImeEvent::update("x"), ImeEvent::new(ImePhase::Update, "x"));
assert_eq!(ImeEvent::commit("æ¼¢").phase, ImePhase::Commit);
assert_eq!(ImeEvent::cancel().phase, ImePhase::Cancel);
}
#[test]
fn modifiers_default() {
assert_eq!(Modifiers::default(), Modifiers::NONE);
}
#[test]
fn key_event_kind_default() {
assert_eq!(KeyEventKind::default(), KeyEventKind::Press);
}
#[test]
fn clipboard_source_default() {
assert_eq!(ClipboardSource::default(), ClipboardSource::Unknown);
}
#[test]
fn function_keys() {
let f1 = KeyEvent::new(KeyCode::F(1));
let f12 = KeyEvent::new(KeyCode::F(12));
assert_eq!(f1.code, KeyCode::F(1));
assert_eq!(f12.code, KeyCode::F(12));
}
#[test]
fn event_is_clone_and_eq() {
let event = Event::Key(KeyEvent::new(KeyCode::Char('x')));
let cloned = event.clone();
assert_eq!(event, cloned);
}
#[test]
fn map_modifiers_ctrl() {
let mapped = map_modifiers(ct_event::KeyModifiers::CONTROL);
assert!(mapped.contains(Modifiers::CTRL));
assert!(!mapped.contains(Modifiers::SHIFT));
}
#[test]
fn map_modifiers_alt() {
let mapped = map_modifiers(ct_event::KeyModifiers::ALT);
assert!(mapped.contains(Modifiers::ALT));
}
#[test]
fn map_modifiers_super_variants() {
let super_mapped = map_modifiers(ct_event::KeyModifiers::SUPER);
assert!(super_mapped.contains(Modifiers::SUPER));
let hyper_mapped = map_modifiers(ct_event::KeyModifiers::HYPER);
assert!(hyper_mapped.contains(Modifiers::SUPER));
let meta_mapped = map_modifiers(ct_event::KeyModifiers::META);
assert!(meta_mapped.contains(Modifiers::SUPER));
}
#[test]
fn map_modifiers_combined() {
let combined = ct_event::KeyModifiers::SHIFT | ct_event::KeyModifiers::CONTROL;
let mapped = map_modifiers(combined);
assert!(mapped.contains(Modifiers::SHIFT));
assert!(mapped.contains(Modifiers::CTRL));
assert!(!mapped.contains(Modifiers::ALT));
}
#[test]
fn map_mouse_button_all() {
assert_eq!(
map_mouse_button(ct_event::MouseButton::Left),
MouseButton::Left
);
assert_eq!(
map_mouse_button(ct_event::MouseButton::Right),
MouseButton::Right
);
assert_eq!(
map_mouse_button(ct_event::MouseButton::Middle),
MouseButton::Middle
);
}
#[test]
fn map_mouse_event_down() {
let ct_event = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::Down(ct_event::MouseButton::Left),
column: 10,
row: 5,
modifiers: ct_event::KeyModifiers::NONE,
};
let mapped = map_mouse_event(ct_event);
assert!(matches!(
mapped.kind,
MouseEventKind::Down(MouseButton::Left)
));
assert_eq!(mapped.x, 10);
assert_eq!(mapped.y, 5);
}
#[test]
fn map_mouse_event_up() {
let ct_event = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::Up(ct_event::MouseButton::Right),
column: 20,
row: 15,
modifiers: ct_event::KeyModifiers::NONE,
};
let mapped = map_mouse_event(ct_event);
assert!(matches!(
mapped.kind,
MouseEventKind::Up(MouseButton::Right)
));
assert_eq!(mapped.x, 20);
assert_eq!(mapped.y, 15);
}
#[test]
fn map_mouse_event_drag() {
let ct_event = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::Drag(ct_event::MouseButton::Middle),
column: 5,
row: 10,
modifiers: ct_event::KeyModifiers::NONE,
};
let mapped = map_mouse_event(ct_event);
assert!(matches!(
mapped.kind,
MouseEventKind::Drag(MouseButton::Middle)
));
}
#[test]
fn map_mouse_event_moved() {
let ct_event = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::Moved,
column: 0,
row: 0,
modifiers: ct_event::KeyModifiers::NONE,
};
let mapped = map_mouse_event(ct_event);
assert!(matches!(mapped.kind, MouseEventKind::Moved));
}
#[test]
fn map_mouse_event_scroll() {
let scroll_up = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::ScrollUp,
column: 0,
row: 0,
modifiers: ct_event::KeyModifiers::NONE,
};
let scroll_down = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::ScrollDown,
column: 0,
row: 0,
modifiers: ct_event::KeyModifiers::NONE,
};
let scroll_left = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::ScrollLeft,
column: 0,
row: 0,
modifiers: ct_event::KeyModifiers::NONE,
};
let scroll_right = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::ScrollRight,
column: 0,
row: 0,
modifiers: ct_event::KeyModifiers::NONE,
};
assert!(matches!(
map_mouse_event(scroll_up).kind,
MouseEventKind::ScrollUp
));
assert!(matches!(
map_mouse_event(scroll_down).kind,
MouseEventKind::ScrollDown
));
assert!(matches!(
map_mouse_event(scroll_left).kind,
MouseEventKind::ScrollLeft
));
assert!(matches!(
map_mouse_event(scroll_right).kind,
MouseEventKind::ScrollRight
));
}
#[test]
fn map_mouse_event_modifiers() {
let ct_event = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::Down(ct_event::MouseButton::Left),
column: 0,
row: 0,
modifiers: ct_event::KeyModifiers::SHIFT | ct_event::KeyModifiers::ALT,
};
let mapped = map_mouse_event(ct_event);
assert!(mapped.modifiers.contains(Modifiers::SHIFT));
assert!(mapped.modifiers.contains(Modifiers::ALT));
}
#[test]
fn map_mouse_event_respects_large_coordinates() {
let ct_event = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::Moved,
column: 1600,
row: 640,
modifiers: ct_event::KeyModifiers::NONE,
};
let mapped = map_mouse_event(ct_event);
assert_eq!(mapped.x, 1600);
assert_eq!(mapped.y, 640);
}
#[test]
fn map_mouse_event_keeps_cell_coordinates() {
let ct_event = ct_event::MouseEvent {
kind: ct_event::MouseEventKind::Moved,
column: 120,
row: 40,
modifiers: ct_event::KeyModifiers::NONE,
};
let mapped = map_mouse_event(ct_event);
assert_eq!(mapped.x, 120);
assert_eq!(mapped.y, 40);
}
#[test]
fn map_key_event_char() {
let ct_event = ct_event::KeyEvent {
code: ct_event::KeyCode::Char('x'),
modifiers: ct_event::KeyModifiers::CONTROL,
kind: ct_event::KeyEventKind::Press,
state: ct_event::KeyEventState::NONE,
};
let mapped = map_key_event(ct_event).expect("should map");
assert_eq!(mapped.code, KeyCode::Char('x'));
assert!(mapped.modifiers.contains(Modifiers::CTRL));
assert_eq!(mapped.kind, KeyEventKind::Press);
}
#[test]
fn map_key_event_function_key() {
let ct_event = ct_event::KeyEvent {
code: ct_event::KeyCode::F(5),
modifiers: ct_event::KeyModifiers::NONE,
kind: ct_event::KeyEventKind::Press,
state: ct_event::KeyEventState::NONE,
};
let mapped = map_key_event(ct_event).expect("should map");
assert_eq!(mapped.code, KeyCode::F(5));
}
#[test]
fn map_crossterm_event_key() {
let ct_event = ct_event::Event::Key(ct_event::KeyEvent {
code: ct_event::KeyCode::Enter,
modifiers: ct_event::KeyModifiers::NONE,
kind: ct_event::KeyEventKind::Press,
state: ct_event::KeyEventState::NONE,
});
let mapped = map_crossterm_event_internal(ct_event).expect("should map");
assert!(matches!(mapped, Event::Key(_)));
}
#[test]
fn map_crossterm_event_mouse() {
let ct_event = ct_event::Event::Mouse(ct_event::MouseEvent {
kind: ct_event::MouseEventKind::Down(ct_event::MouseButton::Left),
column: 10,
row: 5,
modifiers: ct_event::KeyModifiers::NONE,
});
let mapped = map_crossterm_event_internal(ct_event).expect("should map");
assert!(matches!(mapped, Event::Mouse(_)));
}
#[test]
fn map_crossterm_event_resize() {
let ct_event = ct_event::Event::Resize(80, 24);
let mapped = map_crossterm_event_internal(ct_event).expect("should map");
assert!(matches!(
mapped,
Event::Resize {
width: 80,
height: 24
}
));
}
#[test]
fn map_crossterm_event_paste() {
let ct_event = ct_event::Event::Paste("hello world".to_string());
let mapped = map_crossterm_event_internal(ct_event).expect("should map");
match mapped {
Event::Paste(paste) => assert_eq!(paste.text, "hello world"),
_ => panic!("expected Paste event"),
}
}
#[test]
fn map_crossterm_event_focus() {
let gained = ct_event::Event::FocusGained;
let lost = ct_event::Event::FocusLost;
assert!(matches!(
map_crossterm_event_internal(gained),
Some(Event::Focus(true))
));
assert!(matches!(
map_crossterm_event_internal(lost),
Some(Event::Focus(false))
));
}
#[test]
fn map_key_kind_repeat_and_release() {
assert_eq!(
map_key_kind(ct_event::KeyEventKind::Repeat),
KeyEventKind::Repeat
);
assert_eq!(
map_key_kind(ct_event::KeyEventKind::Release),
KeyEventKind::Release
);
}
#[test]
fn map_key_code_escape() {
assert_eq!(map_key_code(ct_event::KeyCode::Esc), Some(KeyCode::Escape));
}
#[test]
fn map_key_code_unmapped_returns_none() {
assert_eq!(map_key_code(ct_event::KeyCode::CapsLock), None);
assert_eq!(map_key_code(ct_event::KeyCode::ScrollLock), None);
assert_eq!(map_key_code(ct_event::KeyCode::NumLock), None);
assert_eq!(map_key_code(ct_event::KeyCode::PrintScreen), None);
assert_eq!(map_key_code(ct_event::KeyCode::Pause), None);
assert_eq!(map_key_code(ct_event::KeyCode::Menu), None);
assert_eq!(map_key_code(ct_event::KeyCode::KeypadBegin), None);
assert_eq!(
map_key_code(ct_event::KeyCode::Modifier(
ct_event::ModifierKeyCode::LeftShift
)),
None
);
}
#[test]
fn map_media_key_known_and_unknown_variants() {
assert_eq!(
map_media_key(ct_event::MediaKeyCode::Play),
Some(KeyCode::MediaPlayPause)
);
assert_eq!(
map_media_key(ct_event::MediaKeyCode::Pause),
Some(KeyCode::MediaPlayPause)
);
assert_eq!(
map_media_key(ct_event::MediaKeyCode::PlayPause),
Some(KeyCode::MediaPlayPause)
);
assert_eq!(
map_media_key(ct_event::MediaKeyCode::TrackNext),
Some(KeyCode::MediaNextTrack)
);
assert_eq!(
map_media_key(ct_event::MediaKeyCode::TrackPrevious),
Some(KeyCode::MediaPrevTrack)
);
assert_eq!(
map_media_key(ct_event::MediaKeyCode::Stop),
Some(KeyCode::MediaStop)
);
assert_eq!(map_media_key(ct_event::MediaKeyCode::Reverse), None);
}
#[test]
fn from_crossterm_returns_none_for_unmapped_keys() {
let ct_event = ct_event::Event::Key(ct_event::KeyEvent {
code: ct_event::KeyCode::CapsLock,
modifiers: ct_event::KeyModifiers::NONE,
kind: ct_event::KeyEventKind::Press,
state: ct_event::KeyEventState::NONE,
});
assert_eq!(Event::from_crossterm(ct_event), None);
}
}