keymap_ui: Show existing keystrokes as placeholders in edit modal (#34307)

Closes #ISSUE

Previously, the keystroke input would be empty, even when editing an
existing binding. This meant you had to re-enter the bindings even if
you just wanted to edit the context. Now, the existing keystrokes are
rendered as a placeholder, are re-shown if newly entered keystrokes are
cleared, and will be returned from the `KeystrokeInput::keystrokes()`
method if no new keystrokes were entered.

Additionally fixed a bug in `KeymapFile::update_keybinding` where
semantically identical contexts would be treated as unequal due to
formatting differences.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Ben Kunkle 2025-07-11 13:07:04 -05:00 committed by GitHub
parent 6f6c2915b2
commit 0797f7b66e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 54 additions and 13 deletions

View file

@ -783,8 +783,12 @@ impl KeymapFile {
target: &KeybindUpdateTarget<'a>,
target_action_value: &Value,
) -> Option<(usize, &'b str)> {
let target_context_parsed =
KeyBindingContextPredicate::parse(target.context.unwrap_or("")).ok();
for (index, section) in keymap.sections().enumerate() {
if section.context != target.context.unwrap_or("") {
let section_context_parsed =
KeyBindingContextPredicate::parse(&section.context).ok();
if section_context_parsed != target_context_parsed {
continue;
}
if section.use_key_equivalents != target.use_key_equivalents {
@ -835,6 +839,7 @@ pub enum KeybindUpdateOperation<'a> {
},
}
#[derive(Debug)]
pub struct KeybindUpdateTarget<'a> {
pub context: Option<&'a str>,
pub keystrokes: &'a [Keystroke],

View file

@ -283,7 +283,7 @@ impl KeymapEditor {
let table_interaction_state = TableInteractionState::new(window, cx);
let keystroke_editor = cx.new(|cx| {
let mut keystroke_editor = KeystrokeInput::new(window, cx);
let mut keystroke_editor = KeystrokeInput::new(None, window, cx);
keystroke_editor.highlight_on_focus = false;
keystroke_editor
});
@ -632,6 +632,11 @@ impl KeymapEditor {
Box::new(EditBinding),
)
.action("Create", Box::new(CreateBinding))
.action_disabled_when(
selected_binding_is_unbound,
"Delete",
Box::new(DeleteBinding),
)
.action("Copy action", Box::new(CopyAction))
.action_disabled_when(
selected_binding_has_no_context,
@ -1298,7 +1303,16 @@ impl KeybindingEditorModal {
window: &mut Window,
cx: &mut App,
) -> Self {
let keybind_editor = cx.new(|cx| KeystrokeInput::new(window, cx));
let keybind_editor = cx.new(|cx| {
KeystrokeInput::new(
editing_keybind
.ui_key_binding
.as_ref()
.map(|keybinding| keybinding.keystrokes.clone()),
window,
cx,
)
});
let context_editor = cx.new(|cx| {
let mut editor = Editor::single_line(window, cx);
@ -1802,6 +1816,7 @@ async fn remove_keybinding(
struct KeystrokeInput {
keystrokes: Vec<Keystroke>,
placeholder_keystrokes: Option<Vec<Keystroke>>,
highlight_on_focus: bool,
focus_handle: FocusHandle,
intercept_subscription: Option<Subscription>,
@ -1809,7 +1824,11 @@ struct KeystrokeInput {
}
impl KeystrokeInput {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
fn new(
placeholder_keystrokes: Option<Vec<Keystroke>>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let focus_handle = cx.focus_handle();
let _focus_subscriptions = [
cx.on_focus_in(&focus_handle, window, Self::on_focus_in),
@ -1817,6 +1836,7 @@ impl KeystrokeInput {
];
Self {
keystrokes: Vec::new(),
placeholder_keystrokes,
highlight_on_focus: true,
focus_handle,
intercept_subscription: None,
@ -1904,6 +1924,11 @@ impl KeystrokeInput {
}
fn keystrokes(&self) -> &[Keystroke] {
if let Some(placeholders) = self.placeholder_keystrokes.as_ref()
&& self.keystrokes.is_empty()
{
return placeholders;
}
if self
.keystrokes
.last()
@ -1913,6 +1938,25 @@ impl KeystrokeInput {
}
return &self.keystrokes;
}
fn render_keystrokes(&self) -> impl Iterator<Item = Div> {
let (keystrokes, color) = if let Some(placeholders) = self.placeholder_keystrokes.as_ref()
&& self.keystrokes.is_empty()
{
(placeholders, Color::Placeholder)
} else {
(&self.keystrokes, Color::Default)
};
keystrokes.iter().map(move |keystroke| {
h_flex().children(ui::render_keystroke(
keystroke,
Some(color),
Some(rems(0.875).into()),
ui::PlatformStyle::platform(),
false,
))
})
}
}
impl EventEmitter<()> for KeystrokeInput {}
@ -1958,15 +2002,7 @@ impl Render for KeystrokeInput {
.justify_center()
.flex_wrap()
.gap(ui::DynamicSpacing::Base04.rems(cx))
.children(self.keystrokes.iter().map(|keystroke| {
h_flex().children(ui::render_keystroke(
keystroke,
None,
Some(rems(0.875).into()),
ui::PlatformStyle::platform(),
false,
))
})),
.children(self.render_keystrokes()),
)
.child(
h_flex()