diff options
| author | lukeflo | 2024-12-23 21:03:48 +0100 |
|---|---|---|
| committer | lukeflo | 2024-12-23 21:03:48 +0100 |
| commit | 57126d7f42b871aa1835b056fbe74179c13e53b0 (patch) | |
| tree | bf81db50446aa7a8e06553f4e8aff97ea0cfb816 /src | |
| parent | ad5c2cb586616eca99fc1db0efaaa0ff5aa97144 (diff) | |
| parent | 9a33a794167d60ce35030f007674f6e9424b1ff3 (diff) | |
| download | bibiman-57126d7f42b871aa1835b056fbe74179c13e53b0.tar.gz bibiman-57126d7f42b871aa1835b056fbe74179c13e53b0.zip | |
Merge branch 'add-entry-via-doi'
+ implement the functionality to add an entry via DOI
+ responsive error messages if resolving of DOI don't work
+ keep changes to choosen file to minimum
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.rs | 116 | ||||
| -rw-r--r-- | src/bibiman.rs | 288 | ||||
| -rw-r--r-- | src/main.rs | 4 | ||||
| -rw-r--r-- | src/tui/commands.rs | 10 | ||||
| -rw-r--r-- | src/tui/popup.rs | 10 | ||||
| -rw-r--r-- | src/tui/ui.rs | 59 |
6 files changed, 416 insertions, 71 deletions
@@ -23,6 +23,7 @@ use crate::tui::commands::InputCmdAction; use crate::tui::popup::PopupKind; use crate::tui::{self, Tui}; use crate::{bibiman::Bibiman, tui::commands::CmdAction}; +use core::panic; use std::ffi::OsStr; use std::path::PathBuf; use std::process::{Command, Stdio}; @@ -58,7 +59,7 @@ impl App { }) } - pub async fn run(&mut self, args: &CLIArgs) -> Result<()> { + pub async fn run(&mut self, args: &mut CLIArgs) -> Result<()> { let mut tui = tui::Tui::new()?; tui.enter()?; @@ -109,29 +110,61 @@ impl App { self.running = false; } - pub fn run_command(&mut self, cmd: CmdAction, args: &CLIArgs, tui: &mut Tui) -> Result<()> { + pub fn run_command(&mut self, cmd: CmdAction, args: &mut CLIArgs, tui: &mut Tui) -> Result<()> { match cmd { CmdAction::Input(cmd) => match cmd { InputCmdAction::Nothing => {} InputCmdAction::Handle(event) => { self.input.handle_event(&event); - self.bibiman.search_list_by_pattern(&self.input); + if let CurrentArea::SearchArea = self.bibiman.current_area { + self.bibiman.search_list_by_pattern(&self.input); + } } InputCmdAction::Enter => { self.input_mode = true; // Logic for TABS to be added - self.bibiman.enter_search_area(); + // self.bibiman.enter_search_area(); } InputCmdAction::Confirm => { + // Logic for TABS to be added + if let CurrentArea::SearchArea = self.bibiman.current_area { + self.bibiman.confirm_search(); + } else if let CurrentArea::PopupArea = self.bibiman.current_area { + match self.bibiman.popup_area.popup_kind { + Some(PopupKind::AddEntry) => { + let doi = self.input.value(); + self.bibiman.close_popup(); + self.input_mode = false; + // Check if the DOI pattern is valid. If not, show warning and break + if doi.starts_with("10.") + || doi.starts_with("https://doi.org") + || doi.starts_with("https://dx.doi.org") + || doi.starts_with("http://doi.org") + || doi.starts_with("http://dx.doi.org") + { + self.bibiman.handle_new_entry_submission(args, doi); + } else { + self.bibiman.popup_area.popup_message( + "No valid DOI pattern: ", + doi, + false, + ); + } + } + _ => {} + } + } self.input = Input::default(); self.input_mode = false; - // Logic for TABS to be added - self.bibiman.confirm_search(); } InputCmdAction::Exit => { self.input = Input::default(); self.input_mode = false; - self.bibiman.break_search(); + if let CurrentArea::SearchArea = self.bibiman.current_area { + self.bibiman.break_search(); + } else if let CurrentArea::PopupArea = self.bibiman.current_area { + self.bibiman.close_popup(); + } } }, CmdAction::SelectNextRow(amount) => match self.bibiman.current_area { @@ -142,13 +175,15 @@ impl App { CurrentArea::TagArea => { self.bibiman.select_next_tag(amount); } - CurrentArea::PopupArea => { - if let Some(PopupKind::Help) = self.bibiman.popup_area.popup_kind { + CurrentArea::PopupArea => match self.bibiman.popup_area.popup_kind { + Some(PopupKind::Help) => { self.bibiman.popup_area.popup_scroll_down(); - } else if let Some(PopupKind::Selection) = self.bibiman.popup_area.popup_kind { + } + Some(PopupKind::OpenRes) | Some(PopupKind::AppendToFile) => { self.bibiman.popup_area.popup_state.scroll_down_by(1) } - } + _ => {} + }, _ => {} }, CmdAction::SelectPrevRow(amount) => match self.bibiman.current_area { @@ -159,13 +194,15 @@ impl App { CurrentArea::TagArea => { self.bibiman.select_previous_tag(amount); } - CurrentArea::PopupArea => { - if let Some(PopupKind::Help) = self.bibiman.popup_area.popup_kind { + CurrentArea::PopupArea => match self.bibiman.popup_area.popup_kind { + Some(PopupKind::Help) => { self.bibiman.popup_area.popup_scroll_up(); - } else if let Some(PopupKind::Selection) = self.bibiman.popup_area.popup_kind { + } + Some(PopupKind::OpenRes) | Some(PopupKind::AppendToFile) => { self.bibiman.popup_area.popup_state.scroll_up_by(1) } - } + _ => {} + }, _ => {} }, CmdAction::SelectNextCol => { @@ -205,14 +242,20 @@ impl App { CmdAction::ToggleArea => { self.bibiman.toggle_area(); } - CmdAction::SearchList => {} + CmdAction::SearchList => { + self.input_mode = true; + self.bibiman.enter_search_area(); + } CmdAction::Reset => { if let CurrentArea::PopupArea = self.bibiman.current_area { if let Some(PopupKind::Help) = self.bibiman.popup_area.popup_kind { self.bibiman.popup_area.popup_scroll_pos = 0; self.bibiman.close_popup() - } else if let Some(PopupKind::Selection) = self.bibiman.popup_area.popup_kind { + } else if let Some(PopupKind::OpenRes) = self.bibiman.popup_area.popup_kind { self.bibiman.close_popup() + } else if let Some(PopupKind::AppendToFile) = self.bibiman.popup_area.popup_kind + { + self.bibiman.close_popup(); } } else { self.bibiman.reset_current_list(); @@ -224,33 +267,11 @@ impl App { } else if let CurrentArea::PopupArea = self.bibiman.current_area { if let Some(PopupKind::Help) = self.bibiman.popup_area.popup_kind { self.bibiman.close_popup(); - } else if let Some(PopupKind::Selection) = self.bibiman.popup_area.popup_kind { - // Index of selected entry - let entry_idx = self - .bibiman - .entry_table - .entry_table_state - .selected() - .unwrap(); - - // Index of selected popup field - let popup_idx = self.bibiman.popup_area.popup_state.selected().unwrap(); - - // Choose ressource depending an selected popup field - if self.bibiman.popup_area.popup_list[popup_idx].contains("Weblink") { - let object = - self.bibiman.entry_table.entry_table_items[entry_idx].doi_url(); - let url = prepare_weblink(object); - open_connected_link(&url)?; - } else if self.bibiman.popup_area.popup_list[popup_idx].contains("File") { - let object = - self.bibiman.entry_table.entry_table_items[entry_idx].filepath(); - open_connected_file(object)?; - } else { - eprintln!("Unable to find ressource to open"); - }; - // run command to open file/Url - self.bibiman.close_popup() + } else if let Some(PopupKind::OpenRes) = self.bibiman.popup_area.popup_kind { + self.bibiman.open_connected_res()?; + } else if let Some(PopupKind::AppendToFile) = self.bibiman.popup_area.popup_kind + { + self.bibiman.append_entry_to_file(args)? } } } @@ -299,6 +320,7 @@ impl App { if entry.filepath.is_some() { items.push("File (PDF/EPUB)".to_owned()) } + self.bibiman.popup_area.popup_kind = Some(PopupKind::OpenRes); self.bibiman.popup_area.popup_selection(items); self.bibiman.former_area = Some(FormerArea::EntryArea); self.bibiman.current_area = CurrentArea::PopupArea; @@ -312,6 +334,12 @@ impl App { } } } + CmdAction::AddEntry => { + if let CurrentArea::EntryArea = self.bibiman.current_area { + self.input_mode = true; + self.bibiman.add_entry(); + } + } CmdAction::ShowHelp => { self.bibiman.show_help(); } diff --git a/src/bibiman.rs b/src/bibiman.rs index bba3eec..0c0d99b 100644 --- a/src/bibiman.rs +++ b/src/bibiman.rs @@ -15,6 +15,7 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. ///// +use crate::app; use crate::bibiman::entries::EntryTableColumn; use crate::bibiman::{bibisetup::*, search::BibiSearch}; use crate::cliargs::CLIArgs; @@ -22,11 +23,17 @@ use crate::tui::popup::{PopupArea, PopupKind}; use crate::tui::Tui; use crate::{bibiman::entries::EntryTable, bibiman::keywords::TagList}; use arboard::Clipboard; -use color_eyre::eyre::{Ok, Result}; +use color_eyre::eyre::Result; use editor_command::EditorBuilder; +use futures::executor::block_on; use ratatui::widgets::ScrollbarState; +use regex::Regex; use std::fs; +use std::fs::{File, OpenOptions}; +use std::io::Write; +use std::path::PathBuf; use std::process::Command; +use std::result::Result::Ok; use tui_input::Input; pub mod bibisetup; @@ -107,6 +114,48 @@ impl Bibiman { self.popup_area.popup_kind = Some(PopupKind::Help); } + pub fn add_entry(&mut self) { + if let CurrentArea::EntryArea = self.current_area { + self.former_area = Some(FormerArea::EntryArea); + } + self.popup_area.is_popup = true; + self.current_area = CurrentArea::PopupArea; + self.popup_area.popup_kind = Some(PopupKind::AddEntry); + } + + ///Try to resolve entered DOI. If successfull, choose file where to append + ///the new entry via `append_to_file()` function. If not, show error popup + /// + ///The method needs two arguments: the CLIArgs struct and the `str` containing the DOI + pub fn handle_new_entry_submission(&mut self, args: &CLIArgs, doi_string: &str) { + let doi_string = if doi_string.starts_with("10.") { + "https://doi.org/".to_string() + doi_string + } else { + doi_string.to_owned() + }; + + // Send GET request to doi resolver + let doi_entry = ureq::get(&doi_string) + .set("Accept", "application/x-bibtex") + .call(); + + if let Ok(entry) = doi_entry { + // Save generated bibtex entry in structs field + let entry = entry + .into_string() + .expect("Couldn't parse fetched entry into string"); + self.popup_area.popup_sel_item = entry; + self.popup_area.popup_kind = Some(PopupKind::AppendToFile); + self.append_to_file(args); + self.former_area = Some(FormerArea::EntryArea); + self.current_area = CurrentArea::PopupArea; + self.popup_area.popup_state.select(Some(0)) + } else { + self.popup_area + .popup_message("Can't find DOI: ", &doi_string, false); + } + } + pub fn close_popup(&mut self) { // Reset all popup fields to default values self.popup_area = PopupArea::default(); @@ -299,6 +348,26 @@ impl Bibiman { } } + pub fn select_entry_by_citekey(&mut self, citekey: &str) { + // Search for entry by matching citekeys + let mut idx_count = 0; + loop { + if idx_count == self.entry_table.entry_table_items.len() { + idx_count = 0; + break; + } else if self.entry_table.entry_table_items[idx_count] + .citekey + .contains(citekey) + { + break; + } + idx_count += 1 + } + + // Set selected entry to vec-index of match + self.entry_table.entry_table_state.select(Some(idx_count)); + } + pub fn run_editor(&mut self, args: &CLIArgs, tui: &mut Tui) -> Result<()> { // get filecontent and citekey for calculating line number let citekey: &str = &self.entry_table.entry_table_items @@ -367,25 +436,205 @@ impl Bibiman { // Update the database and the lists to show changes Self::update_lists(self, args); - // Search for entry, selected before editing, by matching citekeys - // Use earlier saved copy of citekey to match - let mut idx_count = 0; - loop { - if self.entry_table.entry_table_items[idx_count] - .citekey - .contains(citekey) - { - break; + // Select entry which was selected before entering editor + self.select_entry_by_citekey(citekey); + + Ok(()) + } + + pub fn append_to_file(&mut self, args: &CLIArgs) { + let mut items = vec!["Create new file".to_owned()]; + if args.files.len() > 1 { + for f in args.files.clone() { + items.push(f.to_str().unwrap().to_owned()); } - idx_count += 1 + } else { + items.push(args.files.first().unwrap().to_str().unwrap().to_owned()); } + self.popup_area.popup_selection(items); + } - // Set selected entry to vec-index of match - self.entry_table.entry_table_state.select(Some(idx_count)); + pub fn append_entry_to_file(&mut self, args: &mut CLIArgs) -> Result<()> { + // Index of selected popup field + let popup_idx = self.popup_area.popup_state.selected().unwrap(); + + // regex pattern to match citekey in fetched bibtexstring + let pattern = Regex::new(r"\{([^\{\},]*),").unwrap(); + + let citekey = pattern + .captures(&self.popup_area.popup_sel_item) + .unwrap() + .get(1) + .unwrap() + .as_str() + .to_string(); + + // Check if new file or existing file was choosen + let mut file = if self.popup_area.popup_list[popup_idx].contains("Create new file") { + let citekey = PathBuf::from(&citekey); + // Get path of current files + let path: PathBuf = if args.files[0].is_file() { + args.files[0].parent().unwrap().to_owned() + } else { + dirs::home_dir().unwrap() // home dir as fallback + }; + + let citekey = citekey.with_extension("bib"); + + let newfile = path.join(citekey); + + args.files.push(newfile.clone()); + + File::create_new(newfile).unwrap() + } else { + let file_path = &args.files[popup_idx - 1]; + OpenOptions::new().append(true).open(file_path).unwrap() + }; + // Optionally, add a newline before the content + file.write_all(b"\n")?; + // Write content to file + file.write_all(self.popup_area.popup_sel_item.as_bytes())?; + // Update the database and the lists to reflect the new content + self.update_lists(args); + self.close_popup(); + + // Select newly created entry + self.select_entry_by_citekey(&citekey); + + Ok(()) + } + + pub fn open_connected_res(&mut self) -> Result<()> { + // Index of selected entry + let entry_idx = self.entry_table.entry_table_state.selected().unwrap(); + + // Index of selected popup field + let popup_idx = self.popup_area.popup_state.selected().unwrap(); + + // Choose ressource depending an selected popup field + if self.popup_area.popup_list[popup_idx].contains("Weblink") { + let object = self.entry_table.entry_table_items[entry_idx].doi_url(); + let url = app::prepare_weblink(object); + app::open_connected_link(&url)?; + } else if self.popup_area.popup_list[popup_idx].contains("File") { + let object = self.entry_table.entry_table_items[entry_idx].filepath(); + app::open_connected_file(object)?; + } else { + eprintln!("Unable to find ressource to open"); + }; + // run command to open file/Url + self.close_popup(); Ok(()) } + /// Formats a raw BibTeX entry string for better readability. + pub fn format_bibtex_entry(entry: &str, file_path: &str) -> String { + let mut formatted = String::new(); + + // Find the position of the first '{' + if let Some(start_brace_pos) = entry.find('{') { + // Extract the preamble (e.g., '@article{') + let preamble = &entry[..start_brace_pos + 1]; + let preamble = preamble.trim_start(); + formatted.push_str(preamble); + // formatted.push('\n'); // Add newline + + // Get the content inside the braces + let rest = &entry[start_brace_pos + 1..]; + // Remove the last '}' at the end, if present + let rest = rest.trim_end(); + let rest = if rest.ends_with('}') { + &rest[..rest.len() - 1] + } else { + rest + }; + + // Parse the fields, considering braces and quotes + let mut fields = Vec::new(); + let mut current_field = String::new(); + let mut brace_level = 0; + let mut in_quotes = false; + for c in rest.chars() { + match c { + '{' if !in_quotes => { + brace_level += 1; + current_field.push(c); + } + '}' if !in_quotes => { + brace_level -= 1; + current_field.push(c); + } + '"' => { + in_quotes = !in_quotes; + current_field.push(c); + } + ',' if brace_level == 0 && !in_quotes => { + // Outside of braces and quotes, comma separates fields + fields.push(current_field.trim().to_string()); + current_field.clear(); + } + _ => { + current_field.push(c); + } + } + } + // Add the last field + if !current_field.trim().is_empty() { + fields.push(current_field.trim().to_string()); + } + + // **Conditionally Clean the Citation Key** + if let Some(citation_key) = fields.get_mut(0) { + // Check if the citation key contains any non-alphanumerical characters except underscores + let needs_cleaning = citation_key + .chars() + .any(|c| !c.is_alphanumeric() && c != '_'); + if needs_cleaning { + // Retain only alphanumerical characters and underscores + let cleaned_key: String = citation_key + .chars() + .filter(|c| c.is_alphanumeric() || *c == '_') + .collect(); + // If the cleaned key is longer than 14 characters, retain only the last 14 + let limited_key = if cleaned_key.len() > 14 { + cleaned_key + .chars() + .rev() + .take(14) + .collect::<String>() + .chars() + .rev() + .collect() + } else { + cleaned_key + }; + // Replace the original citation key with the cleaned and possibly limited key + *citation_key = limited_key; + } + } + + // Add the new 'file' field + let file_field = format!("file = {{{}}}", file_path); + fields.push(file_field); + + // Reconstruct the entry with proper indentation + for (i, field) in fields.iter().enumerate() { + formatted.push_str(" "); + formatted.push_str(field); + // Add a comma if it's not the last field + if i < fields.len() - 1 { + formatted.push(','); + } + formatted.push('\n'); + } + formatted.push('}'); // Close the entry + formatted + } else { + // No opening brace found, return the entry as is + entry.to_string() + } + } // Search entry list pub fn search_entries(&mut self) { // Use snapshot of entry list saved when starting the search @@ -594,7 +843,7 @@ impl Bibiman { #[cfg(test)] mod tests { - // use super::*; + use super::*; #[test] fn citekey_pattern() { @@ -602,4 +851,15 @@ mod tests { assert_eq!(citekey, "{a_key_2001,") } + + #[test] + fn regex_capture_citekey() { + let re = Regex::new(r"\{([^\{\},]*),").unwrap(); + + let bibstring = String::from("@article{citekey77_2001:!?, author = {Hanks, Tom}, title = {A great book}, year = {2001}}"); + + let result = re.captures(&bibstring).unwrap(); + + assert_eq!(result.get(1).unwrap().as_str(), "citekey77_2001:!?") + } } diff --git a/src/main.rs b/src/main.rs index b1160e2..78c5075 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ pub mod tui; #[tokio::main] async fn main() -> Result<()> { // Parse CLI arguments - let parsed_args = CLIArgs::parse_args().unwrap(); + let mut parsed_args = CLIArgs::parse_args().unwrap(); // Print help if -h/--help flag is passed and exit if parsed_args.helparg { @@ -48,6 +48,6 @@ async fn main() -> Result<()> { // Create an application. let mut app = App::new(&parsed_args)?; - app.run(&parsed_args).await?; + app.run(&mut parsed_args).await?; Ok(()) } diff --git a/src/tui/commands.rs b/src/tui/commands.rs index a3049ee..0e00f95 100644 --- a/src/tui/commands.rs +++ b/src/tui/commands.rs @@ -69,6 +69,8 @@ pub enum CmdAction { Exit, // Show keybindings ShowHelp, + // Add new entry + AddEntry, // Do nothing. Nothing, } @@ -110,7 +112,6 @@ impl From<KeyEvent> for CmdAction { Self::SelectPrevRow(5) } else { Self::Nothing - // Self::Open(OpenRessource::WebLink) } } // Scroll info/preview area @@ -118,6 +119,8 @@ impl From<KeyEvent> for CmdAction { KeyCode::PageUp => Self::ScrollInfoUp, // Exit App KeyCode::Char('q') => Self::Exit, + // Add new entry + KeyCode::Char('a') => Self::AddEntry, KeyCode::Char('c') | KeyCode::Char('C') => { if key_event.modifiers == KeyModifiers::CONTROL { Self::Exit @@ -129,15 +132,14 @@ impl From<KeyEvent> for CmdAction { KeyCode::Tab => Self::ToggleArea, KeyCode::BackTab => Self::ToggleArea, // Enter search mode - KeyCode::Char('/') => Self::Input(InputCmdAction::Enter), + KeyCode::Char('/') => Self::SearchList, KeyCode::Char('f') => { if key_event.modifiers == KeyModifiers::CONTROL { - Self::Input(InputCmdAction::Enter) + Self::SearchList } else { Self::Nothing } } - // KeyCode::Backspace => Self::Input(InputCommand::Resume(Event::Key(key_event))), // Confirm selection KeyCode::Enter => Self::Confirm, // Reset lists/tables diff --git a/src/tui/popup.rs b/src/tui/popup.rs index 890e5c8..78a0719 100644 --- a/src/tui/popup.rs +++ b/src/tui/popup.rs @@ -28,7 +28,9 @@ pub enum PopupKind { Help, MessageConfirm, MessageError, - Selection, + OpenRes, + AppendToFile, + AddEntry, } #[derive(Debug, Default)] @@ -39,6 +41,9 @@ pub struct PopupArea { pub popup_scroll_pos: u16, pub popup_list: Vec<String>, pub popup_state: ListState, + pub popup_sel_item: String, + // pub add_entry_input: String, + // pub add_entry_cursor_position: usize, } impl PopupArea { @@ -60,6 +65,7 @@ impl PopupArea { ("e: ", "Open editor at selected entry"), ("o: ", "Open with selected entry associated PDF"), ("u: ", "Open DOI/URL of selected entry"), + ("a: ", "Add new entry"), ("ESC: ", "Reset all lists"), ("Keyword List", "sub"), ("j,k|↓,↑: ", "Select next/previous item"), @@ -119,7 +125,7 @@ impl PopupArea { pub fn popup_selection(&mut self, items: Vec<String>) { self.popup_list = items; - self.popup_kind = Some(PopupKind::Selection); + // self.popup_kind = Some(PopupKind::SelectRes); self.is_popup = true; } diff --git a/src/tui/ui.rs b/src/tui/ui.rs index cca87ce..4f64338 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -181,6 +181,46 @@ pub fn render_popup(app: &mut App, args: &CLIArgs, frame: &mut Frame) { frame.render_widget(Clear, popup_area); frame.render_widget(par, popup_area) } + + Some(PopupKind::AddEntry) => { + let area = frame.area(); + + let block = Block::bordered() + .title_top(" Add Entry ".bold()) + .title_bottom(" (ESC) ━ (ENTER) ".bold()) + .title_alignment(Alignment::Center) + .style( + Style::new() + .fg(Color::Indexed(args.colors.main_text_color)) + .bg(Color::Indexed(args.colors.popup_bg_color)), + ) + .border_set(symbols::border::THICK) + .border_style(Style::new().fg(Color::Indexed(args.colors.entry_color))); + + // Prepare the input fields + let content = vec![Line::from(vec![ + Span::styled( + "DOI: ", + Style::new().fg(Color::Indexed(args.colors.entry_color)), + ), + Span::raw(app.input.value().to_string().clone()), + ])]; + let paragraph = Paragraph::new(content) + .block(block.clone()) + .style(Style::new().fg(Color::Indexed(args.colors.main_text_color))) + .wrap(Wrap { trim: false }); + + let doi_lines = paragraph.line_count(area.width / 2); + // Calculate popup size + let popup_width = area.width / 4 * 3; + let popup_height = doi_lines as u16; // Adjust as needed + let popup_area = popup_area(area, popup_width, popup_height); + + // Render the popup + frame.render_widget(Clear, popup_area); + render_cursor(app, frame, popup_area, 6, doi_lines as u16 - 1); + frame.render_widget(paragraph, popup_area); + } Some(PopupKind::MessageConfirm) => { let area = frame.area(); @@ -243,7 +283,7 @@ pub fn render_popup(app: &mut App, args: &CLIArgs, frame: &mut Frame) { frame.render_widget(Clear, popup_area); frame.render_widget(&content, popup_area) } - Some(PopupKind::Selection) => { + Some(PopupKind::OpenRes) | Some(PopupKind::AppendToFile) => { let list_items: Vec<ListItem> = app .bibiman .popup_area @@ -252,8 +292,16 @@ pub fn render_popup(app: &mut App, args: &CLIArgs, frame: &mut Frame) { .map(|item| ListItem::from(item.to_owned())) .collect(); + let title = if let Some(PopupKind::OpenRes) = app.bibiman.popup_area.popup_kind { + " Open " + } else if let Some(PopupKind::AppendToFile) = app.bibiman.popup_area.popup_kind { + " Select file to append entry " + } else { + " Select " + }; + let block = Block::bordered() - .title_top(" Open ".bold()) + .title_top(title.bold()) .title_bottom(" (j,k|↓,↑) ━ (ENTER) ━ (ESC) ".bold()) .title_alignment(Alignment::Center) .style( @@ -327,7 +375,7 @@ pub fn render_footer(app: &mut App, args: &CLIArgs, frame: &mut Frame, rect: Rec ])) .block(block); - render_cursor(app, frame, rect, title_lenght + 1); + render_cursor(app, frame, rect, title_lenght + 1, 1); frame.render_widget(search_string, rect); } @@ -986,12 +1034,13 @@ pub fn render_taglist(app: &mut App, args: &CLIArgs, frame: &mut Frame, rect: Re } /// Render the cursor when in InputMode -fn render_cursor(app: &mut App, frame: &mut Frame, area: Rect, x_offset: u16) { +fn render_cursor(app: &mut App, frame: &mut Frame, area: Rect, x_offset: u16, y_offset: u16) { let scroll = app.input.visual_scroll(area.width as usize); if app.input_mode { let (x, y) = ( area.x + ((app.input.visual_cursor()).max(scroll) - scroll) as u16 + x_offset, - area.bottom().saturating_sub(1), + // area.bottom().saturating_sub(1) + y_offset, + area.bottom().saturating_sub(y_offset), ); frame.render_widget( Clear, |
