aboutsummaryrefslogtreecommitdiff
path: root/src/tui
diff options
context:
space:
mode:
authorlukeflo2024-11-11 22:13:32 +0100
committerlukeflo2024-11-14 09:35:45 +0100
commit761ce14125f0e4ecffbfaf3bf82b4b406f5aa769 (patch)
treef78f106d93bfb2466f1d7785b3080eb4b0e333d9 /src/tui
parent45d85162e455db37f9263088a7703d614969b373 (diff)
downloadbibiman-761ce14125f0e4ecffbfaf3bf82b4b406f5aa769.tar.gz
bibiman-761ce14125f0e4ecffbfaf3bf82b4b406f5aa769.zip
impl popup for keybindings/messages
Diffstat (limited to 'src/tui')
-rw-r--r--src/tui/commands.rs4
-rw-r--r--src/tui/handler.rs197
-rw-r--r--src/tui/popup.rs104
-rw-r--r--src/tui/ui.rs36
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) {