From bb68e505707c6cb99b2411be15960a6187a2799d Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 28 Dec 2023 08:45:45 +0000 Subject: [PATCH] Store an OurKey in FileStatusLine and MenuKeypressLine. Now we have a representation for keystrokes that can trigger actions, this is the first step towards a DRY representation in which each menu and status line configures just once the a set of triples (keypress, help text, action to take). Not sure what form the 'action' part of that triple will take yet, though. --- src/text.rs | 77 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/src/text.rs b/src/text.rs index 1cd40b4..33f834c 100644 --- a/src/text.rs +++ b/src/text.rs @@ -6,6 +6,7 @@ use std::collections::{HashMap, BTreeMap, BTreeSet}; use unicode_width::UnicodeWidthStr; use super::html; +use super::tui::OurKey; use super::coloured_string::{ColouredString, ColouredStringSlice}; pub trait TextFragment { @@ -1087,11 +1088,31 @@ fn test_media() { }); } -type Key = String; // FIXME: maybe make this a richer type later so we - // can match it against actual keypresses +fn key_to_string(key: OurKey) -> String { + match key { + // Menu and file keypresses are case insensitive, and the UI shows + // them in uppercase always + OurKey::Pr(c) => c.to_uppercase().to_string(), + OurKey::Ctrl(c) => format!("^{}", c), + OurKey::FunKey(n) => format!("F{}", n), + OurKey::Backspace => "BS".to_string(), // likely won't come up + OurKey::Return => "RET".to_string(), + OurKey::Escape => "ESC".to_string(), + OurKey::Up => "Up".to_string(), + OurKey::Down => "Down".to_string(), + OurKey::Left => "Left".to_string(), + OurKey::Right => "Right".to_string(), + OurKey::PgUp => "PgUp".to_string(), + OurKey::PgDn => "PgDn".to_string(), + OurKey::Home => "Home".to_string(), + OurKey::End => "End".to_string(), + OurKey::Ins => "Ins".to_string(), + OurKey::Del => "Del".to_string(), + } +} struct Keypress { - key: Key, + key: OurKey, description: ColouredString, } @@ -1127,7 +1148,8 @@ impl FileStatusLine { self } - pub fn add(mut self, key: Key, description: &str, priority: usize) -> Self { + pub fn add(mut self, key: OurKey, description: &str, + priority: usize) -> Self { self.keypresses.push((Keypress { key: key, description: ColouredString::plain(description) @@ -1139,8 +1161,8 @@ impl FileStatusLine { let mut bypri = BTreeMap::new(); for (kp, pri) in &self.keypresses { // [key]:desc - let key: &str = &kp.key; - let width = UnicodeWidthStr::width(key) + + let key = key_to_string(kp.key); + let width = UnicodeWidthStr::width(&key as &str) + kp.description.width() + 3; if let Some(priwidth) = bypri.get_mut(pri) { @@ -1184,23 +1206,23 @@ fn test_filestatus_build() { assert_eq!(fs.message, Some("hello, world".to_owned())); let fsf = FileStatusLine::new() - .add("A".to_owned(), "Annoy", 10) + .add(OurKey::Pr('A'), "Annoy", 10) .finalise(); assert_eq!(fsf.priwidth, vec! { (10, 9), }); let fsf = FileStatusLine::new() - .add("A".to_owned(), "Annoy", 10) - .add("B".to_owned(), "Badger", 10) + .add(OurKey::Pr('A'), "Annoy", 10) + .add(OurKey::Pr('B'), "Badger", 10) .finalise(); assert_eq!(fsf.priwidth, vec! { (10, 9 + 10 + FileStatusLine::SPACING), }); let fsf = FileStatusLine::new() - .add("A".to_owned(), "Annoy", 10) - .add("B".to_owned(), "Badger", 5) + .add(OurKey::Pr('A'), "Annoy", 10) + .add(OurKey::Pr('B'), "Badger", 5) .finalise(); assert_eq!(fsf.priwidth, vec! { (10, 9), @@ -1208,7 +1230,7 @@ fn test_filestatus_build() { }); let fsf = FileStatusLine::new() - .add("A".to_owned(), "Annoy", 10) + .add(OurKey::Pr('A'), "Annoy", 10) .finalise(); assert_eq!(fsf.priwidth, vec! { (10, 9), @@ -1216,7 +1238,7 @@ fn test_filestatus_build() { let fsf = FileStatusLine::new() .set_proportion(2, 3) - .add("A".to_owned(), "Annoy", 10) + .add(OurKey::Pr('A'), "Annoy", 10) .finalise(); assert_eq!(fsf.priwidth, vec! { (10, 9 + FileStatusLine::SPACING), @@ -1224,7 +1246,7 @@ fn test_filestatus_build() { let fsf = FileStatusLine::new() .message("aha") - .add("A".to_owned(), "Annoy", 10) + .add(OurKey::Pr('A'), "Annoy", 10) .finalise(); assert_eq!(fsf.priwidth, vec! { (10, 9 + FileStatusLine::SPACING), @@ -1267,7 +1289,8 @@ impl TextFragment for FileStatusLineFinal { for (kp, pri) in &self.fs.keypresses { if *pri >= minpri { let mut ckey = ColouredString::plain("["); - let ckp = ColouredString::uniform(&kp.key, 'k'); + let ckp = ColouredString::uniform( + &key_to_string(kp.key), 'k'); ckey.push_str(&ckp.slice()); ckey.push_str(&ColouredString::plain("]:").slice()); ckey.push_str(&kp.description.slice()); @@ -1295,10 +1318,10 @@ fn test_filestatus_render() { let fs = FileStatusLine::new() .message("stoat") .set_proportion(34, 55) - .add("A".to_owned(), "Annoy", 10) - .add("B".to_owned(), "Badger", 10) - .add("C".to_owned(), "Critical", 1000) - .add("D".to_owned(), "Dull", 1) + .add(OurKey::Pr('a'), "Annoy", 10) + .add(OurKey::Pr('b'), "Badger", 10) + .add(OurKey::Pr('c'), "Critical", 1000) + .add(OurKey::Pr('d'), "Dull", 1) .into_box(); assert_eq!(fs.render(80), vec! { @@ -1343,7 +1366,7 @@ fn test_filestatus_render() { let fs = FileStatusLine::new() .set_proportion(1, 11) - .add("K".to_owned(), "Keypress", 10) + .add(OurKey::Pr('K'), "Keypress", 10) .into_box(); assert_eq!(fs.render(19), vec! { @@ -1360,7 +1383,7 @@ fn test_filestatus_render() { let fs = FileStatusLine::new() .message("weasel") - .add("K".to_owned(), "Keypress", 10) + .add(OurKey::Pr('K'), "Keypress", 10) .into_box(); assert_eq!(fs.render(21), vec! { @@ -1384,8 +1407,11 @@ pub struct MenuKeypressLine { } impl MenuKeypressLine { - pub fn new(key: Key, description: ColouredString) -> Box { - let lwid = UnicodeWidthStr::width(&key as &str) + 2; // [] around it + pub fn new(key: OurKey, description: ColouredString) + -> Box { + // +2 for [] around the key name + let lwid = UnicodeWidthStr::width(&key_to_string(key) as &str) + 2; + let rwid = description.width(); Box::new(MenuKeypressLine { keypress: Keypress { @@ -1415,9 +1441,10 @@ impl TextFragment for MenuKeypressLine { let llspace = lspace / 2; let lrspace = lspace - llspace; + let keydesc = key_to_string(self.keypress.key); let line = ColouredString::plain(" ").repeat(leftpad + llspace) + ColouredString::plain("[") + - ColouredString::uniform(&self.keypress.key, 'k') + + ColouredString::uniform(&keydesc, 'k') + ColouredString::plain("]") + ColouredString::plain(" ").repeat(lrspace) + ColouredString::plain(" = ") + @@ -1435,7 +1462,7 @@ impl TextFragment for MenuKeypressLine { #[test] fn test_menu_keypress() { - let mut mk = MenuKeypressLine::new("S".to_owned(), ColouredString::general( + let mut mk = MenuKeypressLine::new(OurKey::Pr('S'), ColouredString::general( "Something or other", "K ")); -- 2.30.2