feat: delete + styling
This commit is contained in:
@@ -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),
|
||||||
|
|||||||
@@ -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,9 +116,21 @@ pub fn handle_user_input(app: &mut App) -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
// Movement
|
// Movement
|
||||||
if c == 'j' {
|
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.list_state.select_next();
|
||||||
app.description_scroll = 0;
|
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();
|
||||||
app.description_scroll = 0;
|
app.description_scroll = 0;
|
||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user