diff --git a/src/help.rs b/src/help.rs index 2253f5e..5d7ac32 100644 --- a/src/help.rs +++ b/src/help.rs @@ -10,13 +10,17 @@ use ratatui::{ }, }; -use crate::{app::ColorMode, config::Config}; +use crate::{ + app::ColorMode, + config::Config, + keybindings::{Keybinding, build_keybindings}, +}; #[derive(Debug)] pub struct Help { block_height: usize, state: TableState, - keys: Vec<(Cell<'static>, &'static str)>, + keys: Vec, } impl Help { @@ -27,79 +31,7 @@ impl Help { Self { block_height: 0, state, - keys: vec![ - ( - Cell::from("## Global").style(Style::new().bold().fg(Color::Yellow)), - "", - ), - (Cell::from("Esc").bold(), "Dismiss different pop-ups"), - ( - Cell::from("Tab or h/l").bold(), - "Switch between different sections", - ), - (Cell::from("j or Down").bold(), "Scroll down"), - (Cell::from("k or Up").bold(), "Scroll up"), - ( - Cell::from(config.toggle_scanning.to_string()).bold(), - "Start/Stop scanning", - ), - (Cell::from("?").bold(), "Show help"), - (Cell::from("ctrl+c or q").bold(), "Quit"), - (Cell::from(""), ""), - ( - Cell::from("## Adapters").style(Style::new().bold().fg(Color::Yellow)), - "", - ), - ( - Cell::from(config.adapter.toggle_pairing.to_string()).bold(), - "Enable/Disable the pairing", - ), - ( - Cell::from(config.adapter.toggle_power.to_string()).bold(), - "Power on/off the adapter", - ), - ( - Cell::from(config.adapter.toggle_discovery.to_string()).bold(), - "Enable/Disable the discovery", - ), - (Cell::from(""), ""), - ( - Cell::from("## Paired devices").style(Style::new().bold().fg(Color::Yellow)), - "", - ), - ( - Cell::from(config.paired_device.unpair.to_string()).bold(), - "Unpair the device", - ), - ( - Cell::from({ - if config.paired_device.toggle_connect == ' ' { - "Space".to_string() - } else { - config.paired_device.toggle_connect.to_string() - } - }) - .bold(), - "Connect/Disconnect the device", - ), - ( - Cell::from(config.paired_device.toggle_trust.to_string()).bold(), - "Trust/Untrust the device", - ), - ( - Cell::from(config.paired_device.rename.to_string()).bold(), - "Rename the device", - ), - (Cell::from(""), ""), - ( - Cell::from("## New devices").style(Style::default().bold().fg(Color::Yellow)), - "", - ), - ( - Cell::from(config.new_device.pair.to_string()).bold(), - "Pair the device", - ), - ], + keys: build_keybindings(&config), } } @@ -153,8 +85,15 @@ impl Help { let rows: Vec = self .keys .iter() - .map(|key| { - Row::new(vec![key.0.to_owned(), key.1.into()]).style(match color_mode { + .map(|keybinding| { + let key_cell = if keybinding.is_section { + Cell::from(keybinding.key.clone()) + .style(Style::default().bold().fg(Color::Yellow)) + } else { + Cell::from(keybinding.key.clone()).style(Style::default().bold()) + }; + let description_cell = Cell::from(keybinding.description); + Row::new(vec![key_cell, description_cell]).style(match color_mode { ColorMode::Dark => Style::default().fg(Color::White), ColorMode::Light => Style::default().fg(Color::Black), }) diff --git a/src/keybindings.rs b/src/keybindings.rs new file mode 100644 index 0000000..a819eaa --- /dev/null +++ b/src/keybindings.rs @@ -0,0 +1,148 @@ +use crate::config::Config; + +#[derive(Debug)] +pub struct Keybinding { + pub key: String, + pub description: &'static str, + pub is_section: bool, +} + +pub fn build_keybindings(config: &Config) -> Vec { + let human_readable_key = |c: char| match c { + ' ' => "Space".to_string(), + '\n' => "Enter".to_string(), + '\t' => "Tab".to_string(), + other => other.to_string(), + }; + + let keys = vec![ + Keybinding { + key: "## Global".into(), + description: "", + is_section: true, + }, + Keybinding { + key: "Esc".into(), + description: "Dismiss different pop-ups", + is_section: false, + }, + Keybinding { + key: "Tab or h/l".into(), + description: "Switch between different sections", + is_section: false, + }, + Keybinding { + key: "j or Down".into(), + description: "Scroll down", + is_section: false, + }, + Keybinding { + key: "k or Up".into(), + description: "Scroll up", + is_section: false, + }, + Keybinding { + key: human_readable_key(config.toggle_scanning), + description: "Start/Stop scanning", + is_section: false, + }, + Keybinding { + key: "?".into(), + description: "Show help", + is_section: false, + }, + Keybinding { + key: "ctrl+c or q".into(), + description: "Quit", + is_section: false, + }, + Keybinding { + key: "".into(), + description: "", + is_section: false, + }, + Keybinding { + key: "## Adapters".into(), + description: "", + is_section: true, + }, + Keybinding { + key: human_readable_key(config.adapter.toggle_pairing), + description: "Enable/Disable the pairing", + is_section: false, + }, + Keybinding { + key: human_readable_key(config.adapter.toggle_power), + description: "Power on/off the adapter", + is_section: false, + }, + Keybinding { + key: human_readable_key(config.adapter.toggle_discovery), + description: "Enable/Disable the discovery", + is_section: false, + }, + Keybinding { + key: "".into(), + description: "", + is_section: false, + }, + Keybinding { + key: "## Paired devices".into(), + description: "", + is_section: true, + }, + Keybinding { + key: human_readable_key(config.paired_device.unpair), + description: "Unpair the device", + is_section: false, + }, + Keybinding { + key: human_readable_key(config.paired_device.toggle_connect), + description: "Connect/Disconnect the device", + is_section: false, + }, + Keybinding { + key: human_readable_key(config.paired_device.toggle_trust), + description: "Trust/Untrust the device", + is_section: false, + }, + Keybinding { + key: human_readable_key(config.paired_device.rename), + description: "Rename the device", + is_section: false, + }, + Keybinding { + key: "".into(), + description: "", + is_section: false, + }, + Keybinding { + key: "## New devices".into(), + description: "", + is_section: true, + }, + Keybinding { + key: human_readable_key(config.new_device.pair), + description: "Pair the device", + is_section: false, + }, + ]; + + keys +} + +pub fn keybindings_string(config: &Config) -> String { + let bindings = build_keybindings(config); + let mut s = String::from("Hotkeys:\n\n"); + + for kb in bindings { + if kb.is_section { + s.push_str(&format!("{}\n", kb.key)); + } else if kb.key.is_empty() && kb.description.is_empty() { + s.push('\n'); + } else { + s.push_str(&format!(" {:18} {}\n", kb.key, kb.description)); + } + } + s +} diff --git a/src/lib.rs b/src/lib.rs index 11f4851..3ed9885 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,3 +21,5 @@ pub mod config; pub mod rfkill; pub mod confirmation; + +pub mod keybindings; diff --git a/src/main.rs b/src/main.rs index dd913e0..9d98a06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use bluetui::{ config::Config, event::{Event, EventHandler}, handler::handle_key_events, + keybindings::keybindings_string, rfkill, tui::Tui, }; @@ -12,13 +13,14 @@ use std::{io, sync::Arc}; #[tokio::main] async fn main() -> AppResult<()> { + let config = Arc::new(Config::new()); Command::new("bluetui") .version(crate_version!()) + .after_help(keybindings_string(&config)) .get_matches(); rfkill::check()?; - let config = Arc::new(Config::new()); let mut app = App::new(config.clone()).await?; let backend = CrosstermBackend::new(io::stdout()); let terminal = Terminal::new(backend)?;