diff --git a/crates/ui/src/components.rs b/crates/ui/src/components.rs index 6e3c7c78aec2732aeb6cea67b6da5da6686b0f30..e23d76a84b015dc35aa3256aa0d3a80bde9f16c2 100644 --- a/crates/ui/src/components.rs +++ b/crates/ui/src/components.rs @@ -15,6 +15,7 @@ mod indent_guides; mod indicator; mod keybinding; mod keybinding_hint; +mod keyboard_navigation; mod label; mod list; mod modal; @@ -56,6 +57,7 @@ pub use indent_guides::*; pub use indicator::*; pub use keybinding::*; pub use keybinding_hint::*; +pub use keyboard_navigation::*; pub use label::*; pub use list::*; pub use modal::*; diff --git a/crates/ui/src/components/keyboard_navigation.rs b/crates/ui/src/components/keyboard_navigation.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a624cb3a3f5970a7ea111d46444f179d614a9d8 --- /dev/null +++ b/crates/ui/src/components/keyboard_navigation.rs @@ -0,0 +1,59 @@ +use gpui::{Focusable, actions}; + +actions!( + keyboard_navigation, + [NextRow, PreviousRow, NextColumn, PreviousColumn] +); + +/// Implement this trait to enable grid-like keyboard navigation for a layout. +/// +/// This trait allows you to navigate through a layout of rows with mixed column +/// lengths. In example, a layout might have rows with 5, 1 and 3 columns. +/// +/// Moving up or down between rows will focus the first element in the next or previous row. +/// Moving left or right between columns will focus the next or previous element in the same row. +/// +/// Wrapping can be enabled via `vertical_wrapping` and `horizontal_wrapping` respectively. +pub trait KeyboardNavigation: Focusable { + fn has_focus(&self) -> bool; + /// The focused row. Always has a value to allow for "focused inactive" states. + fn focused_row(&self) -> usize; + /// The focused column. Always has a value to allow for "focused inactive" states. + fn focused_column(&self) -> usize; + /// Focus the first focusable element in the layout. + fn focus_first(&self); + /// Focus the next row, wrapping back to the first row if necessary. + /// + /// Is a no-op if wrapping is not enabled. + fn focus_next_row(&self); + /// Focus the previous row, wrapping back to the last row if necessary. + /// + /// Is a no-op if wrapping is not enabled. + fn focus_previous_row(&self); + /// Focus the next column, wrapping back to the first column if necessary. + /// + /// Is a no-op if wrapping is not enabled. + fn focus_next_column(&self); + /// Focus the previous column, wrapping back to the last column if necessary. + /// + /// Is a no-op if wrapping is not enabled. + fn focus_previous_column(&self); + /// Focus the row at the given index. + fn focus_row_index(&self, index: usize); + /// Focus the column at the given index. + fn focus_column_index(&self, ix: usize); + /// When reaching the last row, should moving down wrap + /// back to the first row, and vice versa? + fn vertical_wrap(&self) -> bool { + false + } + /// When reaching the last column, should moving right wrap + /// back to the first column, and vice versa? + fn horizontal_wrap(&self) -> bool { + false + } +} + +pub struct NavigationRow {} + +pub struct NavigationColumn {}