diff --git a/Cargo.lock b/Cargo.lock index ecd32e22efa9198a4e72c1c5dc785938992d4be8..42fa8fa3b4e2e901f9da3688f19346e56f2c2679 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14576,6 +14576,7 @@ dependencies = [ "fuzzy", "gpui", "log", + "menu", "paths", "project", "schemars", diff --git a/crates/settings_ui/Cargo.toml b/crates/settings_ui/Cargo.toml index 4ac0e562f68ac664e74cd3aabc8e5861d9bf8634..2e7622d1686fc8f73c1f510bb3eabcfb0b18b553 100644 --- a/crates/settings_ui/Cargo.toml +++ b/crates/settings_ui/Cargo.toml @@ -22,6 +22,7 @@ fs.workspace = true fuzzy.workspace = true gpui.workspace = true log.workspace = true +menu.workspace = true paths.workspace = true project.workspace = true schemars.workspace = true diff --git a/crates/settings_ui/src/keybindings.rs b/crates/settings_ui/src/keybindings.rs index 2e9e02e819f7b8df65dd00d5b90acef46a5ee8e4..fac792a30a1b28e642f115583beef37319988819 100644 --- a/crates/settings_ui/src/keybindings.rs +++ b/crates/settings_ui/src/keybindings.rs @@ -4,8 +4,8 @@ use db::anyhow::anyhow; use editor::{Editor, EditorEvent}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, Global, ScrollStrategy, - Subscription, actions, div, + AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, Global, KeyContext, + ScrollStrategy, Subscription, actions, div, }; use ui::{ @@ -61,6 +61,7 @@ struct KeymapEditor { matches: Vec, table_interaction_state: Entity, filter_editor: Entity, + selected_index: Option, } impl EventEmitter<()> for KeymapEditor {} @@ -102,6 +103,7 @@ impl KeymapEditor { _keymap_subscription, table_interaction_state, filter_editor, + selected_index: None, }; this.update_keybindings(cx); @@ -201,6 +203,69 @@ impl KeymapEditor { self.update_matches(cx); cx.notify(); } + + fn dispatch_context(&self, _window: &Window, _cx: &Context) -> KeyContext { + let mut dispatch_context = KeyContext::new_with_defaults(); + dispatch_context.add("KeymapEditor"); + dispatch_context.add("menu"); + + // todo! track key context in keybind edit modal + // let identifier = if self.keymap_editor.focus_handle(cx).is_focused(window) { + // "editing" + // } else { + // "not_editing" + // }; + // dispatch_context.add(identifier); + + dispatch_context + } + + fn select_next(&mut self, _: &menu::SelectNext, window: &mut Window, cx: &mut Context) { + if let Some(selected) = &mut self.selected_index { + *selected += 1; + if *selected >= self.matches.len() { + self.select_last(&Default::default(), window, cx); + } else { + cx.notify(); + } + } else { + self.select_first(&Default::default(), window, cx); + } + } + + fn select_previous( + &mut self, + _: &menu::SelectPrevious, + window: &mut Window, + cx: &mut Context, + ) { + if let Some(selected) = &mut self.selected_index { + *selected = selected.saturating_sub(1); + if *selected == 0 { + self.select_first(&Default::default(), window, cx); + } else if *selected >= self.matches.len() { + self.select_last(&Default::default(), window, cx); + } else { + cx.notify(); + } + } else { + self.select_last(&Default::default(), window, cx); + } + } + + fn select_first(&mut self, _: &menu::SelectFirst, window: &mut Window, cx: &mut Context) { + if self.matches.get(0).is_some() { + self.selected_index = Some(0); + cx.notify(); + } + } + + fn select_last(&mut self, _: &menu::SelectLast, _: &mut Window, cx: &mut Context) { + if self.matches.last().is_some() { + self.selected_index = Some(self.matches.len() - 1); + cx.notify(); + } + } } #[derive(Clone)] @@ -220,11 +285,16 @@ impl Item for KeymapEditor { } impl Render for KeymapEditor { - fn render(&mut self, _window: &mut Window, cx: &mut ui::Context) -> impl ui::IntoElement { + fn render(&mut self, window: &mut Window, cx: &mut ui::Context) -> impl ui::IntoElement { let row_count = self.matches.len(); let theme = cx.theme(); div() + .key_context(self.dispatch_context(window, cx)) + .on_action(cx.listener(Self::select_next)) + .on_action(cx.listener(Self::select_previous)) + .on_action(cx.listener(Self::select_first)) + .on_action(cx.listener(Self::select_last)) .size_full() .bg(theme.colors().background) .id("keymap-editor") @@ -248,6 +318,7 @@ impl Render for KeymapEditor { .striped() .column_widths([rems(24.), rems(16.), rems(32.), rems(8.)]) .header(["Command", "Keystrokes", "Context", "Source"]) + .selected_item_index(self.selected_index.clone()) .uniform_list( "keymap-editor-table", row_count, diff --git a/crates/settings_ui/src/ui_components/table.rs b/crates/settings_ui/src/ui_components/table.rs index 09956e3cb1fa9cb4016c807bca57fd601a4d0271..980d53502473ba395965b3cd6dc1eb2f0f3e1ec7 100644 --- a/crates/settings_ui/src/ui_components/table.rs +++ b/crates/settings_ui/src/ui_components/table.rs @@ -353,6 +353,7 @@ pub struct Table { headers: Option<[AnyElement; COLS]>, rows: TableContents, interaction_state: Option>, + selected_item_index: Option, column_widths: Option<[Length; COLS]>, } @@ -365,6 +366,7 @@ impl Table { headers: None, rows: TableContents::Vec(Vec::new()), interaction_state: None, + selected_item_index: None, column_widths: None, } } @@ -405,6 +407,11 @@ impl Table { self } + pub fn selected_item_index(mut self, selected_item_index: Option) -> Self { + self.selected_item_index = selected_item_index; + self + } + pub fn header(mut self, headers: [impl IntoElement; COLS]) -> Self { self.headers = Some(headers.map(IntoElement::into_any_element)); self @@ -450,6 +457,8 @@ pub fn render_row( let column_widths = table_context .column_widths .map_or([None; COLS], |widths| widths.map(|width| Some(width))); + let is_selected = table_context.selected_item_index == Some(row_index); + div() .w_full() .flex() @@ -462,6 +471,10 @@ pub fn render_row( .when(!is_last, |row| { row.border_b_1().border_color(cx.theme().colors().border) }) + .when(is_selected, |row| { + row.border_2() + .border_color(cx.theme().colors().panel_focused_border) + }) .children( items .map(IntoElement::into_any_element) @@ -500,6 +513,7 @@ pub fn render_header( pub struct TableRenderContext { pub striped: bool, pub total_row_count: usize, + pub selected_item_index: Option, pub column_widths: Option<[Length; COLS]>, } @@ -509,6 +523,7 @@ impl TableRenderContext { striped: table.striped, total_row_count: table.rows.len(), column_widths: table.column_widths, + selected_item_index: table.selected_item_index.clone(), } } }