click to focus row

Ben Kunkle created

Change summary

crates/settings_ui/src/keybindings.rs         |  3 +
crates/settings_ui/src/ui_components/table.rs | 49 +++++++++++++++-----
2 files changed, 39 insertions(+), 13 deletions(-)

Detailed changes

crates/settings_ui/src/keybindings.rs 🔗

@@ -319,6 +319,9 @@ impl Render for KeymapEditor {
                     .column_widths([rems(24.), rems(16.), rems(32.), rems(8.)])
                     .header(["Command", "Keystrokes", "Context", "Source"])
                     .selected_item_index(self.selected_index.clone())
+                    .on_click_row(cx.processor(|this, row_index, _window, _cx| {
+                        this.selected_index = Some(row_index);
+                    }))
                     .uniform_list(
                         "keymap-editor-table",
                         row_count,

crates/settings_ui/src/ui_components/table.rs 🔗

@@ -1,4 +1,4 @@
-use std::{ops::Range, time::Duration};
+use std::{ops::Range, rc::Rc, time::Duration};
 
 use editor::{EditorSettings, ShowScrollbar, scroll::ScrollbarAutoHide};
 use gpui::{
@@ -355,6 +355,7 @@ pub struct Table<const COLS: usize = 3> {
     interaction_state: Option<WeakEntity<TableInteractionState>>,
     selected_item_index: Option<usize>,
     column_widths: Option<[Length; COLS]>,
+    on_click_row: Option<Rc<dyn Fn(usize, &mut Window, &mut App)>>,
 }
 
 impl<const COLS: usize> Table<COLS> {
@@ -368,6 +369,7 @@ impl<const COLS: usize> Table<COLS> {
             interaction_state: None,
             selected_item_index: None,
             column_widths: None,
+            on_click_row: None,
         }
     }
 
@@ -428,6 +430,14 @@ impl<const COLS: usize> Table<COLS> {
         self.column_widths = Some(widths.map(Into::into));
         self
     }
+
+    pub fn on_click_row(
+        mut self,
+        callback: impl Fn(usize, &mut Window, &mut App) + 'static,
+    ) -> Self {
+        self.on_click_row = Some(Rc::new(callback));
+        self
+    }
 }
 
 fn base_cell_style(width: Option<Length>, cx: &App) -> Div {
@@ -460,7 +470,7 @@ pub fn render_row<const COLS: usize>(
         .map_or([None; COLS], |widths| widths.map(|width| Some(width)));
     let is_selected = table_context.selected_item_index == Some(row_index);
 
-    div()
+    let row = div()
         .w_full()
         .border_2()
         .border_color(transparent_black())
@@ -489,8 +499,15 @@ pub fn render_row<const COLS: usize>(
                         .zip(column_widths)
                         .map(|(cell, width)| base_cell_style(width, cx).child(cell)),
                 ),
-        )
-        .into_any_element()
+        );
+
+    if let Some(on_click) = table_context.on_click_row {
+        row.id(ElementId::named_usize("table-row", row_index))
+            .on_click(move |_, window, cx| on_click(row_index, window, cx))
+            .into_any_element()
+    } else {
+        row.into_any_element()
+    }
 }
 
 pub fn render_header<const COLS: usize>(
@@ -517,12 +534,13 @@ pub fn render_header<const COLS: usize>(
         }))
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone)]
 pub struct TableRenderContext<const COLS: usize> {
     pub striped: bool,
     pub total_row_count: usize,
     pub selected_item_index: Option<usize>,
     pub column_widths: Option<[Length; COLS]>,
+    pub on_click_row: Option<Rc<dyn Fn(usize, &mut Window, &mut App)>>,
 }
 
 impl<const COLS: usize> TableRenderContext<COLS> {
@@ -532,6 +550,7 @@ impl<const COLS: usize> TableRenderContext<COLS> {
             total_row_count: table.rows.len(),
             column_widths: table.column_widths,
             selected_item_index: table.selected_item_index.clone(),
+            on_click_row: table.on_click_row.clone(),
         }
     }
 }
@@ -581,7 +600,7 @@ impl<const COLS: usize> RenderOnce for Table<COLS> {
                     })
             })
             .when_some(self.headers.take(), |this, headers| {
-                this.child(render_header(headers, table_context, cx))
+                this.child(render_header(headers, table_context.clone(), cx))
             })
             .child(
                 div()
@@ -590,12 +609,11 @@ impl<const COLS: usize> RenderOnce for Table<COLS> {
                     .relative()
                     .overflow_hidden()
                     .map(|parent| match self.rows {
-                        TableContents::Vec(items) => parent.children(
-                            items
-                                .into_iter()
-                                .enumerate()
-                                .map(|(index, row)| render_row(index, row, table_context, cx)),
-                        ),
+                        TableContents::Vec(items) => {
+                            parent.children(items.into_iter().enumerate().map(|(index, row)| {
+                                render_row(index, row, table_context.clone(), cx)
+                            }))
+                        }
                         TableContents::UniformList(uniform_list_data) => parent.child(
                             uniform_list(
                                 uniform_list_data.element_id,
@@ -608,7 +626,12 @@ impl<const COLS: usize> RenderOnce for Table<COLS> {
                                             .into_iter()
                                             .zip(range)
                                             .map(|(row, row_index)| {
-                                                render_row(row_index, row, table_context, cx)
+                                                render_row(
+                                                    row_index,
+                                                    row,
+                                                    table_context.clone(),
+                                                    cx,
+                                                )
                                             })
                                             .collect()
                                     }