use crate::app::{App, ViewMode};
use crate::core::search::SearchType;
use crate::progress::ProgressStore;
use crossterm::event::{KeyCode, KeyEvent};
use ratatui_image::picker::Picker;
use std::error::Error;
pub enum Action {
None,
Exit,
SwitchBook(String),
}
pub fn handle_key_event(
app: &mut App,
key: KeyEvent,
picker: &mut Picker,
) -> Result<Action, Box<dyn Error>> {
match app.mode {
ViewMode::Library => match key.code {
KeyCode::Char('q') => return Ok(Action::Exit),
KeyCode::Esc | KeyCode::Char('l') => app.mode = ViewMode::Reading,
KeyCode::Enter => {
if let Some(path) = app.select_book() {
return Ok(Action::SwitchBook(path));
}
}
KeyCode::Down | KeyCode::Char('j') if !app.library_books.is_empty() => {
let i = app.library_state.selected().unwrap_or(0);
let new_index = (i + 1) % app.library_books.len();
app.library_state.select(Some(new_index));
app.update_library_cover(picker);
}
KeyCode::PageDown if !app.library_books.is_empty() => {
let i = app.library_state.selected().unwrap_or(0);
let new_index = (i + 5) % app.library_books.len();
app.library_state.select(Some(new_index));
app.update_library_cover(picker);
}
KeyCode::Up | KeyCode::Char('k') if !app.library_books.is_empty() => {
let i = app.library_state.selected().unwrap_or(0);
let new_index = (i + app.library_books.len() - 1) % app.library_books.len();
app.library_state.select(Some(new_index));
app.update_library_cover(picker);
}
KeyCode::PageUp if !app.library_books.is_empty() => {
let i = app.library_state.selected().unwrap_or(0);
let new_index = (i + app.library_books.len() - 5) % app.library_books.len();
app.library_state.select(Some(new_index));
app.update_library_cover(picker);
}
KeyCode::Char('d') => {
let i = app.library_state.selected().unwrap_or(0);
let mut store = ProgressStore::load(app.profile_dir.as_ref().map(|p| {
let mut p = p.clone();
p.push("progress.json");
p
}));
app.delete_book(i, &mut store);
if !app.library_books.is_empty() {
if i < app.library_books.len() {
app.library_state.select(Some(i));
app.update_library_cover(picker);
} else {
app.library_state.select(Some(0));
app.update_library_cover(picker);
}
}
}
KeyCode::Char('s') => app.next_sort_criteria(picker),
KeyCode::Char('S') => app.toggle_sort(picker),
_ => {}
},
ViewMode::BookmarkBrowser => match key.code {
KeyCode::Char('q') => return Ok(Action::Exit),
KeyCode::Esc | KeyCode::Char('b') => app.mode = ViewMode::Reading,
KeyCode::Enter => app.jump_to_bookmark(),
KeyCode::Down | KeyCode::Char('j') if !app.bookmarks.is_empty() => {
let i = app.bookmark_list_state.selected().unwrap_or(0);
let new_index = (i + 1) % app.bookmarks.len();
app.bookmark_list_state.select(Some(new_index));
}
KeyCode::Down | KeyCode::Char('j') => {}
KeyCode::Up | KeyCode::Char('k') if !app.bookmarks.is_empty() => {
let i = app.bookmark_list_state.selected().unwrap_or(0);
let new_index = (i + app.bookmarks.len() - 1) % app.bookmarks.len();
app.bookmark_list_state.select(Some(new_index));
}
KeyCode::Up | KeyCode::Char('k') => {}
KeyCode::Char('d') => app.remove_bookmark(),
KeyCode::Char('r') => app.start_renaming_bookmark(),
_ => {}
},
ViewMode::BookmarkRenaming => match key.code {
KeyCode::Enter => app.finish_renaming_bookmark(),
KeyCode::Esc => app.mode = ViewMode::BookmarkBrowser,
KeyCode::Char(c) => {
app.bookmark_rename_input
.insert(app.bookmark_rename_cursor_position, c);
app.bookmark_rename_cursor_position += 1;
}
KeyCode::Backspace if app.bookmark_rename_cursor_position > 0 => {
app.bookmark_rename_input
.remove(app.bookmark_rename_cursor_position - 1);
app.bookmark_rename_cursor_position -= 1;
}
KeyCode::Delete
if app.bookmark_rename_cursor_position < app.bookmark_rename_input.len() =>
{
app.bookmark_rename_input
.remove(app.bookmark_rename_cursor_position);
}
KeyCode::Left => {
app.bookmark_rename_cursor_position =
app.bookmark_rename_cursor_position.saturating_sub(1);
}
KeyCode::Right => {
app.bookmark_rename_cursor_position =
(app.bookmark_rename_cursor_position + 1).min(app.bookmark_rename_input.len());
}
KeyCode::Home => app.bookmark_rename_cursor_position = 0,
KeyCode::End => app.bookmark_rename_cursor_position = app.bookmark_rename_input.len(),
_ => {}
},
ViewMode::ChapterBrowser => match key.code {
KeyCode::Char('q') => return Ok(Action::Exit),
KeyCode::Esc | KeyCode::Tab => app.mode = ViewMode::Reading,
KeyCode::Enter => {
if let Some(selected) = app.chapter_list_state.selected() {
let visible_indices = app.get_visible_chapter_indices();
if let Some(&real_index) = visible_indices.get(selected) {
app.jump_to_chapter(real_index);
}
}
}
KeyCode::Down | KeyCode::Char('j') => {
let i = app.chapter_list_state.selected().unwrap_or(0);
let visible_indices = app.get_visible_chapter_indices();
if i + 1 < visible_indices.len() {
app.chapter_list_state.select(Some(i + 1));
}
}
KeyCode::PageDown => {
let i = app.chapter_list_state.selected().unwrap_or(0);
let visible_indices = app.get_visible_chapter_indices();
if i + 5 < visible_indices.len() {
app.chapter_list_state.select(Some(i + 5));
} else {
app.chapter_list_state
.select(Some(visible_indices.len().saturating_sub(1)));
}
}
KeyCode::Up | KeyCode::Char('k') => {
let i = app.chapter_list_state.selected().unwrap_or(0);
if i > 0 {
app.chapter_list_state.select(Some(i - 1));
}
}
KeyCode::PageUp => {
let i = app.chapter_list_state.selected().unwrap_or(0);
if i > 5 {
app.chapter_list_state.select(Some(i - 5));
} else {
app.chapter_list_state.select(Some(0));
}
}
KeyCode::Right | KeyCode::Char('l') => {
app.expand_chapter();
}
KeyCode::Left | KeyCode::Char('h') => {
app.collapse_chapter();
}
_ => {}
},
ViewMode::Search => match key.code {
KeyCode::Esc => app.mode = ViewMode::Reading,
KeyCode::Char('c') if key.modifiers.contains(crossterm::event::KeyModifiers::ALT) => {
app.search_case_sensitive = !app.search_case_sensitive;
app.perform_search();
}
KeyCode::Tab => {
app.search_type = match app.search_type {
SearchType::Local => SearchType::Global,
SearchType::Global => SearchType::Bookmark,
SearchType::Bookmark => SearchType::Chapter,
SearchType::Chapter => SearchType::Local,
};
app.perform_search();
}
KeyCode::BackTab => {
app.search_type = match app.search_type {
SearchType::Local => SearchType::Chapter,
SearchType::Global => SearchType::Local,
SearchType::Bookmark => SearchType::Global,
SearchType::Chapter => SearchType::Bookmark,
};
app.perform_search();
}
KeyCode::Enter => {
if app.search_results.is_empty() {
app.perform_search();
} else {
app.jump_to_search_result();
}
}
KeyCode::Down | KeyCode::Char('j') if !app.search_results.is_empty() => {
let i = app.search_list_state.selected().unwrap_or(0);
let new_index = (i + 1) % app.search_results.len();
app.search_list_state.select(Some(new_index));
}
KeyCode::Up | KeyCode::Char('k') if !app.search_results.is_empty() => {
let i = app.search_list_state.selected().unwrap_or(0);
let new_index = (i + app.search_results.len() - 1) % app.search_results.len();
app.search_list_state.select(Some(new_index));
}
KeyCode::Char(c) => {
app.search_query.insert(app.search_cursor_position, c);
app.search_cursor_position += 1;
app.perform_search();
}
KeyCode::Backspace if app.search_cursor_position > 0 => {
app.search_query.remove(app.search_cursor_position - 1);
app.search_cursor_position -= 1;
app.perform_search();
}
KeyCode::Delete if app.search_cursor_position < app.search_query.len() => {
app.search_query.remove(app.search_cursor_position);
app.perform_search();
}
KeyCode::Left => {
app.search_cursor_position = app.search_cursor_position.saturating_sub(1);
}
KeyCode::Right => {
app.search_cursor_position =
(app.search_cursor_position + 1).min(app.search_query.len());
}
KeyCode::Home => app.search_cursor_position = 0,
KeyCode::End => app.search_cursor_position = app.search_query.len(),
_ => {}
},
ViewMode::Visual => match key.code {
KeyCode::Esc | KeyCode::Char('q') => app.mode = ViewMode::Reading,
KeyCode::Char('v') | KeyCode::Char(' ') => app.set_visual_anchor(),
KeyCode::Char('m') | KeyCode::Enter => app.add_bookmark(),
KeyCode::Left | KeyCode::Char('h') => app.move_visual_cursor(0, -1),
KeyCode::Right | KeyCode::Char('l') => app.move_visual_cursor(0, 1),
KeyCode::Up | KeyCode::Char('k') => app.move_visual_cursor(-1, 0),
KeyCode::Down | KeyCode::Char('j') => app.move_visual_cursor(1, 0),
KeyCode::Char('w') => app.move_visual_word(true),
KeyCode::Char('b') => app.move_visual_word(false),
KeyCode::PageUp => app.move_visual_cursor(-5, 0),
KeyCode::PageDown => app.move_visual_cursor(5, 0),
_ => {}
},
_ => match key.code {
KeyCode::Char('q') => return Ok(Action::Exit),
KeyCode::Char('/') => app.open_search(),
KeyCode::Char('v') => app.enter_visual_mode(),
KeyCode::Right => {
if app.config.view_type == "book" {
app.next_page(2);
} else {
app.next_chapter();
}
}
KeyCode::Left => {
if app.config.view_type == "book" {
app.prev_page(2);
} else {
app.previous_chapter();
}
}
KeyCode::Home => app.go_to_start(),
KeyCode::End => app.go_to_end(),
KeyCode::Down | KeyCode::Char('j') => {
if app.config.view_type != "book" {
app.scroll_down(1);
}
if app.config.view_type == "book" {
app.next_page(1);
}
}
KeyCode::PageDown => app.next_page(2),
KeyCode::Up | KeyCode::Char('k') => {
if app.config.view_type != "book" {
app.scroll_up(1);
}
if app.config.view_type == "book" {
app.prev_page(1);
}
}
KeyCode::PageUp => app.prev_page(1),
KeyCode::Char('c') => app.toggle_mode(),
KeyCode::Char('V') => {
app.config.view_type = match app.config.view_type.as_str() {
"default" => "book".to_string(),
"book" => "continuous".to_string(),
_ => "default".to_string(),
};
}
KeyCode::Char('l') => app.open_library(picker),
KeyCode::Char('b') => app.open_bookmark_browser(),
KeyCode::Char('m') => app.add_bookmark(),
KeyCode::Tab => app.open_chapter_browser(),
KeyCode::Char('n') => app.next_search_result(),
KeyCode::Char('N') => app.prev_search_result(),
_ => {}
},
}
Ok(Action::None)
}