use std::io; use crossterm::event::{self, Event}; use crate::modules::{ source::{Word, WordSource}, ui::app::{AddState, App, UiState}, }; pub fn handle_user_input(app: &mut App) -> io::Result<()> { if let Event::Key(key) = event::read()? { match key.code { event::KeyCode::Char(c) => { match app.state { UiState::DELETE => { if c == 'y' || c == 'Y' || c == 's' || c == 'S' { // delete word let scroll_position: usize = { if let Some(i) = app.list_state.selected() { i } else { 0 } }; let selected_word = app.word_list.clone().into_iter().nth(scroll_position); if let Some(w) = selected_word { // TODO: manage result with some notification context let _ = app.file_database.remove_word(&w.key); app.update_word_list(); if scroll_position > 0 { app.list_state.select(Some(scroll_position - 1)); } app.state = UiState::LIST; } } else { app.state = UiState::LIST; } } UiState::FILTERWORD => { if let Some(mut prev_word) = app.word_filter.clone() { prev_word.push(c); app.word_filter = Some(prev_word); } else { app.word_filter = Some(c.to_string()); } app.update_word_list(); } UiState::FILTERLANG => { if let Some(mut prev_lang) = app.lang_filter.clone() { prev_lang.push(c); app.lang_filter = Some(prev_lang); } else { app.lang_filter = Some(c.to_string()); } app.update_word_list(); } UiState::ADD => match app.add_state { AddState::KEY => { if let Some(mut prev_word) = app.word_to_add.clone() { prev_word.key.push(c); app.word_to_add = Some(prev_word); } else { app.word_to_add = Some(Word { key: c.to_string(), description: "".to_string(), lang: "".to_string(), }); } } AddState::DESCRIPTION => { if let Some(mut prev_word) = app.word_to_add.clone() { prev_word.description.push(c); app.word_to_add = Some(prev_word); } else { app.word_to_add = Some(Word { key: c.to_string(), description: "".to_string(), lang: "".to_string(), }); } } AddState::LANG => { if let Some(mut prev_word) = app.word_to_add.clone() { prev_word.lang.push(c); app.word_to_add = Some(prev_word); } else { app.word_to_add = Some(Word { key: c.to_string(), description: "".to_string(), lang: "".to_string(), }); } } }, _ => { // Quit if c == 'q' || c == 'x' { app.exit = true; } // State change if c == 'f' || c == '/' { app.state = UiState::FILTERWORD; } if c == 'l' { app.state = UiState::FILTERLANG; } if c == 'd' { app.state = UiState::DELETE; } if c == 'e' { app.state = UiState::EDIT; } if c == 'a' { app.state = UiState::ADD; } // Movement if c == 'j' { let scroll_position: usize = { if let Some(i) = app.list_state.selected() { i } else { 0 } }; if scroll_position < app.word_list.len() - 1 { app.list_state.select_next(); app.description_scroll = 0; } } if c == 'G' { app.list_state.select_last(); } if c == 'k' { app.list_state.select_previous(); app.description_scroll = 0; } if c == 'J' { app.description_scroll += 1; } if c == 'K' { if app.description_scroll > 0 { app.description_scroll -= 1; } } } } } event::KeyCode::Enter => match app.state { UiState::ADD => { if let Some(w) = app.word_to_add.clone() && w.key.len() > 0 { let res = app.file_database.insert_word(w); match res { Ok(_) => { app.update_word_list(); app.word_to_add = None; app.state = get_next_ui_state(app.state); } Err(_) => app.state = get_next_ui_state(app.state), } } } _ => {} }, event::KeyCode::Tab => match app.state { UiState::ADD => { app.add_state = get_next_add_state(app.add_state); } _ => { app.state = get_next_ui_state(app.state); } }, event::KeyCode::Backspace => match app.state { UiState::FILTERWORD => { app.word_filter = remove_last_char(&app.word_filter); app.update_word_list(); } UiState::FILTERLANG => { app.lang_filter = remove_last_char(&app.lang_filter); app.update_word_list(); } UiState::ADD => match app.add_state { AddState::KEY => { if let Some(mut prev_word) = app.word_to_add.clone() { prev_word.key.pop(); app.word_to_add = Some(prev_word); } } AddState::DESCRIPTION => { if let Some(mut prev_word) = app.word_to_add.clone() { prev_word.description.pop(); app.word_to_add = Some(prev_word); } } AddState::LANG => { if let Some(mut prev_word) = app.word_to_add.clone() { prev_word.lang.pop(); app.word_to_add = Some(prev_word); } } }, _ => {} }, event::KeyCode::Esc => { app.state = UiState::LIST; } _ => return Ok(()), } app.last_key = Some(key); } Ok(()) } fn remove_last_char(input: &Option) -> Option { if let Some(l) = input { if l.len() <= 1 { None } else { let mut new_l = l.clone(); new_l.pop(); Some(new_l) } } else { None } } fn get_next_add_state(current: AddState) -> AddState { if current == AddState::KEY { AddState::LANG } else if current == AddState::LANG { AddState::DESCRIPTION } else { AddState::KEY } } fn get_next_ui_state(current: UiState) -> UiState { match current { UiState::LIST => UiState::FILTERWORD, UiState::FILTERWORD => UiState::FILTERLANG, UiState::FILTERLANG => UiState::LIST, _ => UiState::LIST, } }