@@ -1,4 +1,4 @@
-use std::{fmt::Write as _, time::Duration};
+use std::{fmt::Write as _, ops::Range, time::Duration};
use db::anyhow::anyhow;
use gpui::{
@@ -15,7 +15,10 @@ use ui::{
};
use workspace::{Item, SerializableItem, Workspace, register_serializable_item};
-use crate::{keybindings::persistence::KEYBINDING_EDITORS, ui_components::table::Table};
+use crate::{
+ keybindings::persistence::KEYBINDING_EDITORS,
+ ui_components::table::{Table, TableInteractionState},
+};
actions!(zed, [OpenKeymapEditor]);
@@ -54,6 +57,7 @@ struct KeymapEditor {
focus_handle: FocusHandle,
_keymap_subscription: Subscription,
processed_bindings: Vec<ProcessedKeybinding>,
+ table_interaction_state: Entity<TableInteractionState>,
scroll_handle: UniformListScrollHandle,
horizontal_scrollbar: ScrollbarProperties,
vertical_scrollbar: ScrollbarProperties,
@@ -77,10 +81,7 @@ impl KeymapEditor {
this.processed_bindings = key_bindings;
});
- cx.on_focus_out(&focus_handle, window, |this, _, window, cx| {
- this.hide_scrollbars(window, cx);
- })
- .detach();
+ let table_interaction_state = TableInteractionState::new(window, cx);
let scroll_handle = UniformListScrollHandle::new();
let vertical_scrollbar = ScrollbarProperties {
@@ -108,6 +109,7 @@ impl KeymapEditor {
scroll_handle,
horizontal_scrollbar,
vertical_scrollbar,
+ table_interaction_state,
};
this.update_scrollbar_visibility(cx);
@@ -392,10 +394,8 @@ impl Render for KeymapEditor {
};
let row_count = self.processed_bindings.len();
- let table = Table::new(row_count);
let theme = cx.theme();
- let headers = ["Command", "Keystrokes", "Context"].map(Into::into);
div()
.size_full()
@@ -412,108 +412,121 @@ impl Render for KeymapEditor {
}
}))
.child(
- table
- .h_full()
- .v_flex()
- .child(table.render_header(headers, cx))
- .child(
- div()
- .flex_grow()
- .w_full()
- .relative()
- .overflow_hidden()
- .child(
- uniform_list(
- "keybindings",
- row_count,
- cx.processor(move |this, range, _, cx| {
- range
- .map(|index| {
- let binding = &this.processed_bindings[index];
- let row = [
- binding.action.clone(),
- binding.keystroke_text.clone(),
- binding.context.clone(),
- // TODO: Add a source field
- // string_cell(keybinding.source().to_string()),
- ]
- .map(string_cell);
-
- table.render_row(index, row, cx)
- })
- .collect()
- }),
- )
- .size_full()
- .flex_grow()
- .track_scroll(self.scroll_handle.clone())
- .with_sizing_behavior(ListSizingBehavior::Auto)
- .with_horizontal_sizing_behavior(
- ListHorizontalSizingBehavior::Unconstrained,
- ),
- )
- .when(self.vertical_scrollbar.show_track, |this| {
- this.child(
- v_flex()
- .h_full()
- .flex_none()
- .w(scroll_track_size)
- .bg(cx.theme().colors().background)
- .child(
- div()
- .size_full()
- .flex_1()
- .border_l_1()
- .border_color(cx.theme().colors().border),
- ),
+ Table::uniform_list(
+ "keymap-editor-table",
+ row_count,
+ cx.processor(|this, range: Range<usize>, window, cx| {
+ range
+ .map(|index| {
+ let binding = &this.processed_bindings[index];
+ let row = [
+ binding.action.clone(),
+ binding.keystroke_text.clone(),
+ binding.context.clone(),
+ // TODO: Add a source field
+ // binding.source.clone(),
+ ];
+
+ // fixme: pass through callback as a row_cx param
+ let striped = false;
+
+ crate::ui_components::table::render_row(
+ index, row, row_count, striped, cx,
)
})
- .when(self.vertical_scrollbar.show_scrollbar, |this| {
- this.child(self.render_vertical_scrollbar(cx))
- }),
- )
- .when(self.horizontal_scrollbar.show_track, |this| {
- this.child(
- h_flex()
- .w_full()
- .h(scroll_track_size)
- .flex_none()
- .relative()
- .child(
- div()
- .w_full()
- .flex_1()
- // for some reason the horizontal scrollbar is 1px
- // taller than the vertical scrollbar??
- .h(scroll_track_size - px(1.))
- .bg(cx.theme().colors().background)
- .border_t_1()
- .border_color(cx.theme().colors().border),
- )
- .when(self.vertical_scrollbar.show_track, |this| {
- this.child(
- div()
- .flex_none()
- // -1px prevents a missing pixel between the two container borders
- .w(scroll_track_size - px(1.))
- .h_full(),
- )
- .child(
- // HACK: Fill the missing 1px 🥲
- div()
- .absolute()
- .right(scroll_track_size - px(1.))
- .bottom(scroll_track_size - px(1.))
- .size_px()
- .bg(cx.theme().colors().border),
- )
- }),
- )
- })
- .when(self.horizontal_scrollbar.show_scrollbar, |this| {
- this.child(self.render_horizontal_scrollbar(h_scroll_offset, cx))
+ .collect()
}),
+ )
+ .header(["Command", "Keystrokes", "Context"])
+ .interactable(&self.table_interaction_state),
)
+ // .child(
+ // table
+ // .h_full()
+ // .v_flex()
+ // .child(table.render_header(headers, cx))
+ // .child(
+ // div()
+ // .flex_grow()
+ // .w_full()
+ // .relative()
+ // .overflow_hidden()
+ // .child(
+ // uniform_list(
+ // "keybindings",
+ // row_count,
+ // cx.processor(move |this, range, _, cx| {}),
+ // )
+ // .size_full()
+ // .flex_grow()
+ // .track_scroll(self.scroll_handle.clone())
+ // .with_sizing_behavior(ListSizingBehavior::Auto)
+ // .with_horizontal_sizing_behavior(
+ // ListHorizontalSizingBehavior::Unconstrained,
+ // ),
+ // )
+ // .when(self.vertical_scrollbar.show_track, |this| {
+ // this.child(
+ // v_flex()
+ // .h_full()
+ // .flex_none()
+ // .w(scroll_track_size)
+ // .bg(cx.theme().colors().background)
+ // .child(
+ // div()
+ // .size_full()
+ // .flex_1()
+ // .border_l_1()
+ // .border_color(cx.theme().colors().border),
+ // ),
+ // )
+ // })
+ // .when(self.vertical_scrollbar.show_scrollbar, |this| {
+ // this.child(self.render_vertical_scrollbar(cx))
+ // }),
+ // )
+ // .when(self.horizontal_scrollbar.show_track, |this| {
+ // this.child(
+ // h_flex()
+ // .w_full()
+ // .h(scroll_track_size)
+ // .flex_none()
+ // .relative()
+ // .child(
+ // div()
+ // .w_full()
+ // .flex_1()
+ // // for some reason the horizontal scrollbar is 1px
+ // // taller than the vertical scrollbar??
+ // .h(scroll_track_size - px(1.))
+ // .bg(cx.theme().colors().background)
+ // .border_t_1()
+ // .border_color(cx.theme().colors().border),
+ // )
+ // .when(self.vertical_scrollbar.show_track, |this| {
+ // this.child(
+ // div()
+ // .flex_none()
+ // // -1px prevents a missing pixel between the two container borders
+ // .w(scroll_track_size - px(1.))
+ // .h_full(),
+ // )
+ // .child(
+ // // HACK: Fill the missing 1px 🥲
+ // div()
+ // .absolute()
+ // .right(scroll_track_size - px(1.))
+ // .bottom(scroll_track_size - px(1.))
+ // .size_px()
+ // .bg(cx.theme().colors().border),
+ // )
+ // }),
+ // )
+ // })
+ // .when(self.horizontal_scrollbar.show_scrollbar, |this| {
+ // this.child(self.render_horizontal_scrollbar(h_scroll_offset, cx))
+ // }),
+ // )
}
}
@@ -1,12 +1,11 @@
use std::ops::Range;
-use db::smol::stream::iter;
-use gpui::{Entity, FontWeight, Length, uniform_list};
+use gpui::{AppContext as _, Entity, FocusHandle, FontWeight, Length, WeakEntity, uniform_list};
use ui::{
ActiveTheme as _, AnyElement, App, Button, ButtonCommon as _, ButtonStyle, Color, Component,
- ComponentScope, Div, ElementId, FixedWidth as _, FluentBuilder as _, Indicator, IntoElement,
- ParentElement, RegisterComponent, RenderOnce, Styled, StyledTypography, Window, div,
- example_group_with_title, px, single_example, v_flex,
+ ComponentScope, Div, ElementId, FixedWidth as _, FluentBuilder as _, Indicator,
+ InteractiveElement as _, IntoElement, ParentElement, RegisterComponent, RenderOnce, Styled,
+ StyledTypography, Window, div, example_group_with_title, px, single_example, v_flex,
};
struct UniformListData {
@@ -36,6 +35,31 @@ impl<const COLS: usize> TableContents<COLS> {
}
}
+pub struct TableInteractionState {
+ pub focus_handle: FocusHandle,
+}
+
+impl TableInteractionState {
+ pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
+ cx.new(|cx| {
+ let focus_handle = cx.focus_handle();
+
+ // cx.on_focus_out(&focus_handle, window, |this, _, window, cx| {
+ // this.hide_scrollbars(window, cx);
+ // })
+ // .detach();
+
+ Self { focus_handle }
+ })
+ }
+}
+
+impl TableInteractionState {
+ pub fn hide_scrollbars(&mut self, window: &mut Window, cx: &mut App) {
+ todo!()
+ }
+}
+
/// A table component
#[derive(RegisterComponent, IntoElement)]
pub struct Table<const COLS: usize = 3> {
@@ -43,6 +67,7 @@ pub struct Table<const COLS: usize = 3> {
width: Length,
headers: Option<[AnyElement; COLS]>,
rows: TableContents<COLS>,
+ interaction_state: Option<WeakEntity<TableInteractionState>>,
}
impl<const COLS: usize> Table<COLS> {
@@ -60,10 +85,10 @@ impl<const COLS: usize> Table<COLS> {
row_count: row_count,
render_item_fn: Box::new(render_item_fn),
}),
+ interaction_state: None,
}
}
- /// Create a new table with a column count equal to the
/// number of headers provided.
pub fn new() -> Self {
Table {
@@ -71,6 +96,7 @@ impl<const COLS: usize> Table<COLS> {
width: Length::Auto,
headers: None,
rows: TableContents::Vec(Vec::new()),
+ interaction_state: None,
}
}
@@ -86,6 +112,11 @@ impl<const COLS: usize> Table<COLS> {
self
}
+ pub fn interactable(mut self, interaction_state: &Entity<TableInteractionState>) -> Self {
+ self.interaction_state = Some(interaction_state.downgrade());
+ self
+ }
+
pub fn header(mut self, headers: [impl IntoElement; COLS]) -> Self {
self.headers = Some(headers.map(IntoElement::into_any_element));
self
@@ -185,6 +216,12 @@ impl<const COLS: usize> RenderOnce for Table<COLS> {
.when_some(self.headers.take(), |this, headers| {
this.child(render_header(headers, cx))
})
+ .when_some(
+ self.interaction_state.and_then(|state| state.upgrade()),
+ |this, interaction_state| {
+ this.track_focus(&interaction_state.read(cx).focus_handle)
+ },
+ )
.map(|div| match self.rows {
TableContents::Vec(items) => div.children(
items