Skip to content

Commit fd1bf70

Browse files
committed
feat: add directional settings navigation
1 parent 22c2e77 commit fd1bf70

1 file changed

Lines changed: 81 additions & 7 deletions

File tree

src/app/view.rs

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
16211652
fn 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)]
21302167
mod 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

Comments
 (0)