diff options
| author | lukeflo | 2024-11-11 22:13:32 +0100 |
|---|---|---|
| committer | lukeflo | 2024-11-14 09:35:45 +0100 |
| commit | 761ce14125f0e4ecffbfaf3bf82b4b406f5aa769 (patch) | |
| tree | f78f106d93bfb2466f1d7785b3080eb4b0e333d9 /src/tui | |
| parent | 45d85162e455db37f9263088a7703d614969b373 (diff) | |
| download | bibiman-761ce14125f0e4ecffbfaf3bf82b4b406f5aa769.tar.gz bibiman-761ce14125f0e4ecffbfaf3bf82b4b406f5aa769.zip | |
impl popup for keybindings/messages
Diffstat (limited to 'src/tui')
| -rw-r--r-- | src/tui/commands.rs | 4 | ||||
| -rw-r--r-- | src/tui/handler.rs | 197 | ||||
| -rw-r--r-- | src/tui/popup.rs | 104 | ||||
| -rw-r--r-- | src/tui/ui.rs | 36 |
4 files changed, 137 insertions, 204 deletions
diff --git a/src/tui/commands.rs b/src/tui/commands.rs index dc6b2c8..6a2ab13 100644 --- a/src/tui/commands.rs +++ b/src/tui/commands.rs @@ -67,6 +67,8 @@ pub enum CmdAction { Input(InputCmdAction), // Hexdump command. Exit, + // Show keybindings + ShowHelp, // Do nothing. Nothing, } @@ -148,6 +150,8 @@ impl From<KeyEvent> for CmdAction { KeyCode::Char('y') => Self::YankItem, // Sort entry table by selected col KeyCode::Char('s') => Self::SortList, + // Show help popup + KeyCode::Char('?') => Self::ShowHelp, // Else do nothing _ => Self::Nothing, } diff --git a/src/tui/handler.rs b/src/tui/handler.rs deleted file mode 100644 index ae2fd4d..0000000 --- a/src/tui/handler.rs +++ /dev/null @@ -1,197 +0,0 @@ -// bibiman - a TUI for managing BibLaTeX databases -// Copyright (C) 2024 lukeflo -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. -///// - -use crate::bibiman::{Bibiman, CurrentArea}; -use crate::tui::Tui; -use crate::App; -use color_eyre::eyre::Result; -use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - -/// Handles the key events and updates the state of [`App`]. -pub fn handle_key_events(key_event: KeyEvent, app: &mut App, tui: &mut Tui) -> Result<()> { - // Keycodes activated for every area (high priority) - match key_event.code { - // Exit application on `ESC` or `q` - KeyCode::Char('Q') | KeyCode::Char('q') => { - app.quit(); - } - // Exit application on `Ctrl-C` - KeyCode::Char('c') | KeyCode::Char('C') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.quit(); - } - } - KeyCode::PageDown => { - app.bibiman.scroll_info_down(); - } - KeyCode::PageUp => { - app.bibiman.scroll_info_up(); - } - _ => {} - } - // Keycodes for specific areas - match app.bibiman.current_area { - // Keycodes for the tag area - CurrentArea::TagArea => match key_event.code { - KeyCode::Down => { - app.bibiman.select_next_tag(1); - } - KeyCode::Up => { - app.bibiman.select_previous_tag(1); - } - KeyCode::Char('j') => { - if key_event.modifiers == KeyModifiers::ALT { - app.bibiman.scroll_info_down(); - } else { - app.bibiman.select_next_tag(1); - } - } - KeyCode::Char('k') => { - if key_event.modifiers == KeyModifiers::ALT { - app.bibiman.scroll_info_up(); - } else { - app.bibiman.select_previous_tag(1); - } - } - KeyCode::Char('d') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.select_next_tag(5) - } - } - KeyCode::Char('u') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.select_previous_tag(5) - } - } - KeyCode::Char('g') | KeyCode::Home => { - app.bibiman.select_first_tag(); - } - KeyCode::Char('G') | KeyCode::End => { - app.bibiman.select_last_tag(); - } - KeyCode::Char('/') => { - app.bibiman.enter_search_area(); - } - KeyCode::Char('f') | KeyCode::Char('F') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.enter_search_area(); - } - } - KeyCode::Tab | KeyCode::BackTab => { - app.bibiman.toggle_area(); - } - KeyCode::Esc => { - app.bibiman.reset_current_list(); - } - KeyCode::Enter => { - app.bibiman.filter_for_tags(); - } - _ => {} - }, - // Keycodes for the entry area - CurrentArea::EntryArea => match key_event.code { - KeyCode::Down => { - app.bibiman.select_next_entry(1); - } - KeyCode::Up => { - app.bibiman.select_previous_entry(1); - } - KeyCode::Char('j') => { - if key_event.modifiers == KeyModifiers::ALT { - app.bibiman.scroll_info_down(); - } else { - app.bibiman.select_next_entry(1); - } - } - KeyCode::Char('k') => { - if key_event.modifiers == KeyModifiers::ALT { - app.bibiman.scroll_info_up(); - } else { - app.bibiman.select_previous_entry(1); - } - } - KeyCode::Char('d') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.select_next_entry(5); - } - } - KeyCode::Char('u') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.select_previous_entry(5); - } else { - app.bibiman.open_doi_url()?; - } - } - KeyCode::Char('g') | KeyCode::Home => { - app.bibiman.select_first_entry(); - } - KeyCode::Char('G') | KeyCode::End => { - app.bibiman.select_last_entry(); - } - KeyCode::Char('h') => { - app.bibiman.select_prev_column(); - } - KeyCode::Char('l') => { - app.bibiman.select_next_column(); - } - KeyCode::Char('s') => { - app.bibiman.entry_table.sort_entry_table(true); - } - KeyCode::Char('y') => { - Bibiman::yank_text(&app.bibiman.get_selected_citekey()); - } - KeyCode::Char('e') => { - app.bibiman.run_editor(tui)?; - } - KeyCode::Char('o') => { - app.bibiman.open_connected_file()?; - } - KeyCode::Char('/') => { - app.bibiman.enter_search_area(); - } - KeyCode::Char('f') | KeyCode::Char('F') => { - if key_event.modifiers == KeyModifiers::CONTROL { - app.bibiman.enter_search_area(); - } - } - KeyCode::Tab | KeyCode::BackTab => { - app.bibiman.toggle_area(); - } - KeyCode::Esc => { - app.bibiman.reset_current_list(); - } - _ => {} - }, - // Keycodes for the search area (rendered in footer) - CurrentArea::SearchArea => match key_event.code { - KeyCode::Esc => { - app.bibiman.break_search(); - } - KeyCode::Enter => { - app.bibiman.confirm_search(); - } - KeyCode::Backspace => { - app.bibiman.search_pattern_pop(); - } - KeyCode::Char(search_pattern) => { - app.bibiman.search_pattern_push(search_pattern); - } - _ => {} - }, - } - Ok(()) -} diff --git a/src/tui/popup.rs b/src/tui/popup.rs new file mode 100644 index 0000000..60c58b4 --- /dev/null +++ b/src/tui/popup.rs @@ -0,0 +1,104 @@ +// bibiman - a TUI for managing BibLaTeX databases +// Copyright (C) 2024 lukeflo +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +///// + +use ratatui::{ + style::{Color, Stylize}, + text::{Line, Span, Text}, + widgets::ListState, +}; + +use crate::MAIN_PURPLE_COLOR_INDEX; + +#[derive(Debug)] +pub enum PopupKind { + Help, + Message, + Selection, +} + +#[derive(Debug)] +pub struct PopupArea { + pub is_popup: bool, + pub popup_kind: Option<PopupKind>, + pub popup_message: String, + pub popup_list: Vec<String>, + pub popup_state: ListState, +} + +impl Default for PopupArea { + fn default() -> Self { + PopupArea { + is_popup: false, + popup_kind: None, + popup_message: String::new(), + popup_list: Vec::new(), + popup_state: ListState::default(), + } + } +} + +impl PopupArea { + pub fn popup_help<'a>() -> Text<'a> { + let help = [ + ("j,k|↓,↑: ", "Select next/previous item"), + ("h,l|←,→: ", "Select next/previous column (Entry table)"), + ("g|Home: ", "Go to first item"), + ("G|End: ", "Go to last item"), + ("s: ", "sort entries by selected column (toggles reversed)"), + ("TAB: ", "Toggle areas (Entries, Keywords)"), + ("/|Ctrl+f: ", "Enter search mode"), + ("y: ", "yank/copy citekey of selected entry to clipboard"), + ("e: ", "Open editor at selected entry"), + ("o: ", "Open with selected entry associated PDF"), + ("u: ", "Open DOI/URL of selected entry"), + ("ESC: ", "Reset all lists/abort search"), + ("ENTER: ", "Confirm search/filter by selected keyword"), + ("q|Ctrl+c: ", "Quit bibiman"), + ]; + + let help_text: Vec<Line<'_>> = help + .into_iter() + .map(|(keys, help)| { + Line::from(vec![ + Span::raw(keys) + .bold() + .fg(Color::Indexed(MAIN_PURPLE_COLOR_INDEX)), + Span::raw(help), + ]) + }) + .collect(); + + let text = Text::from(help_text); + text + } + + pub fn popup_message(&mut self, message: &str, object: String) { + if object.is_empty() { + self.popup_message = message.into(); + } else { + self.popup_message = format!("{} \"{}\"", message, object); + } + self.popup_kind = Some(PopupKind::Message); + self.is_popup = true; + } + + pub fn popup_close_message(&mut self) { + self.is_popup = false; + self.popup_message.clear(); + self.popup_kind = None + } +} diff --git a/src/tui/ui.rs b/src/tui/ui.rs index f2091a5..260f401 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -15,9 +15,15 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. ///// +use super::popup::PopupArea; use crate::bibiman::entries::EntryTableColumn; use crate::bibiman::{CurrentArea, FormerArea}; +use crate::tui::popup::PopupKind; use crate::App; +use crate::{ + MAIN_BLUE_COLOR_INDEX, MAIN_GREEN_COLOR_INDEX, MAIN_PURPLE_COLOR_INDEX, TEXT_FG_COLOR_INDEX, + TEXT_HIGHLIGHT_COLOR_INDEX, +}; use ratatui::layout::{Direction, Position}; use ratatui::widgets::Clear; use ratatui::Frame; @@ -31,13 +37,7 @@ use ratatui::{ ScrollbarOrientation, Table, Wrap, }, }; - -// Color indices -const MAIN_BLUE_COLOR_INDEX: u8 = 75; -const MAIN_PURPLE_COLOR_INDEX: u8 = 135; -const MAIN_GREEN_COLOR_INDEX: u8 = 29; -const TEXT_HIGHLIGHT_COLOR_INDEX: u8 = 254; -const TEXT_FG_COLOR_INDEX: u8 = 250; +use tui_popup::Popup; // Text colors const TEXT_FG_COLOR: Color = Color::Indexed(TEXT_FG_COLOR_INDEX); @@ -48,6 +48,7 @@ const MAIN_GREEN: Color = Color::Indexed(MAIN_GREEN_COLOR_INDEX); // Background colors const HEADER_FOOTER_BG: Color = Color::Indexed(235); +const POPUP_BG: Color = Color::Indexed(233); // Box styles // Keyword Box @@ -72,6 +73,8 @@ const BOX_SELECTED_TITLE_STYLE: Style = Style::new() const BOX_UNSELECTED_BORDER_STYLE: Style = Style::new().fg(TEXT_FG_COLOR); const BOX_UNSELECTED_TITLE_STYLE: Style = Style::new().fg(TEXT_FG_COLOR).add_modifier(Modifier::BOLD); +// Popup box +const POPUP_HELP_BOX: Style = Style::new().fg(TEXT_FG_COLOR).bg(POPUP_BG); // Entry table styles const ENTRY_SELECTED_ROW_STYLE: Style = Style::new() @@ -142,6 +145,25 @@ pub fn render_ui(app: &mut App, frame: &mut Frame) { render_selected_item(app, frame, info_area); render_taglist(app, frame, tag_area); render_file_info(app, frame, entry_info_area); + if app.bibiman.popup_area.is_popup { + render_popup(app, frame); + } +} + +pub fn render_popup(app: &mut App, frame: &mut Frame) { + let popup = if let Some(PopupKind::Help) = app.bibiman.popup_area.popup_kind { + Popup::new(PopupArea::popup_help()) + .title(" Keybindings ".bold().into_centered_line()) + .style(POPUP_HELP_BOX) + .border_style(Style::new().fg(MAIN_BLUE)) + } else if let Some(PopupKind::Message) = app.bibiman.popup_area.popup_kind { + Popup::new(Text::from(app.bibiman.popup_area.popup_message.as_str()).fg(MAIN_GREEN)) + .title(" Message ".bold().into_centered_line().fg(MAIN_GREEN)) + .style(POPUP_HELP_BOX) + } else { + panic!("No popup text detected") + }; + frame.render_widget(&popup, frame.area()) } pub fn render_header(frame: &mut Frame, rect: Rect) { |
