feat: delete + styling

This commit is contained in:
2026-08-06 18:15:24 +02:00
parent 2526179d0b
commit 7a8a1cb340
2 changed files with 78 additions and 15 deletions

View File

@@ -1,6 +1,7 @@
use crate::modules::source::{FileDatabase, Word, WordSource}; use crate::modules::source::{FileDatabase, Word, WordSource};
use crate::modules::ui::app::UiState::FILTERLANG; use crate::modules::ui::app::UiState::FILTERLANG;
use crate::modules::ui::handler::handle_user_input; use crate::modules::ui::handler::handle_user_input;
use crossterm::event::KeyEvent;
use ratatui::{ use ratatui::{
DefaultTerminal, Frame, DefaultTerminal, Frame,
layout::{Constraint, Direction, Flex, Layout}, layout::{Constraint, Direction, Flex, Layout},
@@ -42,6 +43,7 @@ pub struct App {
pub word_to_edit: Option<Word>, pub word_to_edit: Option<Word>,
pub word_filter: Option<String>, pub word_filter: Option<String>,
pub lang_filter: Option<String>, pub lang_filter: Option<String>,
pub last_key: Option<KeyEvent>,
pub exit: bool, pub exit: bool,
} }
@@ -60,6 +62,7 @@ impl App {
word_to_edit: None, word_to_edit: None,
word_filter: None, word_filter: None,
lang_filter: None, lang_filter: None,
last_key: None,
exit: false, exit: false,
} }
} }
@@ -131,6 +134,15 @@ impl App {
/* /*
* Word list / add / edit block * 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 { if self.state == UiState::ADD {
let add_words_heading_layout = let add_words_heading_layout =
Layout::horizontal(vec![Constraint::Fill(1), Constraint::Length(8)]) Layout::horizontal(vec![Constraint::Fill(1), Constraint::Length(8)])
@@ -171,11 +183,23 @@ impl App {
let highligh_symbol: &str = { let highligh_symbol: &str = {
if self.state == UiState::LIST { if self.state == UiState::LIST {
"> " "> "
} else if self.state == UiState::DELETE {
"x "
} else { } 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 = let words_layout =
Layout::horizontal(vec![Constraint::Percentage(30), Constraint::Percentage(70)]) Layout::horizontal(vec![Constraint::Percentage(30), Constraint::Percentage(70)])
.split(main_layout[1]); .split(main_layout[1]);
@@ -194,21 +218,12 @@ impl App {
.borders(Borders::ALL) .borders(Borders::ALL)
.padding(Padding::horizontal(1)), .padding(Padding::horizontal(1)),
) )
.highlight_style(Style::default().fg(Color::default())) .highlight_style(Style::default().fg(highligh_color))
.highlight_symbol(highligh_symbol) .highlight_symbol(highligh_symbol)
.style(Style::default().fg(Color::DarkGray)); .style(Style::default().fg(Color::DarkGray));
frame.render_stateful_widget(word_key_list, words_layout[0], &mut self.list_state); frame.render_stateful_widget(word_key_list, words_layout[0], &mut self.list_state);
let scroll_position: usize = { if let Some(w) = selected_word.clone() {
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 {
frame.render_widget( frame.render_widget(
Paragraph::new(w.description) Paragraph::new(w.description)
.wrap(Wrap { trim: true }) .wrap(Wrap { trim: true })
@@ -226,8 +241,18 @@ impl App {
.flex(Flex::Center) .flex(Flex::Center)
.split(main_layout[2]); .split(main_layout[2]);
let help_text = Text::raw("(f)ilter | (l)ang | (d)elete | (e)dit | (a)dd | (q)uit ") let help_text = {
.style(Style::new().fg(Color::DarkGray)); 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( let help_area = help_layout[0].centered(
Constraint::Length(help_text.width() as u16), Constraint::Length(help_text.width() as u16),
Constraint::Length(1), Constraint::Length(1),

View File

@@ -12,6 +12,31 @@ pub fn handle_user_input(app: &mut App) -> io::Result<()> {
match key.code { match key.code {
event::KeyCode::Char(c) => { event::KeyCode::Char(c) => {
match app.state { 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 => { UiState::FILTERWORD => {
if let Some(mut prev_word) = app.word_filter.clone() { if let Some(mut prev_word) = app.word_filter.clone() {
prev_word.push(c); prev_word.push(c);
@@ -91,8 +116,20 @@ pub fn handle_user_input(app: &mut App) -> io::Result<()> {
} }
// Movement // Movement
if c == 'j' { if c == 'j' {
app.list_state.select_next(); let scroll_position: usize = {
app.description_scroll = 0; 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' { if c == 'k' {
app.list_state.select_previous(); app.list_state.select_previous();
@@ -171,6 +208,7 @@ pub fn handle_user_input(app: &mut App) -> io::Result<()> {
} }
_ => return Ok(()), _ => return Ok(()),
} }
app.last_key = Some(key);
} }
Ok(()) Ok(())
} }