use crossterm::event::Event;
use crossterm::event::KeyEvent;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TerminalEvent {
Key(KeyEvent),
Paste(String),
Resize { width: u16, height: u16 },
FocusChanged(bool),
}
pub fn map_crossterm_event(event: Event) -> Option<TerminalEvent> {
match event {
Event::Key(key) => Some(TerminalEvent::Key(key)),
Event::Paste(text) => Some(TerminalEvent::Paste(text)),
Event::Resize(width, height) => Some(TerminalEvent::Resize { width, height }),
Event::FocusGained => Some(TerminalEvent::FocusChanged(true)),
Event::FocusLost => Some(TerminalEvent::FocusChanged(false)),
Event::Mouse(_) => None,
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TerminalState {
pub width: u16,
pub height: u16,
pub focused: bool,
}
impl TerminalState {
pub const fn new(width: u16, height: u16) -> Self {
Self {
width,
height,
focused: true,
}
}
pub fn apply(&mut self, event: &TerminalEvent) -> bool {
match event {
TerminalEvent::Resize { width, height } => {
let changed = self.width != *width || self.height != *height;
self.width = *width;
self.height = *height;
changed
}
TerminalEvent::FocusChanged(focused) => {
let changed = self.focused != *focused;
self.focused = *focused;
changed
}
TerminalEvent::Key(_) | TerminalEvent::Paste(_) => false,
}
}
}
#[cfg(test)]
mod tests {
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers;
use super::*;
#[test]
fn maps_focus_resize_paste_and_key_without_app_events() {
assert_eq!(
map_crossterm_event(Event::FocusLost),
Some(TerminalEvent::FocusChanged(false))
);
assert_eq!(
map_crossterm_event(Event::FocusGained),
Some(TerminalEvent::FocusChanged(true))
);
assert_eq!(
map_crossterm_event(Event::Resize(80, 24)),
Some(TerminalEvent::Resize {
width: 80,
height: 24
})
);
assert_eq!(
map_crossterm_event(Event::Paste("hello".into())),
Some(TerminalEvent::Paste("hello".into()))
);
let key = KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE);
assert_eq!(
map_crossterm_event(Event::Key(key)),
Some(TerminalEvent::Key(key))
);
}
#[test]
fn focus_and_resize_change_state_and_require_redraw() {
let mut state = TerminalState::new(80, 24);
assert!(state.apply(&TerminalEvent::FocusChanged(false)));
assert!(!state.focused);
assert!(!state.apply(&TerminalEvent::FocusChanged(false)));
assert!(state.apply(&TerminalEvent::Resize {
width: 160,
height: 48,
}));
assert_eq!((state.width, state.height), (160, 48));
assert!(!state.apply(&TerminalEvent::Resize {
width: 160,
height: 48,
}));
}
}