feat: word list cache + word filtering
This commit is contained in:
@@ -1,16 +1,16 @@
|
|||||||
use crate::modules::source::{FileDatabase, WordSource};
|
use crate::modules::source::{FileDatabase, Word, WordSource};
|
||||||
use crossterm::event::{
|
use crossterm::event::{
|
||||||
self,
|
self,
|
||||||
Event::{self, Key},
|
Event::{self},
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
DefaultTerminal, Frame,
|
DefaultTerminal, Frame,
|
||||||
layout::{Constraint, Direction, Flex, Layout},
|
layout::{Constraint, Direction, Flex, Layout},
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
text::Text,
|
text::Text,
|
||||||
widgets::{Block, Borders, Padding, Paragraph},
|
widgets::{Block, Borders, Paragraph},
|
||||||
};
|
};
|
||||||
use std::{io, ops::Index};
|
use std::io;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum UiState {
|
pub enum UiState {
|
||||||
@@ -26,6 +26,7 @@ pub struct App {
|
|||||||
state: UiState,
|
state: UiState,
|
||||||
file_database: FileDatabase,
|
file_database: FileDatabase,
|
||||||
scroll_position: usize,
|
scroll_position: usize,
|
||||||
|
word_list: Vec<Word>,
|
||||||
word_filter: Option<String>,
|
word_filter: Option<String>,
|
||||||
lang_filter: Option<String>,
|
lang_filter: Option<String>,
|
||||||
exit: bool,
|
exit: bool,
|
||||||
@@ -33,10 +34,13 @@ pub struct App {
|
|||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(db: FileDatabase) -> Self {
|
pub fn new(db: FileDatabase) -> Self {
|
||||||
|
let wl = db.get_all_words();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
state: UiState::LIST,
|
state: UiState::LIST,
|
||||||
file_database: db,
|
file_database: db,
|
||||||
scroll_position: 0,
|
scroll_position: 0,
|
||||||
|
word_list: wl,
|
||||||
word_filter: None,
|
word_filter: None,
|
||||||
lang_filter: None,
|
lang_filter: None,
|
||||||
exit: false,
|
exit: false,
|
||||||
@@ -105,25 +109,13 @@ impl App {
|
|||||||
filters_layout[1],
|
filters_layout[1],
|
||||||
);
|
);
|
||||||
|
|
||||||
// FIXME: this is wrong, the list must reside in memory and only be updated when required,
|
|
||||||
// this makes a new call every frame, insane
|
|
||||||
let word_list = {
|
|
||||||
if let Some(w_filter) = &self.word_filter
|
|
||||||
&& let Some(l_filter) = &self.lang_filter
|
|
||||||
{
|
|
||||||
self.file_database
|
|
||||||
.find_by_word_and_lang(&word_filter, &l_filter)
|
|
||||||
} else if let Some(l_filter) = &self.lang_filter {
|
|
||||||
self.file_database.find_by_lang(&l_filter)
|
|
||||||
} else {
|
|
||||||
self.file_database.get_all_words()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new("Word list").block(Block::new().borders(Borders::ALL).title("Entries")),
|
Paragraph::new("Word list").block(Block::new().borders(Borders::ALL).title("Entries")),
|
||||||
words_layout[0],
|
words_layout[0],
|
||||||
);
|
);
|
||||||
let selected_word = word_list.into_iter().nth(self.scroll_position);
|
|
||||||
|
// PERF: do not clone
|
||||||
|
let selected_word = self.word_list.clone().into_iter().nth(self.scroll_position);
|
||||||
if let Some(w) = selected_word {
|
if let Some(w) = selected_word {
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
Paragraph::new(w.description).block(Block::new().borders(Borders::ALL)),
|
Paragraph::new(w.description).block(Block::new().borders(Borders::ALL)),
|
||||||
@@ -141,6 +133,25 @@ impl App {
|
|||||||
frame.render_widget(help_text, help_area);
|
frame.render_widget(help_text, help_area);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_word_list(&mut self) {
|
||||||
|
let word_list = {
|
||||||
|
if let Some(w_filter) = &self.word_filter
|
||||||
|
&& let Some(l_filter) = &self.lang_filter
|
||||||
|
{
|
||||||
|
self.file_database
|
||||||
|
.find_by_word_and_lang(&w_filter, &l_filter)
|
||||||
|
} else if let Some(w_filter) = &self.word_filter {
|
||||||
|
self.file_database.find_by_word(&w_filter)
|
||||||
|
} else if let Some(l_filter) = &self.lang_filter {
|
||||||
|
self.file_database.find_by_lang(&l_filter)
|
||||||
|
} else {
|
||||||
|
self.file_database.get_all_words()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.scroll_position = 0;
|
||||||
|
self.word_list = word_list;
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_events(&mut self) -> io::Result<()> {
|
fn handle_events(&mut self) -> io::Result<()> {
|
||||||
if let Event::Key(key) = event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
match key.code {
|
match key.code {
|
||||||
@@ -153,6 +164,7 @@ impl App {
|
|||||||
} else {
|
} else {
|
||||||
self.word_filter = Some(c.to_string());
|
self.word_filter = Some(c.to_string());
|
||||||
}
|
}
|
||||||
|
self.update_word_list();
|
||||||
}
|
}
|
||||||
UiState::FILTERLANG => {
|
UiState::FILTERLANG => {
|
||||||
if let Some(mut prev_lang) = self.lang_filter.clone() {
|
if let Some(mut prev_lang) = self.lang_filter.clone() {
|
||||||
@@ -161,6 +173,7 @@ impl App {
|
|||||||
} else {
|
} else {
|
||||||
self.lang_filter = Some(c.to_string());
|
self.lang_filter = Some(c.to_string());
|
||||||
}
|
}
|
||||||
|
self.update_word_list();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Quit
|
// Quit
|
||||||
@@ -185,14 +198,34 @@ impl App {
|
|||||||
}
|
}
|
||||||
// Movement
|
// Movement
|
||||||
if c == 'j' {
|
if c == 'j' {
|
||||||
|
if self.scroll_position > 0 {
|
||||||
self.scroll_position -= 1;
|
self.scroll_position -= 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if c == 'k' {
|
if c == 'k' {
|
||||||
|
if self.scroll_position < self.word_list.len() - 1 {
|
||||||
self.scroll_position += 1;
|
self.scroll_position += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
event::KeyCode::Backspace => match self.state {
|
||||||
|
UiState::FILTERWORD => {
|
||||||
|
if let Some(w) = &self.word_filter {
|
||||||
|
if w.len() <= 1 {
|
||||||
|
self.word_filter = None;
|
||||||
|
} else {
|
||||||
|
let mut new_w = w.clone();
|
||||||
|
new_w.pop();
|
||||||
|
self.word_filter = Some(new_w);
|
||||||
|
}
|
||||||
|
self.update_word_list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiState::FILTERLANG => {}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
event::KeyCode::Esc => {
|
event::KeyCode::Esc => {
|
||||||
self.state = UiState::LIST;
|
self.state = UiState::LIST;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user