use anyhow::Result;
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use std::time::Duration;
pub fn key_press_filter(ev: Event) -> Option<KeyEvent> {
match ev {
Event::Key(key) if key.kind == KeyEventKind::Press => Some(key),
_ => None,
}
}
pub fn next_key_press(timeout: Duration) -> Result<Option<KeyEvent>> {
if event::poll(timeout)? {
return Ok(key_press_filter(event::read()?));
}
Ok(None)
}
pub fn is_ctrl_c(key: &KeyEvent) -> bool {
key.modifiers.contains(KeyModifiers::CONTROL)
&& matches!(key.code, KeyCode::Char('c') | KeyCode::Char('C'))
}
pub fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
]
.as_ref(),
)
.split(r);
let horizontal = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
]
.as_ref(),
)
.split(popup_layout[1]);
horizontal[1]
}
#[cfg(test)]
mod tests {
use super::*;
use crossterm::event::{KeyCode, KeyEventKind, KeyEventState, KeyModifiers};
fn key_event(kind: KeyEventKind) -> Event {
Event::Key(KeyEvent {
code: KeyCode::Char('j'),
modifiers: KeyModifiers::NONE,
kind,
state: KeyEventState::NONE,
})
}
#[test]
fn key_press_filter_accepts_press_events() {
let got = key_press_filter(key_event(KeyEventKind::Press));
assert_eq!(got.map(|k| k.code), Some(KeyCode::Char('j')));
}
#[test]
fn key_press_filter_drops_release_and_repeat_events() {
assert!(key_press_filter(key_event(KeyEventKind::Release)).is_none());
assert!(key_press_filter(key_event(KeyEventKind::Repeat)).is_none());
}
#[test]
fn key_press_filter_ignores_non_key_events() {
assert!(key_press_filter(Event::Resize(80, 24)).is_none());
}
#[test]
fn is_ctrl_c_matches_the_quit_chord_in_both_cases() {
assert!(is_ctrl_c(&KeyEvent::new(
KeyCode::Char('c'),
KeyModifiers::CONTROL
)));
assert!(is_ctrl_c(&KeyEvent::new(
KeyCode::Char('C'),
KeyModifiers::CONTROL
)));
assert!(!is_ctrl_c(&KeyEvent::new(
KeyCode::Char('c'),
KeyModifiers::NONE
)));
assert!(!is_ctrl_c(&KeyEvent::new(
KeyCode::Char('x'),
KeyModifiers::CONTROL
)));
}
}