History

feat: emphasize active tix shortcut prefixes

Expanded shortcut groups previously differed from their collapsed form primarily through parentheses and an underlined key. That cue was easy to miss in the dense history footer, particularly when deciding whether the next key would invoke a direct action or a prefixed one. Reverse every cell belonging to the open view, edit, or information group while preserving its existing colors, dimmed toggles, and underlines. Keep separators and neighboring top-level actions outside the reversed range so the active mode has a strong, theme-independent boundary.

Codex committed 3 months ago

Commit 152ee6b1 parent 1a7f9332 files · +38 −2Browse files

Unified · Split

2 files
  1. gix-tix/spec.md +3 −2
  2. gix-tix/src/ui.rs +35 −0
gix-tix/spec.md+3−2
@@ -219,8 +219,9 @@ for signature verification, alignment, message, and changes actions, but these · 218 unmodified lines
219219 actions retain their direct shortcuts while the group is closed. While expanded,
220220 the group visually contains every following status action through `<enter> diff`,
221221 including pane switching and navigation; none of those hints is visible while
222−the group is closed, and quit remains outside it. The footer
223−underlines the `v` in `view`; while active, `view (` contains
222+the group is closed, and quit remains outside it. The footer underlines the `v`
223+in `view`; an open prefix reverses its complete expanded group for a strong,
224+terminal-theme-independent cue. While active, `view (` contains
224225 every applicable display option and a closing `)` so direct shortcuts remain visibly
225226 outside the prefix. The history status starts with the history position, then the
226227 `v` prefix and the `e` prefix when it is addressable. Remaining history-level
gix-tix/src/ui.rs+35−0
@@ -878,6 +878,7 @@ pub(crate) fn draw_with_worktree( · 877 unmodified lines
878878 }
879879 }
880880 edit_prefix_spans.push(Span::raw(")"));
881+ emphasize_prefix(&mut edit_prefix_spans[1..]);
881882 } else if !app.history_display_expanded {
882883 edit_prefix_spans.push(Span::raw(" · "));
883884 edit_prefix_spans.push(Span::styled("e", Style::default().add_modifier(Modifier::UNDERLINED)));
@@ -929,6 +930,7 @@ pub(crate) fn draw_with_worktree( · 45 unmodified lines
929930 ));
930931 }
931932 view_prefix_spans.push(Span::raw(")"));
933+ emphasize_prefix(&mut view_prefix_spans[1..]);
932934 } else {
933935 view_prefix_spans.push(Span::raw(" · "));
934936 view_prefix_spans.push(Span::styled("v", Style::default().add_modifier(Modifier::UNDERLINED)));
@@ -946,6 +948,7 @@ pub(crate) fn draw_with_worktree( · 11 unmodified lines
946948 ordered.push(Span::raw(" · "));
947949 ordered.extend(shortcut("refs", 'r', app.ref_mode != RefMode::None));
948950 ordered.push(Span::raw(" · "));
951+ let information_prefix_start = app.information_expanded.then_some(ordered.len());
949952 ordered.push(Span::styled("?", Style::default().add_modifier(Modifier::UNDERLINED)));
950953 if app.information_expanded {
951954 ordered.push(Span::raw(" ("));
@@ -981,6 +984,7 @@ pub(crate) fn draw_with_worktree( · 29 unmodified lines
981984 footer_spans.push(Span::raw(" · <enter> diff"));
982985 }
983986 footer_spans.push(Span::raw(")"));
987+ emphasize_prefix(&mut footer_spans[information_prefix_start.expect("the prefix is expanded")..]);
984988 }
985989 if app.changes_focus.is_none() {
986990 footer_spans.push(Span::raw(" · "));
@@ -1623,6 +1627,12 @@ fn shortcut(label: &'static str, key: char, enabled: bool) -> Vec<Span<'static>> · 636 unmodified lines
16231627 ]
16241628 }
16251629
1630+fn emphasize_prefix(spans: &mut [Span<'_>]) {
1631+ for span in spans {
1632+ span.style = span.style.add_modifier(Modifier::REVERSED);
1633+ }
1634+}
1635+
16261636 fn notification_discs(spans: Vec<Span<'_>>) -> Vec<Span<'_>> {
16271637 let mut out = Vec::with_capacity(spans.len());
16281638 for span in spans {
@@ -2924,6 +2934,7 @@ mod tests { · 1295 unmodified lines
29242934 .contains(Modifier::UNDERLINED),
29252935 "the prefix key is underlined in its verb"
29262936 );
2937+ assert_reversed_group(&terminal, 1, view);
29272938
29282939 app.history_display_expanded = false;
29292940 app.edit_expanded = true;
@@ -2932,6 +2943,7 @@ mod tests { · 2 unmodified lines
29322943 rendered_line(&terminal, 1).starts_with("0 commits · view · edit (no actions) · copy · refs · ?"),
29332944 "the edit prefix follows the view prefix even when no action is available"
29342945 );
2946+ assert_reversed_group(&terminal, 1, "edit (no actions)");
29352947
29362948 app.edit_expanded = false;
29372949 app.information_expanded = true;
@@ -2943,6 +2955,11 @@ mod tests { · 5 unmodified lines
29432955 ),
29442956 "the information prefix contains every following action except quit: {footer}"
29452957 );
2958+ assert_reversed_group(
2959+ &terminal,
2960+ 1,
2961+ "? ([ align · message · changes · Esc cancel · ↑↓/jk move · h/l pan · <enter> diff)",
2962+ );
29462963 Ok(())
29472964 }
29482965
@@ -5108,6 +5125,24 @@ mod tests { · 2159 unmodified lines
51085125 })
51095126 }
51105127
5128+ fn assert_reversed_group(terminal: &Terminal<TestBackend>, y: u16, group: &str) {
5129+ let line = rendered_line(terminal, y);
5130+ let start = line[..line.find(group).expect("the active prefix group is visible")]
5131+ .chars()
5132+ .count() as u16;
5133+ let end = start + group.chars().count() as u16;
5134+ let buffer = terminal.backend().buffer();
5135+ assert!(
5136+ (start..end).all(|x| buffer[(x, y)].modifier.contains(Modifier::REVERSED)),
5137+ "every cell in {group:?} is reversed"
5138+ );
5139+ assert!(
5140+ !buffer[(start - 1, y)].modifier.contains(Modifier::REVERSED)
5141+ && !buffer[(end, y)].modifier.contains(Modifier::REVERSED),
5142+ "the active treatment is bounded to {group:?}"
5143+ );
5144+ }
5145+
51115146 fn footer_is_dim(terminal: &Terminal<TestBackend>, label: &str) -> bool {
51125147 let y = terminal.backend().buffer().area.height - 1;
51135148 let footer = rendered_line(terminal, y);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t