use super::app::{TuiApp, ViewMode};
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers};
use std::time::Duration;
pub fn poll_event(timeout: Duration) -> anyhow::Result<Option<KeyEvent>> {
if event::poll(timeout)?
&& let Event::Key(key) = event::read()?
{
return Ok(Some(key));
}
Ok(None)
}
pub fn handle_key_event(app: &mut TuiApp, key: KeyEvent, num_nodes: usize) {
match key.code {
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
app.quit();
return;
}
KeyCode::Char('q') => {
app.quit();
return;
}
KeyCode::Char('?') => {
app.toggle_help();
return;
}
KeyCode::Esc => {
if app.show_help {
app.show_help = false;
app.mark_needs_redraw();
} else {
app.show_summary();
}
return;
}
KeyCode::Char('l') => {
app.toggle_log_panel();
return;
}
_ => {}
}
if app.log_panel_visible {
match key.code {
KeyCode::Char('j') => {
app.scroll_log_down(1);
return;
}
KeyCode::Char('k') => {
app.scroll_log_up(1);
return;
}
KeyCode::Char('+') | KeyCode::Char('=') => {
app.increase_log_panel_height();
return;
}
KeyCode::Char('-') | KeyCode::Char('_') => {
app.decrease_log_panel_height();
return;
}
KeyCode::Char('t') => {
app.toggle_log_timestamps();
return;
}
_ => {}
}
}
match &app.view_mode {
ViewMode::Summary => handle_summary_keys(app, key, num_nodes),
ViewMode::Detail(_) => handle_detail_keys(app, key, num_nodes),
ViewMode::Split(_) => handle_split_keys(app, key, num_nodes),
ViewMode::Diff(_, _) => handle_diff_keys(app, key),
}
}
fn handle_summary_keys(app: &mut TuiApp, key: KeyEvent, num_nodes: usize) {
match key.code {
KeyCode::Char(c @ '1'..='9') => {
let idx = (c as u8 - b'1') as usize;
if idx < num_nodes {
app.show_detail(idx, num_nodes);
}
}
KeyCode::Char('s') => {
if num_nodes >= 2 {
let indices: Vec<usize> = (0..num_nodes.min(4)).collect();
app.show_split(indices, num_nodes);
}
}
KeyCode::Char('d') => {
if num_nodes >= 2 {
app.show_diff(0, 1, num_nodes);
}
}
_ => {}
}
}
fn handle_detail_keys(app: &mut TuiApp, key: KeyEvent, num_nodes: usize) {
match key.code {
KeyCode::Up => {
app.scroll_up(1);
}
KeyCode::Down => {
app.scroll_down(1, usize::MAX); }
KeyCode::PageUp => {
app.scroll_up(10);
}
KeyCode::PageDown => {
app.scroll_down(10, usize::MAX);
}
KeyCode::Home => {
if let ViewMode::Detail(idx) = app.view_mode {
app.set_scroll(idx, 0);
app.follow_mode = false;
}
}
KeyCode::End => {
if let ViewMode::Detail(idx) = app.view_mode {
app.set_scroll(idx, usize::MAX);
app.follow_mode = true; }
}
KeyCode::Left => {
app.prev_node(num_nodes);
}
KeyCode::Right => {
app.next_node(num_nodes);
}
KeyCode::Char('f') => {
app.toggle_follow();
}
KeyCode::Char(c @ '1'..='9') => {
let idx = (c as u8 - b'1') as usize;
if idx < num_nodes {
app.show_detail(idx, num_nodes);
}
}
_ => {}
}
}
fn handle_split_keys(app: &mut TuiApp, key: KeyEvent, num_nodes: usize) {
if let KeyCode::Char(c @ '1'..='9') = key.code {
let idx = (c as u8 - b'1') as usize;
if idx < num_nodes {
app.show_detail(idx, num_nodes);
}
}
}
fn handle_diff_keys(_app: &mut TuiApp, key: KeyEvent) {
match key.code {
KeyCode::Up => {
}
KeyCode::Down => {
}
_ => {}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_quit_keys() {
let mut app = TuiApp::new();
let key = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE);
handle_key_event(&mut app, key, 5);
assert!(app.should_quit);
app.should_quit = false;
let key = KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL);
handle_key_event(&mut app, key, 5);
assert!(app.should_quit);
}
#[test]
fn test_summary_navigation() {
let mut app = TuiApp::new();
let key = KeyEvent::new(KeyCode::Char('2'), KeyModifiers::NONE);
handle_key_event(&mut app, key, 5);
assert_eq!(app.view_mode, ViewMode::Detail(1));
app.show_summary();
let key = KeyEvent::new(KeyCode::Char('s'), KeyModifiers::NONE);
handle_key_event(&mut app, key, 5);
assert!(matches!(app.view_mode, ViewMode::Split(_)));
}
#[test]
fn test_detail_scrolling() {
let mut app = TuiApp::new();
app.show_detail(0, 5);
app.set_scroll(0, 10);
let key = KeyEvent::new(KeyCode::Up, KeyModifiers::NONE);
handle_key_event(&mut app, key, 5);
assert_eq!(app.get_scroll(0), 9);
let key = KeyEvent::new(KeyCode::Down, KeyModifiers::NONE);
handle_key_event(&mut app, key, 5);
assert_eq!(app.get_scroll(0), 10);
}
#[test]
fn test_detail_node_switching() {
let mut app = TuiApp::new();
app.show_detail(1, 5);
let key = KeyEvent::new(KeyCode::Right, KeyModifiers::NONE);
handle_key_event(&mut app, key, 5);
assert_eq!(app.view_mode, ViewMode::Detail(2));
let key = KeyEvent::new(KeyCode::Left, KeyModifiers::NONE);
handle_key_event(&mut app, key, 5);
assert_eq!(app.view_mode, ViewMode::Detail(1));
}
#[test]
fn test_esc_to_summary() {
let mut app = TuiApp::new();
app.show_detail(0, 5);
let key = KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE);
handle_key_event(&mut app, key, 5);
assert_eq!(app.view_mode, ViewMode::Summary);
}
}