keymap_ui: Keyboard navigation for keybind edit modal (#34482)
Adds keyboard navigation to the keybind edit modal. Using up/down arrows to select the previous/next input editor, and `cmd-enter` to save + `escape` to exit Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
parent
3ecdfc9b5a
commit
ebbf02e25b
3 changed files with 228 additions and 97 deletions
|
@ -1129,5 +1129,21 @@
|
||||||
"escape escape escape": "keystroke_input::StopRecording",
|
"escape escape escape": "keystroke_input::StopRecording",
|
||||||
"delete": "keystroke_input::ClearKeystrokes"
|
"delete": "keystroke_input::ClearKeystrokes"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "KeybindEditorModal",
|
||||||
|
"use_key_equivalents": true,
|
||||||
|
"bindings": {
|
||||||
|
"ctrl-enter": "menu::Confirm",
|
||||||
|
"escape": "menu::Cancel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "KeybindEditorModal > Editor",
|
||||||
|
"use_key_equivalents": true,
|
||||||
|
"bindings": {
|
||||||
|
"up": "menu::SelectPrevious",
|
||||||
|
"down": "menu::SelectNext"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1226,5 +1226,21 @@
|
||||||
"escape escape escape": "keystroke_input::StopRecording",
|
"escape escape escape": "keystroke_input::StopRecording",
|
||||||
"delete": "keystroke_input::ClearKeystrokes"
|
"delete": "keystroke_input::ClearKeystrokes"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "KeybindEditorModal",
|
||||||
|
"use_key_equivalents": true,
|
||||||
|
"bindings": {
|
||||||
|
"cmd-enter": "menu::Confirm",
|
||||||
|
"escape": "menu::Cancel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"context": "KeybindEditorModal > Editor",
|
||||||
|
"use_key_equivalents": true,
|
||||||
|
"bindings": {
|
||||||
|
"up": "menu::SelectPrevious",
|
||||||
|
"down": "menu::SelectNext"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1451,6 +1451,7 @@ struct KeybindingEditorModal {
|
||||||
error: Option<InputError>,
|
error: Option<InputError>,
|
||||||
keymap_editor: Entity<KeymapEditor>,
|
keymap_editor: Entity<KeymapEditor>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
|
focus_state: KeybindingEditorModalFocusState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModalView for KeybindingEditorModal {}
|
impl ModalView for KeybindingEditorModal {}
|
||||||
|
@ -1539,6 +1540,14 @@ impl KeybindingEditorModal {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let focus_state = KeybindingEditorModalFocusState::new(
|
||||||
|
keybind_editor.read_with(cx, |keybind_editor, cx| keybind_editor.focus_handle(cx)),
|
||||||
|
input_editor.as_ref().map(|input_editor| {
|
||||||
|
input_editor.read_with(cx, |input_editor, cx| input_editor.focus_handle(cx))
|
||||||
|
}),
|
||||||
|
context_editor.read_with(cx, |context_editor, cx| context_editor.focus_handle(cx)),
|
||||||
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
creating: create,
|
creating: create,
|
||||||
editing_keybind,
|
editing_keybind,
|
||||||
|
@ -1550,6 +1559,7 @@ impl KeybindingEditorModal {
|
||||||
error: None,
|
error: None,
|
||||||
keymap_editor,
|
keymap_editor,
|
||||||
workspace,
|
workspace,
|
||||||
|
focus_state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1731,6 +1741,33 @@ impl KeybindingEditorModal {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn key_context(&self) -> KeyContext {
|
||||||
|
let mut key_context = KeyContext::new_with_defaults();
|
||||||
|
key_context.add("KeybindEditorModal");
|
||||||
|
key_context
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_next(&mut self, _: &menu::SelectNext, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.focus_state.focus_next(window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_prev(
|
||||||
|
&mut self,
|
||||||
|
_: &menu::SelectPrevious,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
self.focus_state.focus_previous(window, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.save(cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
cx.emit(DismissEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for KeybindingEditorModal {
|
impl Render for KeybindingEditorModal {
|
||||||
|
@ -1739,7 +1776,15 @@ impl Render for KeybindingEditorModal {
|
||||||
let action_name =
|
let action_name =
|
||||||
command_palette::humanize_action_name(&self.editing_keybind.action_name).to_string();
|
command_palette::humanize_action_name(&self.editing_keybind.action_name).to_string();
|
||||||
|
|
||||||
v_flex().w(rems(34.)).elevation_3(cx).child(
|
v_flex()
|
||||||
|
.w(rems(34.))
|
||||||
|
.elevation_3(cx)
|
||||||
|
.key_context(self.key_context())
|
||||||
|
.on_action(cx.listener(Self::focus_next))
|
||||||
|
.on_action(cx.listener(Self::focus_prev))
|
||||||
|
.on_action(cx.listener(Self::confirm))
|
||||||
|
.on_action(cx.listener(Self::cancel))
|
||||||
|
.child(
|
||||||
Modal::new("keybinding_editor_modal", None)
|
Modal::new("keybinding_editor_modal", None)
|
||||||
.header(
|
.header(
|
||||||
ModalHeader::new().child(
|
ModalHeader::new().child(
|
||||||
|
@ -1829,6 +1874,61 @@ impl Render for KeybindingEditorModal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct KeybindingEditorModalFocusState {
|
||||||
|
handles: Vec<FocusHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeybindingEditorModalFocusState {
|
||||||
|
fn new(
|
||||||
|
keystrokes: FocusHandle,
|
||||||
|
action_input: Option<FocusHandle>,
|
||||||
|
context: FocusHandle,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
handles: Vec::from_iter(
|
||||||
|
[Some(keystrokes), action_input, Some(context)]
|
||||||
|
.into_iter()
|
||||||
|
.flatten(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focused_index(&self, window: &Window, cx: &App) -> Option<i32> {
|
||||||
|
self.handles
|
||||||
|
.iter()
|
||||||
|
.position(|handle| handle.contains_focused(window, cx))
|
||||||
|
.map(|i| i as i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_index(&self, mut index: i32, window: &mut Window) {
|
||||||
|
if index < 0 {
|
||||||
|
index = self.handles.len() as i32 - 1;
|
||||||
|
}
|
||||||
|
if index >= self.handles.len() as i32 {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
window.focus(&self.handles[index as usize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_next(&self, window: &mut Window, cx: &App) {
|
||||||
|
let index_to_focus = if let Some(index) = self.focused_index(window, cx) {
|
||||||
|
index + 1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
self.focus_index(index_to_focus, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_previous(&self, window: &mut Window, cx: &App) {
|
||||||
|
let index_to_focus = if let Some(index) = self.focused_index(window, cx) {
|
||||||
|
index - 1
|
||||||
|
} else {
|
||||||
|
self.handles.len() as i32 - 1
|
||||||
|
};
|
||||||
|
self.focus_index(index_to_focus, window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct KeyContextCompletionProvider {
|
struct KeyContextCompletionProvider {
|
||||||
contexts: Vec<SharedString>,
|
contexts: Vec<SharedString>,
|
||||||
}
|
}
|
||||||
|
@ -2207,9 +2307,7 @@ impl KeystrokeInput {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let close_keystroke_result = self.handle_possible_close_keystroke(keystroke, window, cx);
|
let close_keystroke_result = self.handle_possible_close_keystroke(keystroke, window, cx);
|
||||||
if close_keystroke_result == CloseKeystrokeResult::Close {
|
if close_keystroke_result != CloseKeystrokeResult::Close {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let Some(last) = self.keystrokes.last()
|
if let Some(last) = self.keystrokes.last()
|
||||||
&& last.key.is_empty()
|
&& last.key.is_empty()
|
||||||
&& self.keystrokes.len() <= Self::KEYSTROKE_COUNT_MAX
|
&& self.keystrokes.len() <= Self::KEYSTROKE_COUNT_MAX
|
||||||
|
@ -2227,6 +2325,7 @@ impl KeystrokeInput {
|
||||||
self.keystrokes.push(Self::dummy(keystroke.modifiers));
|
self.keystrokes.push(Self::dummy(keystroke.modifiers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self.keystrokes_changed(cx);
|
self.keystrokes_changed(cx);
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue