1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! The global hints toggle: its visibility flag, its bound chord and the
//! bindings a host advertises for it.
//!
//! Split out of the layout code because this is *state* - a per-thread
//! preference plus the chord that flips it - while the rest of the module is a
//! pure function from hints to lines. The two change for unrelated reasons.
use Cell;
use ;
use keymap;
thread_local!
/// The description shown beside the hints toggle.
const TOGGLE_LABEL: &str = "toggle hints";
/// The hard quit chord, wired into `terminal::classify` and not rebindable.
const HARD_QUIT: = ;
/// Whether shortcut hints are currently shown.
/// Shows or hides every hint footer at once, e.g. to restore a saved session.
/// Flips the hint visibility; what the global toggle chord does.
/// The chord bound to the hints toggle out of the box: `F1`, no modifiers.
///
/// A function key rather than a `Ctrl+…` chord: it can never be text input, so
/// it stays free inside every text field and modal. `Ctrl+Q` (quit), `Ctrl+S`,
/// `Ctrl+G`, `Ctrl+H` and the editing chords are already spoken for.
/// The chord currently toggling the hints, or `None` while it is unbound.
/// Rebinds the global hints toggle, or unbinds it with `None` so the key
/// reaches the host's own `handle_key` instead. Call it before `run`.
///
/// # Examples
///
/// ```
/// use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
/// use ratada::shortcut_hints::{set_toggle_key, toggle_key};
///
/// set_toggle_key(None);
/// assert!(toggle_key().is_none());
///
/// let chord = KeyEvent::new(KeyCode::F(2), KeyModifiers::NONE);
/// set_toggle_key(Some(chord));
/// assert_eq!(toggle_key(), Some(chord));
/// ```
/// The chords the toolkit itself intercepts, as `(key, description)` tokens:
/// the hints toggle (omitted while unbound) and the hard quit.
///
/// A host appends its own conventional chords (`?`, `q`) from its keymap: only
/// it knows them, and only it notices when the user rebinds them. With the
/// hints hidden the toggle appears nowhere else on screen, so a host that
/// builds a help overlay should list these.
///
/// # Examples
///
/// ```
/// use ratada::shortcut_hints::global_bindings;
///
/// let bindings = global_bindings();
/// assert!(bindings.iter().any(|(key, _)| key == "f1"));
/// assert!(bindings.iter().any(|(key, _)| key == "ctrl+q"));
/// ```
/// Consumes `key` when it is the bound hints toggle, flipping the visibility.
///
/// `driver::run` and `overlay::popup` call this before a key reaches the host
/// or a modal's handler, so every `Screen` and every modal inherits the chord.
/// An app that drives its own event loop calls it at the top of its own key
/// dispatch — rather than matching [`toggle_key`] by hand, which is how the
/// modifier comparison gets forgotten.
///
/// Only `code` and `modifiers` are compared: `kind` and `state` vary by
/// terminal.
///
/// # Examples
///
/// ```
/// use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
/// use ratada::shortcut_hints::{consume_toggle, default_toggle_key, visible};
///
/// // In an app's own `handle_key`, before anything else:
/// assert!(consume_toggle(default_toggle_key()));
/// assert!(!visible());
///
/// // Every other key passes through untouched.
/// let other = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::NONE);
/// assert!(!consume_toggle(other));
/// ```
/// A chord as a footer token: `"f1"`, `"ctrl+h"`, `"shift+enter"`.
///
/// Renders through [`keymap::KeyChord`], so a hint reads exactly like the chord
/// a user writes in config and a handler matches on: one rendering of a key in
/// the crate, not one per caller that can drift from the others.
pub