From bac26a76cd3368f42814e3d5d96ed64f68c8ee33 Mon Sep 17 00:00:00 2001 From: Remigiusz Micielski Date: Mon, 12 Aug 2024 11:47:00 +0200 Subject: [PATCH] hidable keys, padded help popup, search tab help in help popup --- rm-config/defaults/keymap.toml | 9 +++-- rm-config/src/keymap/mod.rs | 22 +++++++++-- rm-main/src/ui/global_popups/help.rs | 49 +++++++++++++++++++++++- rm-main/src/ui/tabs/search/bottom_bar.rs | 24 +++++++++++- rm-main/src/ui/tabs/search/mod.rs | 5 +-- 5 files changed, 95 insertions(+), 14 deletions(-) diff --git a/rm-config/defaults/keymap.toml b/rm-config/defaults/keymap.toml index 9edd7ae..c0b2d50 100644 --- a/rm-config/defaults/keymap.toml +++ b/rm-config/defaults/keymap.toml @@ -1,7 +1,7 @@ [general] keybindings = [ - { on = "?", action = "ShowHelp" }, - { on = "F1", action = "ShowHelp" }, + { on = "?", action = "ShowHelp", show_in_help = false }, + { on = "F1", action = "ShowHelp", show_in_help = false }, { on = "q", action = "Quit" }, { on = "Esc", action = "Close" }, @@ -16,8 +16,8 @@ keybindings = [ { on = "Home", action = "GoToBeginning" }, { on = "End", action = "GoToEnd" }, - { on = "PageUp", action = "ScrollPageUp" }, - { on = "PageDown", action = "ScrollPageDown" }, + { on = "PageUp", action = "ScrollPageUp", show_in_help = false }, + { on = "PageDown", action = "ScrollPageDown", show_in_help = false }, { modifier = "Ctrl", on = "u", action = "ScrollPageUp" }, { modifier = "Ctrl", on = "d", action = "ScrollPageDown" }, @@ -51,3 +51,4 @@ keybindings = [ keybindings = [ { on = "p", action = "ShowProvidersInfo" } ] + diff --git a/rm-config/src/keymap/mod.rs b/rm-config/src/keymap/mod.rs index 29de7aa..74c34cf 100644 --- a/rm-config/src/keymap/mod.rs +++ b/rm-config/src/keymap/mod.rs @@ -41,6 +41,7 @@ pub struct Keybinding> { #[serde(default)] pub modifier: KeyModifier, pub action: T, + pub show_in_help: bool, } impl> Keybinding { @@ -90,11 +91,12 @@ impl> Keybinding { } impl> Keybinding { - fn new(on: KeyCode, action: T, modifier: Option) -> Self { + fn new(on: KeyCode, action: T, modifier: Option, show_in_help: bool) -> Self { Self { on, modifier: modifier.unwrap_or(KeyModifier::None), action, + show_in_help, } } } @@ -105,11 +107,12 @@ impl<'de, T: Into + Deserialize<'de>> Deserialize<'de> for Keybinding D: serde::Deserializer<'de>, { #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "lowercase")] + #[serde(field_identifier, rename_all = "snake_case")] enum Field { On, Modifier, Action, + ShowInHelp, } struct KeybindingVisitor { @@ -130,6 +133,7 @@ impl<'de, T: Into + Deserialize<'de>> Deserialize<'de> for Keybinding let mut on = None; let mut modifier = None; let mut action = None; + let mut show_in_help = None; while let Some(key) = map.next_key()? { match key { Field::On => { @@ -187,11 +191,18 @@ impl<'de, T: Into + Deserialize<'de>> Deserialize<'de> for Keybinding } action = Some(map.next_value()); } + Field::ShowInHelp => { + if show_in_help.is_some() { + return Err(de::Error::duplicate_field("action")); + } + show_in_help = Some(map.next_value()); + } } } let on = on.ok_or_else(|| de::Error::missing_field("on"))?; let action = action.ok_or_else(|| de::Error::missing_field("action"))??; let modifier = modifier.transpose().unwrap(); + let show_in_help = show_in_help.transpose().unwrap().unwrap_or(true); if modifier.is_some() { if let KeyCode::Char(char) = on { @@ -203,7 +214,7 @@ impl<'de, T: Into + Deserialize<'de>> Deserialize<'de> for Keybinding } } - Ok(Keybinding::new(on, action, modifier)) + Ok(Keybinding::new(on, action, modifier, show_in_help)) } } @@ -305,6 +316,11 @@ impl KeymapConfig { keys.push(keybinding.keycode_string()); } } + for keybinding in &self.search_tab.keybindings { + if action == keybinding.action.into() { + keys.push(keybinding.keycode_string()); + } + } if keys.is_empty() { None diff --git a/rm-main/src/ui/global_popups/help.rs b/rm-main/src/ui/global_popups/help.rs index 57d2021..85e71df 100644 --- a/rm-main/src/ui/global_popups/help.rs +++ b/rm-main/src/ui/global_popups/help.rs @@ -59,15 +59,49 @@ impl HelpPopup { lines: &mut Vec, ) { let mut keys = BTreeMap::new(); + let mut max_len = 0; for keybinding in keybindings { + if !keybinding.show_in_help { + continue; + } + + let keycode = keybinding.keycode_string(); + if keycode.len() > max_len { + max_len = keycode.chars().count(); + } + keys.entry(&keybinding.action) .or_insert_with(Vec::new) .push(keybinding.keycode_string()); } + for (_, keycodes) in &keys { + let delimiter_len; + let mut keycodes_total_len = 0; + if keycodes.len() >= 2 { + delimiter_len = (keycodes.len() - 1) * 3; + } else { + delimiter_len = 0; + } + + for keycode in keycodes { + keycodes_total_len += keycode.chars().count(); + } + + if keycodes_total_len + delimiter_len > max_len { + max_len = keycodes_total_len + delimiter_len; + } + } + for (action, keycodes) in keys { - let keycode_string = keycodes.join(" / "); + let mut keycode_string = keycodes.join(" / "); + let mut how_much_to_pad = max_len - keycode_string.chars().count(); + while how_much_to_pad > 0 { + keycode_string.insert(0, ' '); + how_much_to_pad -= 1; + } + add_line!(lines, keycode_string, action.desc()); } } @@ -167,6 +201,19 @@ impl Component for HelpPopup { &mut lines, ); + lines.push( + Line::from(vec![Span::styled( + "Search Tab", + Style::default().bold().underlined(), + )]) + .centered(), + ); + + Self::write_keybindings( + &self.ctx.config.keybindings.search_tab.keybindings, + &mut lines, + ); + let help_text = Text::from(lines); if text_rect.height <= u16::try_from(help_text.lines.len()).unwrap() { diff --git a/rm-main/src/ui/tabs/search/bottom_bar.rs b/rm-main/src/ui/tabs/search/bottom_bar.rs index d8639b4..4f6e377 100644 --- a/rm-main/src/ui/tabs/search/bottom_bar.rs +++ b/rm-main/src/ui/tabs/search/bottom_bar.rs @@ -101,6 +101,24 @@ impl Component for SearchState { } fn render(&mut self, f: &mut Frame, rect: Rect) { + let append_key_info = |line: &mut Line| { + let providers_key = self + .ctx + .config + .keybindings + .get_keys_for_action(Action::ShowProvidersInfo); + if let Some(key) = providers_key { + line.push_span(Span::raw("Press ")); + line.push_span(Span::styled( + key, + Style::default() + .fg(self.ctx.config.general.accent_color) + .underlined(), + )); + line.push_span(Span::raw(" for details.")) + } + }; + match &mut self.stage { SearchStage::Nothing => (), SearchStage::Searching(ref mut state) => { @@ -116,14 +134,16 @@ impl Component for SearchState { SearchStage::NoResults => { let mut line = Line::default(); line.push_span(Span::styled("", Style::default().red())); - line.push_span(Span::raw(" No results")); + line.push_span(Span::raw(" No results.")); + append_key_info(&mut line); let paragraph = Paragraph::new(line); f.render_widget(paragraph, rect); } SearchStage::Found(count) => { let mut line = Line::default(); line.push_span(Span::styled("", Style::default().green())); - line.push_span(Span::raw(format!(" Found {count}"))); + line.push_span(Span::raw(format!(" Found {count}. "))); + append_key_info(&mut line); let paragraph = Paragraph::new(line); f.render_widget(paragraph, rect); } diff --git a/rm-main/src/ui/tabs/search/mod.rs b/rm-main/src/ui/tabs/search/mod.rs index f9d11c5..5225b3d 100644 --- a/rm-main/src/ui/tabs/search/mod.rs +++ b/rm-main/src/ui/tabs/search/mod.rs @@ -1,10 +1,7 @@ mod bottom_bar; mod popups; -use std::{ - borrow::{BorrowMut, Cow}, - sync::Arc, -}; +use std::{borrow::Cow, sync::Arc}; use bottom_bar::BottomBar; use crossterm::event::{KeyCode, KeyEvent};