From 18d6be250f4c102a0d23d6b9ec5f7ed3629118fc Mon Sep 17 00:00:00 2001 From: Aleksei Gusev Date: Tue, 9 Jul 2024 11:50:13 +0300 Subject: [PATCH] Add keyboard shortcuts for the prompts on Linux (#13915) This change adds ability to choose any action from prompts, not just the default one and cancel as Zed has right now. For example, when a user tries to close a file with edits in it the prompt offers "Don't save" option that can be selected only with mouse. Now you can use arrows, tab/shift-tab to pick action and enter/space to confirm it. Fixes [#13906](https://github.com/zed-industries/zed/issues/13906) Release Notes: - Added keyboard navigation in the prompts on Linux ([#13906](https://github.com/zed-industries/zed/issues/13906)). Co-authored-by: Thorsten Ball --- assets/keymaps/default-linux.json | 4 +++ assets/keymaps/default-macos.json | 4 +++ crates/zed/src/zed/linux_prompts.rs | 38 ++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index c4c2cb2d0e..336ec8e8cc 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -3,10 +3,14 @@ { "bindings": { "up": "menu::SelectPrev", + "shift-tab": "menu::SelectPrev", + "home": "menu::SelectFirst", "pageup": "menu::SelectFirst", "shift-pageup": "menu::SelectFirst", "ctrl-p": "menu::SelectPrev", "down": "menu::SelectNext", + "tab": "menu::SelectNext", + "end": "menu::SelectLast", "pagedown": "menu::SelectLast", "shift-pagedown": "menu::SelectFirst", "ctrl-n": "menu::SelectNext", diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 63c737e614..567cc1fc65 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -3,10 +3,14 @@ { "bindings": { "up": "menu::SelectPrev", + "shift-tab": "menu::SelectPrev", + "home": "menu::SelectFirst", "pageup": "menu::SelectFirst", "shift-pageup": "menu::SelectFirst", "ctrl-p": "menu::SelectPrev", "down": "menu::SelectNext", + "tab": "menu::SelectNext", + "end": "menu::SelectLast", "pagedown": "menu::SelectLast", "shift-pagedown": "menu::SelectFirst", "ctrl-n": "menu::SelectNext", diff --git a/crates/zed/src/zed/linux_prompts.rs b/crates/zed/src/zed/linux_prompts.rs index 949bba4936..7493f8c6f3 100644 --- a/crates/zed/src/zed/linux_prompts.rs +++ b/crates/zed/src/zed/linux_prompts.rs @@ -31,6 +31,7 @@ pub fn fallback_prompt_renderer( detail: detail.map(ToString::to_string), actions: actions.iter().map(ToString::to_string).collect(), focus: cx.focus_handle(), + active_action_id: actions.len() - 1, } }); @@ -44,10 +45,12 @@ pub struct FallbackPromptRenderer { detail: Option, actions: Vec, focus: FocusHandle, + active_action_id: usize, } + impl FallbackPromptRenderer { fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { - cx.emit(PromptResponse(0)); + cx.emit(PromptResponse(self.active_action_id)); } fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext) { @@ -55,7 +58,32 @@ impl FallbackPromptRenderer { cx.emit(PromptResponse(ix)); } } + + fn select_first(&mut self, _: &menu::SelectFirst, cx: &mut ViewContext) { + self.active_action_id = 0; + cx.notify(); + } + + fn select_last(&mut self, _: &menu::SelectLast, cx: &mut ViewContext) { + self.active_action_id = self.actions.len().saturating_sub(1); + cx.notify(); + } + + fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext) { + self.active_action_id = (self.active_action_id + 1) % self.actions.len(); + cx.notify(); + } + + fn select_prev(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext) { + if self.active_action_id > 0 { + self.active_action_id -= 1; + } else { + self.active_action_id = self.actions.len().saturating_sub(1); + } + cx.notify(); + } } + impl Render for FallbackPromptRenderer { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { let settings = ThemeSettings::get_global(cx); @@ -66,6 +94,10 @@ impl Render for FallbackPromptRenderer { .track_focus(&self.focus) .on_action(cx.listener(Self::confirm)) .on_action(cx.listener(Self::cancel)) + .on_action(cx.listener(Self::select_next)) + .on_action(cx.listener(Self::select_prev)) + .on_action(cx.listener(Self::select_first)) + .on_action(cx.listener(Self::select_last)) .elevation_3(cx) .w_72() .overflow_hidden() @@ -87,11 +119,11 @@ impl Render for FallbackPromptRenderer { .child(detail) })) .child(h_flex().justify_end().gap_2().children( - self.actions.iter().enumerate().rev().map(|(ix, action)| { + self.actions.iter().rev().enumerate().map(|(ix, action)| { ui::Button::new(ix, action.clone()) .label_size(LabelSize::Large) .style(ButtonStyle::Filled) - .when(ix == 0, |el| { + .when(ix == self.active_action_id, |el| { el.style(ButtonStyle::Tinted(TintColor::Accent)) }) .layer(ElevationIndex::ModalSurface)