tui
This commit is contained in:
184
src/app.rs
Normal file
184
src/app.rs
Normal file
@ -0,0 +1,184 @@
|
||||
use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2};
|
||||
use ratatui::widgets::ListState;
|
||||
use ratatui_image::{picker::Picker, protocol};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Mode {
|
||||
Normal,
|
||||
Command,
|
||||
Search,
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
pub mode: Mode,
|
||||
pub all_items: Vec<String>,
|
||||
pub filtered_items: Vec<String>,
|
||||
pub list_state: ListState,
|
||||
pub input_buffer: String,
|
||||
pub should_quit: bool,
|
||||
pub undo_stack: Vec<(usize, String)>,
|
||||
pub current_image: Option<protocol::StatefulProtocol>,
|
||||
pub last_selected_index: Option<usize>,
|
||||
pub picker: Picker,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new() -> Self {
|
||||
let mut list_state = ListState::default();
|
||||
list_state.select(Some(0));
|
||||
let items = vec![
|
||||
"Ceci est un texte copié.".to_string(),
|
||||
"https://github.com/ratatui-org/ratatui".to_string(),
|
||||
"30426b4d-26e0-45af-9fa4-25f4476387a8.jpg".to_string(),
|
||||
"35789d6a-dea4-46de-90da-aee693a16031.jpg".to_string(),
|
||||
"fn main() {\n println!(\"Hello\");\n}".to_string(),
|
||||
];
|
||||
|
||||
let picker = Picker::from_query_stdio().unwrap_or_else(|_| Picker::halfblocks());
|
||||
|
||||
let mut app = Self {
|
||||
mode: Mode::Normal,
|
||||
filtered_items: items.clone(),
|
||||
all_items: items,
|
||||
list_state,
|
||||
input_buffer: String::new(),
|
||||
should_quit: false,
|
||||
undo_stack: Vec::new(),
|
||||
current_image: None,
|
||||
last_selected_index: None,
|
||||
picker,
|
||||
};
|
||||
|
||||
app.update_preview();
|
||||
app
|
||||
}
|
||||
|
||||
pub fn update_search(&mut self) {
|
||||
if self.input_buffer.is_empty() {
|
||||
self.filtered_items = self.all_items.clone();
|
||||
} else {
|
||||
let matcher = SkimMatcherV2::default();
|
||||
|
||||
let mut matched: Vec<(i64, String)> = self
|
||||
.all_items
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
matcher
|
||||
.fuzzy_match(item, &self.input_buffer)
|
||||
.map(|score| (score, item.clone()))
|
||||
})
|
||||
.collect();
|
||||
|
||||
matched.sort_by(|a, b| b.0.cmp(&a.0));
|
||||
|
||||
self.filtered_items = matched.into_iter().map(|(_, item)| item).collect();
|
||||
self.update_preview();
|
||||
}
|
||||
|
||||
self.list_state.select(if self.filtered_items.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(0)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn delete_selected(&mut self) {
|
||||
if let Some(i) = self.list_state.selected() {
|
||||
if i < self.filtered_items.len() {
|
||||
let item_to_remove = self.filtered_items.remove(i);
|
||||
|
||||
self.undo_stack.push((i, item_to_remove.clone()));
|
||||
|
||||
if let Some(pos) = self.all_items.iter().position(|x| *x == item_to_remove) {
|
||||
self.all_items.remove(pos);
|
||||
}
|
||||
|
||||
if self.filtered_items.is_empty() {
|
||||
self.list_state.select(None);
|
||||
} else if i >= self.filtered_items.len() {
|
||||
self.list_state.select(Some(self.filtered_items.len() - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.update_preview();
|
||||
}
|
||||
|
||||
pub fn undo_delete(&mut self) {
|
||||
if let Some((i, item)) = self.undo_stack.pop() {
|
||||
self.all_items.insert(i, item.clone());
|
||||
self.update_search();
|
||||
self.list_state.select(Some(i));
|
||||
}
|
||||
self.update_preview();
|
||||
}
|
||||
|
||||
pub fn update_preview(&mut self) {
|
||||
let current_index = self.list_state.selected();
|
||||
|
||||
if self.last_selected_index == current_index {
|
||||
return;
|
||||
}
|
||||
self.last_selected_index = current_index;
|
||||
self.current_image = None;
|
||||
|
||||
if let Some(selected_text) = self.get_selected_item() {
|
||||
// To change later with entry type
|
||||
if selected_text.ends_with(".jpg") || selected_text.ends_with(".png") {
|
||||
let base_dir = directories::ProjectDirs::from("com", "zefad", "rklipd")
|
||||
.expect("No home dir")
|
||||
.data_dir()
|
||||
.to_path_buf();
|
||||
|
||||
let img_path = base_dir.join("images").join(selected_text);
|
||||
|
||||
if img_path.exists() {
|
||||
if let Ok(img) = image::open(&img_path) {
|
||||
let protocol = self.picker.new_resize_protocol(img);
|
||||
self.current_image = Some(protocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&mut self) {
|
||||
if self.filtered_items.is_empty() {
|
||||
return;
|
||||
}
|
||||
let i = match self.list_state.selected() {
|
||||
Some(i) => {
|
||||
if i >= self.filtered_items.len() - 1 {
|
||||
0
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
self.list_state.select(Some(i));
|
||||
self.update_preview();
|
||||
}
|
||||
|
||||
pub fn previous(&mut self) {
|
||||
if self.filtered_items.is_empty() {
|
||||
return;
|
||||
}
|
||||
let i = match self.list_state.selected() {
|
||||
Some(i) => {
|
||||
if i == 0 {
|
||||
self.filtered_items.len() - 1
|
||||
} else {
|
||||
i - 1
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
self.list_state.select(Some(i));
|
||||
self.update_preview();
|
||||
}
|
||||
|
||||
pub fn get_selected_item(&self) -> Option<&String> {
|
||||
self.list_state.selected().map(|i| &self.filtered_items[i])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user