From 7a8a1cb340cc1e388d8394ce1e0ed7f166720f02 Mon Sep 17 00:00:00 2001 From: Daniel Heras Quesada Date: Thu, 6 Aug 2026 18:15:24 +0200 Subject: [PATCH] feat: delete + styling --- src/modules/ui/app.rs | 51 +++++++++++++++++++++++++++++---------- src/modules/ui/handler.rs | 42 ++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/modules/ui/app.rs b/src/modules/ui/app.rs index 2209567..dbc3499 100644 --- a/src/modules/ui/app.rs +++ b/src/modules/ui/app.rs @@ -1,6 +1,7 @@ use crate::modules::source::{FileDatabase, Word, WordSource}; use crate::modules::ui::app::UiState::FILTERLANG; use crate::modules::ui::handler::handle_user_input; +use crossterm::event::KeyEvent; use ratatui::{ DefaultTerminal, Frame, layout::{Constraint, Direction, Flex, Layout}, @@ -42,6 +43,7 @@ pub struct App { pub word_to_edit: Option, pub word_filter: Option, pub lang_filter: Option, + pub last_key: Option, pub exit: bool, } @@ -60,6 +62,7 @@ impl App { word_to_edit: None, word_filter: None, lang_filter: None, + last_key: None, exit: false, } } @@ -131,6 +134,15 @@ impl App { /* * Word list / add / edit block * */ + let scroll_position: usize = { + if let Some(i) = self.list_state.selected() { + i + } else { + 0 + } + }; + // PERF: do not clone + let selected_word = self.word_list.clone().into_iter().nth(scroll_position); if self.state == UiState::ADD { let add_words_heading_layout = Layout::horizontal(vec![Constraint::Fill(1), Constraint::Length(8)]) @@ -171,11 +183,23 @@ impl App { let highligh_symbol: &str = { if self.state == UiState::LIST { "> " + } else if self.state == UiState::DELETE { + "x " } else { " " } }; + let highligh_color: Color = { + if self.state == UiState::LIST { + Color::Yellow + } else if self.state == UiState::DELETE { + Color::LightRed + } else { + Color::default() + } + }; + let words_layout = Layout::horizontal(vec![Constraint::Percentage(30), Constraint::Percentage(70)]) .split(main_layout[1]); @@ -194,21 +218,12 @@ impl App { .borders(Borders::ALL) .padding(Padding::horizontal(1)), ) - .highlight_style(Style::default().fg(Color::default())) + .highlight_style(Style::default().fg(highligh_color)) .highlight_symbol(highligh_symbol) .style(Style::default().fg(Color::DarkGray)); frame.render_stateful_widget(word_key_list, words_layout[0], &mut self.list_state); - let scroll_position: usize = { - if let Some(i) = self.list_state.selected() { - i - } else { - 0 - } - }; - // PERF: do not clone - let selected_word = self.word_list.clone().into_iter().nth(scroll_position); - if let Some(w) = selected_word { + if let Some(w) = selected_word.clone() { frame.render_widget( Paragraph::new(w.description) .wrap(Wrap { trim: true }) @@ -226,8 +241,18 @@ impl App { .flex(Flex::Center) .split(main_layout[2]); - let help_text = Text::raw("(f)ilter | (l)ang | (d)elete | (e)dit | (a)dd | (q)uit ") - .style(Style::new().fg(Color::DarkGray)); + let help_text = { + if self.state == UiState::DELETE + && let Some(s_w) = selected_word.clone() + { + Text::raw(format!("Sure you want to delete '{0}'? (y)es", s_w.key)) + .style(Style::new().fg(Color::LightRed)) + } else { + Text::raw("(f)ilter | (l)ang | (d)elete | (e)dit | (a)dd | (q)uit ") + .style(Style::new().fg(Color::DarkGray)) + } + }; + let help_area = help_layout[0].centered( Constraint::Length(help_text.width() as u16), Constraint::Length(1), diff --git a/src/modules/ui/handler.rs b/src/modules/ui/handler.rs index afc4c27..3db5029 100644 --- a/src/modules/ui/handler.rs +++ b/src/modules/ui/handler.rs @@ -12,6 +12,31 @@ pub fn handle_user_input(app: &mut App) -> io::Result<()> { 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); @@ -91,8 +116,20 @@ pub fn handle_user_input(app: &mut App) -> io::Result<()> { } // Movement if c == 'j' { - app.list_state.select_next(); - app.description_scroll = 0; + 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(); @@ -171,6 +208,7 @@ pub fn handle_user_input(app: &mut App) -> io::Result<()> { } _ => return Ok(()), } + app.last_key = Some(key); } Ok(()) }