diff --git a/wezterm-gui/src/commands.rs b/wezterm-gui/src/commands.rs index 906dd685c..61edaf563 100644 --- a/wezterm-gui/src/commands.rs +++ b/wezterm-gui/src/commands.rs @@ -316,7 +316,7 @@ impl CommandDef { // And sweep to pick up stuff from their key assignments let inputmap = InputMap::new(config); - for entry in inputmap.keys.default.values() { + for ((keycode, mods), entry) in inputmap.keys.default.iter() { if result .iter() .position(|cmd| cmd.action == entry.action) @@ -328,7 +328,7 @@ impl CommandDef { result.push(ExpandedCommand { brief: cmd.brief.into(), doc: cmd.doc.into(), - keys: vec![], + keys: vec![(*mods, keycode.clone())], action: entry.action.clone(), menubar: cmd.menubar, icon: cmd.icon, diff --git a/wezterm-gui/src/inputmap.rs b/wezterm-gui/src/inputmap.rs index ce90b0e99..5cecade3e 100644 --- a/wezterm-gui/src/inputmap.rs +++ b/wezterm-gui/src/inputmap.rs @@ -572,7 +572,24 @@ fn section_header(title: &str) { println!(); } -fn human_key(key: &KeyCode) -> String { +pub fn ui_key(key: &KeyCode) -> String { + match key { + KeyCode::Char('\x1b') => "Esc".to_string(), + KeyCode::Char('\x7f') => "Esc".to_string(), + KeyCode::Char('\x08') => "Del".to_string(), + KeyCode::Char('\r') => "Enter".to_string(), + KeyCode::Char(' ') => "Space".to_string(), + KeyCode::Char('\t') => "Tab".to_string(), + KeyCode::Char(c) if c.is_ascii_control() => c.escape_debug().to_string(), + KeyCode::Char(c) => c.to_uppercase().to_string(), + KeyCode::Function(n) => format!("F{n}"), + KeyCode::Numpad(n) => format!("Numpad{n}"), + KeyCode::Physical(phys) => phys.to_string(), + _ => format!("{key:?}"), + } +} + +pub fn human_key(key: &KeyCode) -> String { match key { KeyCode::Char('\x1b') => "Escape".to_string(), KeyCode::Char('\x7f') => "Escape".to_string(), diff --git a/wezterm-gui/src/termwindow/palette.rs b/wezterm-gui/src/termwindow/palette.rs index d68023a1b..0319b64af 100644 --- a/wezterm-gui/src/termwindow/palette.rs +++ b/wezterm-gui/src/termwindow/palette.rs @@ -230,28 +230,27 @@ impl CommandPalette { None => &' ', }; + let solid_bg_color: InheritableColor = term_window + .config + .command_palette_bg_color + .to_linear() + .into(); + let solid_fg_color: InheritableColor = term_window + .config + .command_palette_fg_color + .to_linear() + .into(); + let (bg, text) = if display_idx == selected_row { - ( - term_window - .config - .command_palette_fg_color - .to_linear() - .into(), - term_window - .config - .command_palette_bg_color - .to_linear() - .into(), - ) + (solid_fg_color.clone(), solid_bg_color.clone()) } else { - ( - LinearRgba::TRANSPARENT.into(), - term_window - .config - .command_palette_fg_color - .to_linear() - .into(), - ) + (LinearRgba::TRANSPARENT.into(), solid_fg_color.clone()) + }; + + let (label_bg, label_text) = if display_idx == selected_row { + (solid_fg_color.clone(), solid_bg_color.clone()) + } else { + (solid_bg_color.clone(), solid_fg_color.clone()) }; // DRY if the brief and doc are the same @@ -263,6 +262,18 @@ impl CommandPalette { format!("{group}{}. {}", command.brief, command.doc) }; + let key_label = if command.keys.is_empty() { + String::new() + } else { + let (mods, keycode) = &command.keys[0]; + let mut mod_string = mods.to_string_with_separator("-", false, true); + if !mod_string.is_empty() { + mod_string.push_str("-"); + } + let keycode = crate::inputmap::ui_key(keycode); + format!("{mod_string}{keycode}") + }; + elements.push( Element::new( &font, @@ -270,6 +281,20 @@ impl CommandPalette { Element::new(&font, ElementContent::Text(icon.to_string())) .min_width(Some(Dimension::Cells(2.))), Element::new(&font, ElementContent::Text(label)), + Element::new(&font, ElementContent::Text(key_label)) + .float(Float::Right) + .padding(BoxDimension { + left: Dimension::Cells(1.25), + right: Dimension::Cells(0.5), + top: Dimension::Cells(0.), + bottom: Dimension::Cells(0.), + }) + .zindex(10) + .colors(ElementColors { + border: BorderColor::default(), + bg: label_bg.clone(), + text: label_text.clone(), + }), ]), ) .colors(ElementColors { @@ -292,7 +317,7 @@ impl CommandPalette { let size = term_window.terminal_size; // Avoid covering the entire width - let desired_width = (size.cols / 3).max(75).min(size.cols); + let desired_width = (size.cols / 3).max(120).min(size.cols); // Center it let avail_pixel_width = diff --git a/wezterm-input-types/src/lib.rs b/wezterm-input-types/src/lib.rs index 7c92359d5..1f5edbc53 100644 --- a/wezterm-input-types/src/lib.rs +++ b/wezterm-input-types/src/lib.rs @@ -506,32 +506,52 @@ impl Into for &Modifiers { } } -impl ToString for Modifiers { - fn to_string(&self) -> String { +impl Modifiers { + pub fn to_string_with_separator(&self, separator: &str, want_none: bool, ui: bool) -> String { let mut s = String::new(); - if *self == Self::NONE { + if want_none && *self == Self::NONE { s.push_str("NONE"); } for (value, label) in [ - (Self::SHIFT, "SHIFT"), - (Self::ALT, "ALT"), - (Self::CTRL, "CTRL"), - (Self::SUPER, "SUPER"), - (Self::LEFT_ALT, "LEFT_ALT"), - (Self::RIGHT_ALT, "RIGHT_ALT"), - (Self::LEADER, "LEADER"), - (Self::LEFT_CTRL, "LEFT_CTRL"), - (Self::RIGHT_CTRL, "RIGHT_CTRL"), - (Self::LEFT_SHIFT, "LEFT_SHIFT"), - (Self::RIGHT_SHIFT, "RIGHT_SHIFT"), + (Self::SHIFT, if ui { "Shift" } else { "SHIFT" }), + ( + Self::ALT, + if ui && cfg!(target_os = "macos") { + "Opt" + } else if ui { + "Alt" + } else { + "ALT" + }, + ), + (Self::CTRL, if ui { "Ctrl" } else { "CTRL" }), + ( + Self::SUPER, + if ui && cfg!(target_os = "macos") { + "Cmd" + } else if ui && cfg!(windows) { + "Win" + } else if ui { + "Super" + } else { + "SUPER" + }, + ), + (Self::LEFT_ALT, if ui { "LAlt" } else { "LEFT_ALT" }), + (Self::RIGHT_ALT, if ui { "RAlt" } else { "RIGHT_ALT" }), + (Self::LEADER, if ui { "Leader" } else { "LEADER" }), + (Self::LEFT_CTRL, if ui { "LCtrl" } else { "LEFT_CTRL" }), + (Self::RIGHT_CTRL, if ui { "RCtrl" } else { "RIGHT_CTRL" }), + (Self::LEFT_SHIFT, if ui { "LShift" } else { "LEFT_SHIFT" }), + (Self::RIGHT_SHIFT, if ui { "RShift" } else { "RIGHT_SHIFT" }), (Self::ENHANCED_KEY, "ENHANCED_KEY"), ] { if !self.contains(value) { continue; } if !s.is_empty() { - s.push('|'); + s.push_str(separator); } s.push_str(label); } @@ -540,6 +560,12 @@ impl ToString for Modifiers { } } +impl ToString for Modifiers { + fn to_string(&self) -> String { + self.to_string_with_separator("|", true, false) + } +} + impl Modifiers { /// Remove positional and other "supplemental" bits that /// are used to carry around implementation details, but that