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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! Keyboard focus traversal — `tab` and `shift-tab` between controls.
//!
//! gpui has all the machinery and none of it is on by default: a focus handle
//! carries a `tab_index` and a `tab_stop` flag, `.track_focus` registers the
//! handle for the frame, and [`Window::focus_next`] walks the order — but
//! `tab_stop` starts `false` and gpui binds no keys. This module turns it on.
//!
//! # Order is paint order
//!
//! gpui sorts tab stops by their `tab_index` path and then by insertion, so
//! leaving every index at 0 yields the order the controls are painted in.
//! Nothing has to be numbered by hand, and inserting a control in the middle of
//! a form does not renumber the rest — which is the failure mode that makes
//! HTML `tabindex` a liability.
//!
//! # Where the handle lives
//!
//! Most of this crate is `fn(&Theme, ..) -> Div`: stateless, with the app
//! owning whether a checkbox is checked. Focus is more of that state, so the
//! app owns the handle too and [`focusable`] wires it up. Giving every widget a
//! handle of its own would mean giving every widget an identity and a lifetime,
//! which is the entity machinery [`crate::input::TextField`] needs and a
//! checkbox does not.
//!
//! ```ignore
//! ui::focus::init(cx); // once, at startup
//!
//! // ..and on the root view, so `tab` works wherever focus currently is:
//! focus::traversal(div().track_focus(&self.focus_handle))
//! .child(focus::focusable(&theme, &self.ok_focus, popover::button(&theme, "OK", "ok")))
//! ```
use ;
use Theme;
actions!;
/// Claimed by every [`focusable`] control, so `enter` and `space` mean "press
/// this" only where something is actually focused.
///
/// Scoping matters here: [`crate::palette`] and [`crate::combobox`] both bind
/// `enter` for their own lists, and a multi-line field binds it to insert a
/// newline. A focused control sits deeper in the focus path than any of them,
/// so it wins `enter` while focused and gives it straight back afterwards.
pub const CONTROL_KEY_CONTEXT: &str = "Control";
/// Added to a surface's key context when `tab` is its own key — a document
/// nests a list with it. [`traversal`] stands down while that surface holds
/// focus.
///
/// A mark, not a predicate on the binding: any predicate fails against an empty
/// context stack, which is what an element with no key context dispatches
/// against.
///
/// ```ignore
/// let mut context = KeyContext::default();
/// context.add(MY_CONTEXT);
/// context.add(focus::CLAIMS_TAB);
/// div().key_context(context).track_focus(&self.focus_handle)
/// ```
pub const CLAIMS_TAB: &str = "ClaimsTab";
/// Install the bindings — [`bindings`], bound. Call once at startup.
/// Traversal's keymap, as data, so an app can have it without having to
/// take it — see [`crate::keys`] for layering over it or taking a chord
/// away.
///
/// `tab` and `shift-tab`, global rather than scoped to a context: traversal is a property of the window, not
/// of whatever happens to be focused.
///
/// Nothing in this crate claims `tab` for itself, deliberately. A multi-line
/// field could reasonably insert one, but trapping `tab` inside a text box is
/// the classic way to make a form impossible to leave by keyboard. A surface
/// where the key is structural says so with [`CLAIMS_TAB`] instead.
///
/// [`Decrement`]/[`Increment`] on `left`/`right` are for a control that holds a
/// *value* rather than a press — [`slider`](crate::widgets::Controls::slider)
/// is the one. They
/// carry no step: only the caller knows the range, and a library that picked
/// one would be picking it for a percentage and a font size alike.
/// Attach the traversal handlers, normally to the app's root element.
///
/// It has to live on an element rather than on the app because moving focus
/// needs a [`Window`], and an app-level action handler only gets an [`App`].
///
/// Where the focused surface [claims the key](CLAIMS_TAB), both handlers
/// propagate instead: an action handler stops propagation by default, and
/// continuing it is what sends gpui on to the next binding the chord matched.
/// The innermost context alone, not any in the path: a field *inside* a
/// claiming surface still means "next control" by `tab`.
/// Put a stateless control into the tab order, show when it holds focus, and
/// let `enter`/`space` press it.
///
/// The ring is the same one [`crate::input::TextField`] paints — the border in
/// [`Theme::ring`] — so a focused button and a focused field read alike.
///
/// Keyboard focus only, like CSS `:focus-visible`. A control also takes focus
/// when clicked, and a ring that landed on it there would outline a slider for
/// the whole drag — the pointer already says which control is being used.
///
/// It lands on the control's *own* border, which is why every control in
/// [`crate::widgets`] carries one even where it paints nothing: gpui sizes
/// border-box, so a border that only appeared on focus would move the content
/// under it by a pixel. A ring wrapped *around* the control instead would cost
/// every one of them a radius parameter, and would prise a focused tab off the
/// hairline its underline has to overlap.
///
/// Pressing dispatches [`Activate`]. Use [`pressable`] to route it and a click
/// through one callback, or handle it separately for custom interaction.
/// One activation path for pointer and keyboard, with a shared enabled gate.
/// The caller still owns the value changed by the callback.
+ 'static,
)