use std::time::Duration;
use anyhow::Result;
use crossterm::event::{
self, Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
};
use crate::app::{App, View};
pub fn poll_event(timeout: Duration) -> Result<Option<Event>> {
if event::poll(timeout)? {
Ok(Some(event::read()?))
} else {
Ok(None)
}
}
pub fn handle_key_event(app: &mut App, key: KeyEvent) {
if app.show_help {
app.show_help = false;
return;
}
if app.show_detail_overlay {
match key.code {
KeyCode::Esc | KeyCode::Enter | KeyCode::Backspace | KeyCode::Char('q') => {
app.close_overlay();
}
KeyCode::Up | KeyCode::Char('k') => app.select_prev(),
KeyCode::Down | KeyCode::Char('j') => app.select_next(),
KeyCode::PageUp => app.select_prev_n(10),
KeyCode::PageDown => app.select_next_n(10),
KeyCode::Home => app.select_first(),
KeyCode::End => app.select_last(),
_ => {}
}
return;
}
if app.filter_active {
handle_filter_input(app, key);
return;
}
match key.code {
KeyCode::Char('q') => app.quit(),
KeyCode::Tab => {
if key.modifiers.contains(KeyModifiers::SHIFT) {
app.prev_view();
} else {
app.next_view();
}
}
KeyCode::BackTab => app.prev_view(),
KeyCode::Char('1') => app.set_view(View::Summary),
KeyCode::Char('2') => app.set_view(View::Bottleneck),
KeyCode::Char('3') => app.set_view(View::DataFlow),
KeyCode::Up | KeyCode::Char('k') => app.select_prev(),
KeyCode::Down | KeyCode::Char('j') => app.select_next(),
KeyCode::Left | KeyCode::Char('h') => app.prev_view(),
KeyCode::Right | KeyCode::Char('l') => app.next_view(),
KeyCode::PageUp => app.select_prev_n(10),
KeyCode::PageDown => app.select_next_n(10),
KeyCode::Home => app.select_first(),
KeyCode::End => app.select_last(),
KeyCode::Enter => app.enter_detail(),
KeyCode::Esc | KeyCode::Backspace => app.go_back(),
KeyCode::Char('r') => {
let _ = app.reload_data();
}
KeyCode::Char('?') => app.toggle_help(),
KeyCode::Char('s') => {
if app.current_view == View::Summary || app.current_view == View::Bottleneck {
app.cycle_sort();
}
}
KeyCode::Char('S') => {
if app.current_view == View::Summary || app.current_view == View::Bottleneck {
app.toggle_sort_direction();
}
}
KeyCode::Char('/') => app.start_filter(),
KeyCode::Char('c') => {
if !app.filter_text.is_empty() {
app.clear_filter();
}
}
KeyCode::Char('e') => {
let export_path = std::path::PathBuf::from("monitor_export.json");
match app.export_state(&export_path) {
Ok(()) => {
app.set_status_message(format!("Exported to {}", export_path.display()));
}
Err(e) => {
app.set_status_message(format!("Export failed: {}", e));
}
}
}
_ => {}
}
}
fn handle_filter_input(app: &mut App, key: KeyEvent) {
match key.code {
KeyCode::Enter => {
app.filter_active = false;
}
KeyCode::Esc => {
app.cancel_filter();
}
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
app.clear_filter();
}
KeyCode::Backspace => {
app.filter_pop();
if app.filter_text.is_empty() {
app.filter_active = false;
}
}
KeyCode::Char(c) => {
app.filter_push(c);
}
_ => {}
}
}
pub fn handle_mouse_event(app: &mut App, mouse: MouseEvent, content_start_row: u16) {
match mouse.kind {
MouseEventKind::ScrollUp => {
app.select_prev();
}
MouseEventKind::ScrollDown => {
app.select_next();
}
MouseEventKind::Down(MouseButton::Left) => {
let clicked_row = mouse.row;
if clicked_row > content_start_row {
let item_row = (clicked_row - content_start_row - 1) as usize;
match app.current_view {
View::Summary => {
if let Some(ref data) = app.data {
let filtered_count = data
.modules
.iter()
.filter(|m| app.matches_filter(&m.name))
.count();
if item_row < filtered_count {
app.selected_module_index = item_row;
}
}
}
View::Bottleneck => {
if let Some(ref data) = app.data {
let count = data.unhealthy_topics().len();
if item_row < count {
app.selected_topic_index = item_row;
}
}
}
View::DataFlow => {
if let Some(ref data) = app.data {
let graph = crate::data::DataFlowGraph::from_monitor_data(data);
if item_row < graph.topics.len() {
app.selected_topic_index = item_row;
}
}
}
}
}
if clicked_row == 1 {
let col = mouse.column;
if col < 13 {
app.set_view(View::Summary);
} else if col < 29 {
app.set_view(View::Bottleneck);
} else if col < 39 {
app.set_view(View::DataFlow);
}
}
}
MouseEventKind::Down(MouseButton::Right) => {
app.go_back();
}
_ => {}
}
}