use crossterm::event::{self, Event, KeyCode, KeyEventKind};
use std::io;
use std::process::Command;
use std::time::Duration;
use crate::tui::state::AppState;
pub async fn handle_input_events(state: &AppState) -> io::Result<bool> {
if event::poll(Duration::from_millis(
state.config.internal.refresh_interval,
))? && let Event::Key(key) = event::read()?
&& key.kind == KeyEventKind::Press
{
return handle_key_event(key.code, state).await;
}
Ok(false) }
fn keycode_to_string(key: KeyCode) -> String {
match key {
KeyCode::Char(c) => c.to_string(),
KeyCode::Enter => "Enter".to_string(),
KeyCode::Esc => "Esc".to_string(),
KeyCode::Up => "Up".to_string(),
KeyCode::Down => "Down".to_string(),
KeyCode::Left => "Left".to_string(),
KeyCode::Right => "Right".to_string(),
KeyCode::Tab => "Tab".to_string(),
KeyCode::Backspace => "Backspace".to_string(),
KeyCode::Delete => "Delete".to_string(),
KeyCode::Home => "Home".to_string(),
KeyCode::End => "End".to_string(),
KeyCode::PageUp => "PageUp".to_string(),
KeyCode::PageDown => "PageDown".to_string(),
_ => String::new(),
}
}
async fn handle_key_event(key_code: KeyCode, state: &AppState) -> io::Result<bool> {
let key_str = keycode_to_string(key_code);
if key_str.is_empty() {
return Ok(false);
}
let kb = &state.config.ui.keybindings;
if kb.matches("quit", &key_str) {
return Ok(true);
}
if kb.matches("cd", &key_str) {
return handle_cd_to_repo(state).await;
}
if kb.matches("back", &key_str) {
handle_escape(state).await;
} else if kb.matches("details", &key_str) {
handle_enter(state).await;
} else if kb.matches("move_down", &key_str) {
handle_move_down(state).await;
} else if kb.matches("move_up", &key_str) {
handle_move_up(state).await;
} else if kb.matches("open", &key_str) {
handle_open_in_file_manager(state).await;
}
Ok(false)
}
async fn handle_escape(state: &AppState) {
let is_detail = state.is_detail_view().await;
if is_detail {
state.set_detail_view(false).await;
}
}
async fn handle_enter(state: &AppState) {
let is_detail = state.is_detail_view().await;
if !is_detail && !state.is_repos_empty().await {
state.set_detail_view(true).await;
}
}
async fn handle_move_down(state: &AppState) {
let is_detail = state.is_detail_view().await;
if !is_detail {
state.move_selection_down().await;
}
}
async fn handle_move_up(state: &AppState) {
let is_detail = state.is_detail_view().await;
if !is_detail {
state.move_selection_up().await;
}
}
async fn handle_open_in_file_manager(state: &AppState) {
let is_detail = state.is_detail_view().await;
if !is_detail && let Some(path) = state.get_selected_repo_path().await {
#[cfg(target_os = "macos")]
let _ = Command::new("open").arg(&path).spawn();
#[cfg(target_os = "linux")]
let _ = Command::new("xdg-open").arg(&path).spawn();
#[cfg(target_os = "windows")]
let _ = Command::new("explorer").arg(&path).spawn();
}
}
async fn handle_cd_to_repo(state: &AppState) -> io::Result<bool> {
let is_detail = state.is_detail_view().await;
if !is_detail && let Some(path) = state.get_selected_repo_path().await {
if let Some(cwd_file) = &state.config.internal.cwd_file {
std::fs::write(cwd_file, path.to_string_lossy().as_bytes())?;
}
return Ok(true); }
Ok(false)
}