use std::time::Duration;
use anyhow::{Context, Result};
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};
use super::app::{App, Outcome};
const PAGE_SIZE: usize = 10;
const POLL_TIMEOUT: Duration = Duration::from_millis(200);
pub fn next(app: &mut App) -> Result<Outcome> {
if !event::poll(POLL_TIMEOUT).context("crossterm event poll")? {
return Ok(Outcome::Continue);
}
match event::read().context("crossterm event read")? {
Event::Key(key) if key.kind == event::KeyEventKind::Press => {
if app.help_visible {
return Ok(dispatch_help_key(app, key));
}
if app.filter_active {
return Ok(dispatch_filter_key(app, key));
}
Ok(dispatch_nav_key(app, key))
}
Event::Mouse(me) => Ok(dispatch_mouse(app, me)),
_ => Ok(Outcome::Continue),
}
}
fn dispatch_nav_key(app: &mut App, key: KeyEvent) -> Outcome {
if key.modifiers.contains(KeyModifiers::CONTROL)
&& matches!(key.code, KeyCode::Char('c' | 'g' | 'd'))
{
return Outcome::Cancel;
}
match key.code {
KeyCode::Esc | KeyCode::Char('q') => Outcome::Cancel,
KeyCode::Enter => app
.selected_path()
.map_or(Outcome::Continue, Outcome::Select),
KeyCode::Char('/') => {
app.open_filter();
Outcome::Continue
}
KeyCode::Char('?') => {
app.toggle_help();
Outcome::Continue
}
KeyCode::Up | KeyCode::Char('k') => {
app.move_up();
Outcome::Continue
}
KeyCode::Down | KeyCode::Char('j') => {
app.move_down();
Outcome::Continue
}
KeyCode::PageUp => {
app.page_up(PAGE_SIZE);
Outcome::Continue
}
KeyCode::PageDown => {
app.page_down(PAGE_SIZE);
Outcome::Continue
}
KeyCode::Home | KeyCode::Char('g') => {
app.move_first();
Outcome::Continue
}
KeyCode::End | KeyCode::Char('G') => {
app.move_last();
Outcome::Continue
}
_ => Outcome::Continue,
}
}
fn dispatch_filter_key(app: &mut App, key: KeyEvent) -> Outcome {
if key.modifiers.contains(KeyModifiers::CONTROL)
&& matches!(key.code, KeyCode::Char('c' | 'g' | 'd'))
{
return Outcome::Cancel;
}
match key.code {
KeyCode::Esc => {
app.close_filter(true);
Outcome::Continue
}
KeyCode::Enter => {
app.close_filter(false);
app.selected_path()
.map_or(Outcome::Continue, Outcome::Select)
}
KeyCode::Backspace => {
app.filter_pop();
Outcome::Continue
}
KeyCode::Up => {
app.move_up();
Outcome::Continue
}
KeyCode::Down => {
app.move_down();
Outcome::Continue
}
KeyCode::PageUp => {
app.page_up(PAGE_SIZE);
Outcome::Continue
}
KeyCode::PageDown => {
app.page_down(PAGE_SIZE);
Outcome::Continue
}
KeyCode::Char(c) => {
app.filter_push(c);
Outcome::Continue
}
_ => Outcome::Continue,
}
}
fn dispatch_help_key(app: &mut App, key: KeyEvent) -> Outcome {
if key.modifiers.contains(KeyModifiers::CONTROL)
&& matches!(key.code, KeyCode::Char('c' | 'g' | 'd'))
{
return Outcome::Cancel;
}
match key.code {
KeyCode::Esc | KeyCode::Char('?' | 'q') => {
app.toggle_help();
Outcome::Continue
}
_ => Outcome::Continue,
}
}
fn dispatch_mouse(app: &mut App, me: MouseEvent) -> Outcome {
match me.kind {
MouseEventKind::ScrollUp => {
app.move_up();
Outcome::Continue
}
MouseEventKind::ScrollDown => {
app.move_down();
Outcome::Continue
}
_ => Outcome::Continue,
}
}