@@ -1617,6 +1617,37 @@ fn settings_navigation_activation(keystroke: &gpui::Keystroke) -> bool {
16171617 !keystroke. modifiers . modified ( ) && matches ! ( keystroke. key. as_str( ) , "enter" | "space" )
16181618}
16191619
1620+ /// Maps layout-appropriate arrow keys to one step through the settings workspaces.
1621+ ///
1622+ /// The compact row reads horizontally while the wide rail reads vertically, so the direction
1623+ /// follows the visible arrangement instead of forcing users to remember one global key pair.
1624+ fn settings_navigation_direction ( keystroke : & gpui:: Keystroke , compact : bool ) -> Option < i8 > {
1625+ if keystroke. modifiers . modified ( ) {
1626+ return None ;
1627+ }
1628+ match ( compact, keystroke. key . as_str ( ) ) {
1629+ ( true , "left" ) | ( false , "up" ) => Some ( -1 ) ,
1630+ ( true , "right" ) | ( false , "down" ) => Some ( 1 ) ,
1631+ _ => None ,
1632+ }
1633+ }
1634+
1635+ /// Selects the next settings workspace and wraps at either end of the visible navigation list.
1636+ fn adjacent_settings_section ( section : SettingsSection , direction : i8 ) -> SettingsSection {
1637+ const SECTIONS : [ SettingsSection ; 4 ] = [
1638+ SettingsSection :: Capture ,
1639+ SettingsSection :: Files ,
1640+ SettingsSection :: Recording ,
1641+ SettingsSection :: System ,
1642+ ] ;
1643+ let index = SECTIONS
1644+ . iter ( )
1645+ . position ( |candidate| * candidate == section)
1646+ . expect ( "every settings section must be listed in navigation" ) ;
1647+ let next = ( index as isize + isize:: from ( direction) ) . rem_euclid ( SECTIONS . len ( ) as isize ) ;
1648+ SECTIONS [ next as usize ]
1649+ }
1650+
16201651/// Renders a vertical navigation rail on roomy windows and a compact section row on narrow ones.
16211652fn settings_navigation (
16221653 selected : SettingsSection ,
@@ -1765,6 +1796,12 @@ fn settings_navigation_item(
17651796 keyboard_app. update ( cx, |this, cx| {
17661797 this. select_settings_section ( item. section , cx)
17671798 } ) ;
1799+ } else if let Some ( direction) = settings_navigation_direction ( & event. keystroke , compact)
1800+ {
1801+ keyboard_app. update ( cx, |this, cx| {
1802+ let section = adjacent_settings_section ( this. settings_section , direction) ;
1803+ this. select_settings_section ( section, cx) ;
1804+ } ) ;
17681805 }
17691806 } )
17701807 . on_click ( move |_, _, cx| {
@@ -2129,13 +2166,14 @@ fn settings_delay_button(
21292166#[ cfg( test) ]
21302167mod tests {
21312168 use super :: {
2132- RecordingViewState , capture_command_label, capture_shortcut_summary,
2133- history_clear_confirmation_label, history_entry_label, history_entry_matches,
2134- history_result_summary, history_retention_label, history_visibility_label,
2135- recording_progress_label, recording_source_discovery_busy, recording_status_visible,
2136- recording_toggle_label, relative_timestamp_label, settings_navigation_activation,
2137- settings_navigation_items, settings_page_copy, settings_page_intro, settings_path_label,
2138- status_indicator_color, uses_compact_settings_navigation, visible_history_entries,
2169+ RecordingViewState , adjacent_settings_section, capture_command_label,
2170+ capture_shortcut_summary, history_clear_confirmation_label, history_entry_label,
2171+ history_entry_matches, history_result_summary, history_retention_label,
2172+ history_visibility_label, recording_progress_label, recording_source_discovery_busy,
2173+ recording_status_visible, recording_toggle_label, relative_timestamp_label,
2174+ settings_navigation_activation, settings_navigation_direction, settings_navigation_items,
2175+ settings_page_copy, settings_page_intro, settings_path_label, status_indicator_color,
2176+ uses_compact_settings_navigation, visible_history_entries,
21392177 } ;
21402178 use crate :: app:: { HistoryClearScope , HistoryFilter , SettingsSection } ;
21412179 use crate :: history:: { HistoryEntry , HistorySource } ;
@@ -2223,6 +2261,42 @@ mod tests {
22232261 }
22242262 }
22252263
2264+ #[ test]
2265+ fn settings_navigation_uses_the_visible_layout_direction ( ) {
2266+ assert_eq ! (
2267+ settings_navigation_direction( & gpui:: Keystroke :: parse( "left" ) . unwrap( ) , true ) ,
2268+ Some ( -1 )
2269+ ) ;
2270+ assert_eq ! (
2271+ settings_navigation_direction( & gpui:: Keystroke :: parse( "down" ) . unwrap( ) , false ) ,
2272+ Some ( 1 )
2273+ ) ;
2274+ assert_eq ! (
2275+ settings_navigation_direction( & gpui:: Keystroke :: parse( "right" ) . unwrap( ) , false ) ,
2276+ None
2277+ ) ;
2278+ assert_eq ! (
2279+ settings_navigation_direction( & gpui:: Keystroke :: parse( "shift-left" ) . unwrap( ) , true ) ,
2280+ None
2281+ ) ;
2282+ }
2283+
2284+ #[ test]
2285+ fn settings_navigation_wraps_when_traversing_past_the_first_or_last_section ( ) {
2286+ assert_eq ! (
2287+ adjacent_settings_section( SettingsSection :: Capture , -1 ) ,
2288+ SettingsSection :: System
2289+ ) ;
2290+ assert_eq ! (
2291+ adjacent_settings_section( SettingsSection :: System , 1 ) ,
2292+ SettingsSection :: Capture
2293+ ) ;
2294+ assert_eq ! (
2295+ adjacent_settings_section( SettingsSection :: Files , 1 ) ,
2296+ SettingsSection :: Recording
2297+ ) ;
2298+ }
2299+
22262300 #[ test]
22272301 fn settings_page_copy_matches_navigation_purposes ( ) {
22282302 assert_eq ! (
0 commit comments