Skip to content

Commit 5ee4931

Browse files
committed
Add several more methods relating event ergonomics
- add is_key_release() and is_key_repeat() checks - add as_key_event() - rename as_key_press() to as_key_press_event() - add as_key_repeat_event() - add as_key_release_event() - add as_mouse_event() - add as_paste_event() - more tests - update event-match and key-display examples
1 parent 5dea800 commit 5ee4931

File tree

3 files changed

+222
-70
lines changed

3 files changed

+222
-70
lines changed

examples/event-match-modifiers.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
66

77
fn match_event(event: Event) {
8-
if let Some(key) = event.as_key_press() {
8+
if let Some(key) = event.as_key_press_event() {
99
match key {
1010
KeyEvent {
1111
modifiers: KeyModifiers::CONTROL,
@@ -33,7 +33,7 @@ fn match_event(event: Event) {
3333
KeyEvent {
3434
code, modifiers, ..
3535
} => {
36-
if *modifiers == (KeyModifiers::ALT | KeyModifiers::SHIFT) {
36+
if modifiers == (KeyModifiers::ALT | KeyModifiers::SHIFT) {
3737
println!("Alt + Shift {:?}", code);
3838
} else {
3939
println!("({:?}) with key: {:?}", modifiers, code)

examples/key-display.rs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
88
use std::io;
99

10-
use crossterm::event::{KeyEventKind, KeyModifiers};
10+
use crossterm::event::KeyModifiers;
1111
use crossterm::{
12-
event::{read, Event, KeyCode},
12+
event::{read, KeyCode},
1313
terminal::{disable_raw_mode, enable_raw_mode},
1414
};
1515

@@ -29,20 +29,17 @@ fn main() -> io::Result<()> {
2929
}
3030

3131
fn print_events() -> io::Result<()> {
32-
loop {
33-
let event = read()?;
34-
match event {
35-
Event::Key(event) if event.kind == KeyEventKind::Press => {
36-
print!("Key pressed: ");
37-
if event.modifiers != KeyModifiers::NONE {
38-
print!("{}+", event.modifiers);
39-
}
40-
println!("{}\r", event.code);
41-
if event.code == KeyCode::Esc {
42-
break;
43-
}
44-
}
45-
_ => {}
32+
while let Ok(event) = read() {
33+
let Some(event) = event.as_key_press_event() else {
34+
continue;
35+
};
36+
let modifier = match event.modifiers {
37+
KeyModifiers::NONE => "".to_string(),
38+
_ => format!("{:}+", event.modifiers),
39+
};
40+
println!("Key pressed: {modifier}{code}\r", code = event.code);
41+
if event.code == KeyCode::Esc {
42+
break;
4643
}
4744
}
4845
Ok(())

src/event.rs

Lines changed: 207 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,54 @@ impl Event {
593593
)
594594
}
595595

596+
/// Returns `true` if the event is a key release event.
597+
#[inline]
598+
pub fn is_key_release(&self) -> bool {
599+
matches!(
600+
self,
601+
Event::Key(KeyEvent {
602+
kind: KeyEventKind::Release,
603+
..
604+
})
605+
)
606+
}
607+
608+
/// Returns `true` if the event is a key repeat event.
609+
#[inline]
610+
pub fn is_key_repeat(&self) -> bool {
611+
matches!(
612+
self,
613+
Event::Key(KeyEvent {
614+
kind: KeyEventKind::Repeat,
615+
..
616+
})
617+
)
618+
}
619+
620+
/// Returns the key event if the event is a key event, otherwise `None`.
621+
///
622+
/// This is a convenience method that makes apps that only care about key events easier to write.
623+
///
624+
/// # Examples
625+
///
626+
/// The following code runs a loop that only processes key events:
627+
///
628+
/// ```no_run
629+
/// use crossterm::event;
630+
///
631+
/// while let Some(key_event) = event::read()?.as_key_event() {
632+
/// // ...
633+
/// }
634+
/// # std::io::Result::Ok(())
635+
/// ```
636+
#[inline]
637+
pub fn as_key_event(&self) -> Option<KeyEvent> {
638+
match self {
639+
Event::Key(event) => Some(*event),
640+
_ => None,
641+
}
642+
}
643+
596644
/// Returns an Option containing the KeyEvent if the event is a key press event.
597645
///
598646
/// This is a convenience method that makes apps that only care about key press events, and not
@@ -608,14 +656,100 @@ impl Event {
608656
/// use crossterm::event;
609657
///
610658
/// while let Ok(event) = event::read() {
611-
/// if let Some(key) = event.as_key_press() {
659+
/// if let Some(key) = event.as_key_press_event() {
612660
/// // ...
613661
/// }
614662
/// }
615663
#[inline]
616-
pub fn as_key_press(&self) -> Option<&KeyEvent> {
664+
pub fn as_key_press_event(&self) -> Option<KeyEvent> {
665+
match self {
666+
Event::Key(event) if self.is_key_press() => Some(*event),
667+
_ => None,
668+
}
669+
}
670+
671+
/// Returns an Option containing the KeyEvent if the event is a key release event.
672+
#[inline]
673+
pub fn as_key_release_event(&self) -> Option<KeyEvent> {
674+
match self {
675+
Event::Key(event) if event.kind == KeyEventKind::Release => Some(*event),
676+
_ => None,
677+
}
678+
}
679+
680+
/// Returns an Option containing the KeyEvent if the event is a key repeat event.
681+
#[inline]
682+
pub fn as_key_repeat_event(&self) -> Option<KeyEvent> {
683+
match self {
684+
Event::Key(event) if event.kind == KeyEventKind::Repeat => Some(*event),
685+
_ => None,
686+
}
687+
}
688+
689+
/// Returns the mouse event if the event is a mouse event, otherwise `None`.
690+
///
691+
/// This is a convenience method that makes code which only cares about mouse events easier to
692+
/// write.
693+
///
694+
/// # Examples
695+
///
696+
/// ```no_run
697+
/// use crossterm::event;
698+
///
699+
/// while let Some(mouse_event) = event::read()?.as_mouse_event() {
700+
/// // ...
701+
/// }
702+
/// # std::io::Result::Ok(())
703+
/// ```
704+
#[inline]
705+
pub fn as_mouse_event(&self) -> Option<MouseEvent> {
706+
match self {
707+
Event::Mouse(event) => Some(*event),
708+
_ => None,
709+
}
710+
}
711+
712+
/// Returns the pasted string if the event is a paste event, otherwise `None`.
713+
///
714+
/// This is a convenience method that makes code which only cares about paste events easier to write.
715+
///
716+
/// # Examples
717+
///
718+
/// ```no_run
719+
/// use crossterm::event;
720+
///
721+
/// while let Some(paste) = event::read()?.as_paste_event() {
722+
/// // ...
723+
/// }
724+
/// # std::io::Result::Ok(())
725+
/// ```
726+
#[cfg(feature = "bracketed-paste")]
727+
#[inline]
728+
pub fn as_paste_event(&self) -> Option<&str> {
729+
match self {
730+
Event::Paste(paste) => Some(paste),
731+
_ => None,
732+
}
733+
}
734+
735+
/// Returns the size as a tuple if the event is a resize event, otherwise `None`.
736+
///
737+
/// This is a convenience method that makes code which only cares about resize events easier to write.
738+
///
739+
/// # Examples
740+
///
741+
/// ```no_run
742+
/// use crossterm::event;
743+
///
744+
/// while let Some((columns, rows)) = event::read()?.as_resize_event() {
745+
/// // ...
746+
/// }
747+
/// # std::io::Result::Ok(())
748+
/// ```
749+
#[inline]
750+
pub fn as_resize_event(&self) -> Option<(u16, u16)> {
617751
match self {
618-
Event::Key(event) if self.is_key_press() => Some(event),
752+
Event::Resize(columns, rows) => Some((*columns, *rows)),
619753
_ => None,
620754
}
621755
}
@@ -1485,86 +1619,107 @@ mod tests {
14851619
assert_eq!(format!("{}", Modifier(RightSuper)), "Right Super");
14861620
}
14871621

1622+
const ESC_PRESSED: KeyEvent =
1623+
KeyEvent::new_with_kind(KeyCode::Esc, KeyModifiers::empty(), KeyEventKind::Press);
1624+
const ESC_RELEASED: KeyEvent =
1625+
KeyEvent::new_with_kind(KeyCode::Esc, KeyModifiers::empty(), KeyEventKind::Release);
1626+
const ESC_REPEAT: KeyEvent =
1627+
KeyEvent::new_with_kind(KeyCode::Esc, KeyModifiers::empty(), KeyEventKind::Repeat);
1628+
const MOUSE_CLICK: MouseEvent = MouseEvent {
1629+
kind: MouseEventKind::Down(MouseButton::Left),
1630+
column: 1,
1631+
row: 1,
1632+
modifiers: KeyModifiers::empty(),
1633+
};
1634+
14881635
#[test]
1489-
fn test_event_is() {
1636+
fn event_is() {
14901637
let event = Event::FocusGained;
14911638
assert!(event.is_focus_gained());
1639+
assert!(event.is_focus_gained());
14921640
assert!(!event.is_key());
14931641

14941642
let event = Event::FocusLost;
14951643
assert!(event.is_focus_lost());
1644+
assert!(!event.is_focus_gained());
14961645
assert!(!event.is_key());
14971646

14981647
let event = Event::Resize(1, 1);
14991648
assert!(event.is_resize());
15001649
assert!(!event.is_key());
15011650

1502-
let event = Event::Key(KeyCode::Esc.into());
1503-
assert!(event.is_key());
1504-
assert!(!event.is_focus_gained());
1505-
1506-
let event = Event::Mouse(MouseEvent {
1507-
kind: MouseEventKind::Down(MouseButton::Left),
1508-
column: 1,
1509-
row: 1,
1510-
modifiers: KeyModifiers::empty(),
1511-
});
1512-
assert!(event.is_mouse());
1513-
assert!(!event.is_key());
1514-
}
1515-
1516-
const ESC_PRESSED: KeyEvent =
1517-
KeyEvent::new_with_kind(KeyCode::Esc, KeyModifiers::empty(), KeyEventKind::Press);
1518-
const ESC_RELEASED: KeyEvent =
1519-
KeyEvent::new_with_kind(KeyCode::Esc, KeyModifiers::empty(), KeyEventKind::Release);
1520-
const ESC_REPEAT: KeyEvent =
1521-
KeyEvent::new_with_kind(KeyCode::Esc, KeyModifiers::empty(), KeyEventKind::Repeat);
1522-
1523-
#[test]
1524-
fn test_event_is_key_press() {
15251651
let event = Event::Key(ESC_PRESSED);
1652+
assert!(event.is_key());
15261653
assert!(event.is_key_press());
1654+
assert!(!event.is_key_release());
1655+
assert!(!event.is_key_repeat());
1656+
assert!(!event.is_focus_gained());
15271657

15281658
let event = Event::Key(ESC_RELEASED);
1659+
assert!(event.is_key());
15291660
assert!(!event.is_key_press());
1661+
assert!(event.is_key_release());
1662+
assert!(!event.is_key_repeat());
1663+
assert!(!event.is_focus_gained());
15301664

15311665
let event = Event::Key(ESC_REPEAT);
1666+
assert!(event.is_key());
15321667
assert!(!event.is_key_press());
1668+
assert!(!event.is_key_release());
1669+
assert!(event.is_key_repeat());
1670+
assert!(!event.is_focus_gained());
15331671

1534-
let event = Event::FocusGained;
1535-
assert!(!event.is_key_press());
1672+
let event = Event::Mouse(MOUSE_CLICK);
1673+
assert!(event.is_mouse());
1674+
assert!(!event.is_key());
1675+
1676+
#[cfg(feature = "bracketed-paste")]
1677+
{
1678+
let event = Event::Paste("".to_string());
1679+
assert!(event.is_paste());
1680+
assert!(!event.is_key());
1681+
}
15361682
}
15371683

15381684
#[test]
1539-
fn test_event_as_key_press() {
1685+
fn event_as() {
1686+
let event = Event::FocusGained;
1687+
assert_eq!(event.as_key_event(), None);
1688+
15401689
let event = Event::Key(ESC_PRESSED);
1541-
assert_eq!(event.as_key_press(), Some(&ESC_PRESSED));
1690+
assert_eq!(event.as_key_event(), Some(ESC_PRESSED));
1691+
assert_eq!(event.as_key_press_event(), Some(ESC_PRESSED));
1692+
assert_eq!(event.as_key_release_event(), None);
1693+
assert_eq!(event.as_key_repeat_event(), None);
1694+
assert_eq!(event.as_resize_event(), None);
15421695

15431696
let event = Event::Key(ESC_RELEASED);
1544-
assert_eq!(event.as_key_press(), None);
1697+
assert_eq!(event.as_key_event(), Some(ESC_RELEASED));
1698+
assert_eq!(event.as_key_release_event(), Some(ESC_RELEASED));
1699+
assert_eq!(event.as_key_press_event(), None);
1700+
assert_eq!(event.as_key_repeat_event(), None);
1701+
assert_eq!(event.as_resize_event(), None);
15451702

15461703
let event = Event::Key(ESC_REPEAT);
1547-
assert_eq!(event.as_key_press(), None);
1704+
assert_eq!(event.as_key_event(), Some(ESC_REPEAT));
1705+
assert_eq!(event.as_key_repeat_event(), Some(ESC_REPEAT));
1706+
assert_eq!(event.as_key_press_event(), None);
1707+
assert_eq!(event.as_key_release_event(), None);
1708+
assert_eq!(event.as_resize_event(), None);
15481709

1549-
let event = Event::FocusGained;
1550-
assert_eq!(event.as_key_press(), None);
1551-
}
1710+
let event = Event::Resize(1, 1);
1711+
assert_eq!(event.as_resize_event(), Some((1, 1)));
1712+
assert_eq!(event.as_key_event(), None);
15521713

1553-
#[test]
1554-
fn test_key_event_is() {
1555-
let event = ESC_PRESSED;
1556-
assert!(event.is_press());
1557-
assert!(!event.is_release());
1558-
assert!(!event.is_repeat());
1559-
1560-
let event = ESC_RELEASED;
1561-
assert!(!event.is_press());
1562-
assert!(event.is_release());
1563-
assert!(!event.is_repeat());
1564-
1565-
let event = ESC_REPEAT;
1566-
assert!(!event.is_press());
1567-
assert!(!event.is_release());
1568-
assert!(event.is_repeat());
1714+
let event = Event::Mouse(MOUSE_CLICK);
1715+
assert_eq!(event.as_mouse_event(), Some(MOUSE_CLICK));
1716+
assert_eq!(event.as_key_event(), None);
1717+
1718+
#[cfg(feature = "bracketed-paste")]
1719+
{
1720+
let event = Event::Paste("".to_string());
1721+
assert_eq!(event.as_paste_event(), Some(""));
1722+
assert_eq!(event.as_key_event(), None);
1723+
}
15691724
}
15701725
}

0 commit comments

Comments
 (0)