From 33f2783b0b51f60231f2dc71de33cf973a00fa12 Mon Sep 17 00:00:00 2001 From: Remigiusz Micielski Date: Mon, 26 Aug 2024 18:21:10 +0200 Subject: [PATCH] merge lines in help --- rm-config/src/keymap/actions/general.rs | 54 +++++++++++++++++++ rm-config/src/keymap/actions/mod.rs | 6 +++ rm-config/src/keymap/mod.rs | 71 ++++++++++++++++++++++++- rm-main/src/tui/global_popups/help.rs | 40 ++++++++++++-- 4 files changed, 166 insertions(+), 5 deletions(-) diff --git a/rm-config/src/keymap/actions/general.rs b/rm-config/src/keymap/actions/general.rs index c99cd84..9f8b380 100644 --- a/rm-config/src/keymap/actions/general.rs +++ b/rm-config/src/keymap/actions/general.rs @@ -27,7 +27,34 @@ pub enum GeneralAction { MoveToColumnRight, } +pub enum GeneralActionMergable { + MoveUpDown, + MoveLeftRight, + ScrollPageUpDown, + MoveColumnLeftRight, + SwitchToTorrentsSearch, +} + impl UserAction for GeneralAction { + fn is_mergable_with(&self, other: &GeneralAction) -> bool { + let other = *other; + match self { + GeneralAction::SwitchToTorrents => other == Self::SwitchToSearch, + GeneralAction::SwitchToSearch => other == Self::SwitchToTorrents, + GeneralAction::Left => other == Self::Right, + GeneralAction::Right => other == Self::Left, + GeneralAction::Down => other == Self::Up, + GeneralAction::Up => other == Self::Down, + GeneralAction::ScrollPageDown => other == Self::ScrollPageUp, + GeneralAction::ScrollPageUp => other == Self::ScrollPageDown, + GeneralAction::GoToBeginning => other == Self::GoToEnd, + GeneralAction::GoToEnd => other == Self::GoToBeginning, + GeneralAction::MoveToColumnLeft => other == Self::MoveToColumnRight, + GeneralAction::MoveToColumnRight => other == Self::MoveToColumnLeft, + _ => false, + } + } + fn desc(&self) -> &'static str { match self { GeneralAction::ShowHelp => "toggle help", @@ -52,6 +79,33 @@ impl UserAction for GeneralAction { GeneralAction::MoveToColumnLeft => "move to left column", } } + + fn merged_desc(&self, other: &GeneralAction) -> Option<&'static str> { + match (&self, other) { + (Self::Left, Self::Right) => Some("switch to tab left / right"), + (Self::Right, Self::Left) => Some("switch to tab right / left"), + (Self::Down, Self::Up) => Some("move down / up"), + (Self::Up, Self::Down) => Some("move up / down"), + (Self::SwitchToTorrents, Self::SwitchToSearch) => { + Some("switch to torrents / search tab") + } + (Self::SwitchToSearch, Self::SwitchToTorrents) => { + Some("switch to search / torrents tab") + } + (Self::MoveToColumnLeft, Self::MoveToColumnRight) => { + Some("move to column left / right") + } + (Self::MoveToColumnRight, Self::MoveToColumnLeft) => { + Some("move to column right / left") + } + (Self::ScrollPageDown, Self::ScrollPageUp) => Some("scroll page down / up"), + (Self::ScrollPageUp, Self::ScrollPageDown) => Some("scroll page up / down"), + (Self::GoToBeginning, Self::GoToEnd) => Some("go to beginning / end"), + (Self::GoToEnd, Self::GoToBeginning) => Some("go to end / beginning"), + + _ => None, + } + } } impl From for Action { diff --git a/rm-config/src/keymap/actions/mod.rs b/rm-config/src/keymap/actions/mod.rs index 3436448..044f687 100644 --- a/rm-config/src/keymap/actions/mod.rs +++ b/rm-config/src/keymap/actions/mod.rs @@ -6,4 +6,10 @@ pub mod torrents_tab; pub trait UserAction: Into { fn desc(&self) -> &'static str; + fn merged_desc(&self, other: &Self) -> Option<&'static str> { + None + } + fn is_mergable_with(&self, other: &Self) -> bool { + false + } } diff --git a/rm-config/src/keymap/mod.rs b/rm-config/src/keymap/mod.rs index f7c3882..6e276a0 100644 --- a/rm-config/src/keymap/mod.rs +++ b/rm-config/src/keymap/mod.rs @@ -1,10 +1,14 @@ pub mod actions; use std::{ - collections::HashMap, io::ErrorKind, marker::PhantomData, path::PathBuf, sync::OnceLock, + collections::{BTreeMap, HashMap}, + io::ErrorKind, + marker::PhantomData, + path::PathBuf, + sync::OnceLock, }; -use actions::search_tab::SearchAction; +use actions::{search_tab::SearchAction, UserAction}; use anyhow::{Context, Result}; use crossterm::event::{KeyCode, KeyModifiers as CrosstermKeyModifiers}; use serde::{ @@ -36,6 +40,8 @@ pub struct KeymapConfig { #[derive(Serialize, Deserialize, Clone)] pub struct KeybindsHolder> { pub keybindings: Vec>, + #[serde(skip)] + pub help_repr: Vec<(String, &'static str)>, } #[derive(Serialize, Clone)] @@ -314,6 +320,7 @@ impl KeymapConfig { keys.push(keybinding.keycode_string()); } } + for keybinding in &self.torrents_tab.keybindings { if action == keybinding.action.into() { keys.push(keybinding.keycode_string()); @@ -350,6 +357,66 @@ impl KeymapConfig { } } + fn populate_help_repr(&mut self) { + fn get_keybindings + UserAction + Ord>( + keybindings: &[Keybinding], + max_len: &mut usize, + ) -> Vec<(String, &'static str)> { + let mut keys: BTreeMap<&T, Vec> = BTreeMap::new(); + + 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.values() { + let mut keycodes_total_len = 0; + let delimiter_len = if keycodes.len() >= 2 { + (keycodes.len() - 1) * 3 + } else { + 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; + } + } + + let mut res = vec![]; + for (action, keycodes) in keys { + let keycode_string = keycodes.join(" / "); + let desc = action.desc(); + res.push((keycode_string, desc)); + } + + res + } + + let mut max_len = 0; + // let global_keys_deduped = self + // .general + // .keybindings + // .clone() + // .dedup_by(|a, b| a.action.is_mergable_with(b.action)); + let mut global_keys = get_keybindings(&self.general.keybindings, &mut max_len); + let mut torrents_tab = get_keybindings(&self.torrents_tab.keybindings, &mut max_len); + let mut search_tab = get_keybindings(&self.search_tab.keybindings, &mut max_len); + } + pub fn path() -> &'static PathBuf { static PATH: OnceLock = OnceLock::new(); PATH.get_or_init(|| utils::get_config_path(Self::FILENAME)) diff --git a/rm-main/src/tui/global_popups/help.rs b/rm-main/src/tui/global_popups/help.rs index 1e6edc6..a335589 100644 --- a/rm-main/src/tui/global_popups/help.rs +++ b/rm-main/src/tui/global_popups/help.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::{collections::BTreeMap, str::FromStr}; use ratatui::{ prelude::*, @@ -90,9 +90,43 @@ impl HelpPopup { } } - let mut res = vec![]; + let mut new_keys = vec![]; + for (action, keycodes) in keys { - let keycode_string = keycodes.join(" / "); + new_keys.push((action, keycodes)); + } + + let mut res = vec![]; + let mut skip_next_loop = false; + for (idx, (action, keycodes)) in new_keys.iter().enumerate() { + if skip_next_loop { + skip_next_loop = false; + continue; + } + + if idx < new_keys.len().saturating_sub(1) { + if action.is_mergable_with(new_keys[idx + 1].0) { + skip_next_loop = true; + let keys = format!( + "{} / {}", + keycodes.join(", "), + new_keys[idx + 1].1.join(", ") + ); + + if keys.chars().count() > *max_len { + *max_len = keys.chars().count(); + } + + let desc = action.merged_desc(new_keys[idx + 1].0).unwrap(); + res.push((keys, desc)); + continue; + } + } + + let keycode_string = keycodes.join(", "); + if keycode_string.chars().count() > *max_len { + *max_len = keycode_string.chars().count(); + } let desc = action.desc(); res.push((keycode_string, desc)); }