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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
//! Keyboard focus: which node holds it, how it moves, and how keys route.
//!
//! The focused node hears key events before they bubble to its ancestors.
//! Focus moves by a press on a focusable node or by Tab navigation, and the
//! keyboard navigation mode records which of the two moved it last, so a
//! focus ring can draw for keyboard users alone. A pointer press turns the
//! mode off again.
use crate::geometry::{Point, Rect};
use crate::input::{Handled, Key, KeyEvent};
use crate::widget::Widget;
use super::interaction::Interaction;
use super::{WidgetId, WidgetTree};
#[cfg(test)]
#[path = "focus_tests.rs"]
mod tests;
/// Returns whether `event` is a fresh press of Enter or Space, the keys that
/// activate a control.
///
/// Releases and key repeats activate nothing, so holding a key fires a button
/// once.
fn is_activation_key(event: &KeyEvent) -> bool {
event.pressed && !event.repeat && matches!(event.key, Key::Enter | Key::Space)
}
impl WidgetTree {
/// Routes a key event from the focused node through its ancestors.
///
/// The focused node hears the key first, then its parent, and so on to the
/// root, until a handler reports [`Handled::Yes`]. A press of Tab or an
/// arrow key turns the keyboard navigation mode on, whether or not a
/// handler took the key, and an unhandled Tab or Shift+Tab moves the focus
/// to the next or previous focusable node in tree order. Releases and
/// every other key only route.
///
/// A focused node that became disabled or hidden since it took the focus
/// loses it first, and its own handler does not run: a disabled button
/// must not activate from the keyboard. The key still bubbles from it to
/// the ancestors that can hear it, so shortcuts above it keep working.
pub(crate) fn key(&mut self, event: &KeyEvent) {
// A node that lost the focus by becoming disabled or hidden still
// anchors the next key, so shortcuts above it keep working. The key
// starts at its parent: the node itself no longer holds the focus,
// even if it has been enabled again since.
let start = match self.focused {
Some(focused) => Some(focused),
None => self
.stale_focus
.take()
.and_then(|anchor| self.nodes.get(anchor)?.parent),
};
self.drop_invalid_focus();
let handled = self.route_key(start, event);
if !event.pressed {
return;
}
match event.key {
Key::Tab => {
self.set_keyboard_nav(true);
if handled != Handled::Yes {
self.move_focus(event.modifiers.shift);
}
}
Key::ArrowLeft | Key::ArrowRight | Key::ArrowUp | Key::ArrowDown => {
self.set_keyboard_nav(true);
}
_ => {}
}
}
/// Delivers the event to `start` and its ancestors, deepest first, and
/// returns whether a handler took it.
///
/// `start` is the node that held the focus when the key arrived. Without
/// one the event reaches no handler.
fn route_key(&mut self, start: Option<WidgetId>, event: &KeyEvent) -> Handled {
let mut next = start;
while let Some(id) = next {
next = self.nodes.get(id).and_then(|node| node.parent);
// A disabled or hidden node's handlers never run, but the key keeps
// bubbling, so shortcuts above it still work.
if self.nodes.get(id).is_some_and(|node| {
node.interaction.has(Interaction::DISABLED) || node.resolved.layout.hidden
}) {
continue;
}
// A control that activates on Enter or Space fires its press
// handler before any key handler of its own hears the key, and a
// handler that ran takes the key.
if is_activation_key(event)
&& self
.nodes
.get(id)
.is_some_and(|node| node.handlers.activate_on_keys)
&& self.run_press(id)
{
return Handled::Yes;
}
let Some(owner) = self.owner(id).cloned() else {
continue;
};
let mut handled = Handled::No;
owner.with(|| {
if let Some(on_key) = self
.nodes
.get_mut(id)
.and_then(|node| node.handlers.on_key.as_mut())
{
handled = on_key(event);
}
});
if handled == Handled::Yes {
return Handled::Yes;
}
}
Handled::No
}
/// Delivers committed input method text to the focused node.
///
/// The commit walks up from the focused node, like a key event, and the
/// first `on_ime_commit` handler that runs takes it. A committed string
/// can arrive with no key event at all: a commit is how an input method
/// delivers the text the user composed, so an editor that reads keys
/// alone would lose it. A focused node that lost the focus by becoming
/// disabled or hidden does not hear the commit, but its ancestors still
/// can.
pub(crate) fn ime_commit(&mut self, text: &str) {
self.route_from_focus(|tree, id| {
let Some(on_ime_commit) = tree
.nodes
.get_mut(id)
.and_then(|node| node.handlers.on_ime_commit.as_mut())
else {
return false;
};
on_ime_commit(text);
true
});
}
/// Delivers a change of the composing text to the focused node.
///
/// Non-empty text shows or updates the preedit, empty text clears it,
/// which is how a canceled composition arrives. The routing is the
/// commit's: the deepest `on_ime_preedit` handler in the focused chain
/// takes it, and a node that can no longer hold the focus does not hear
/// it while its ancestors still can.
pub(crate) fn ime_preedit(&mut self, text: &str, cursor: Option<(usize, usize)>) {
self.route_from_focus(|tree, id| {
let Some(on_ime_preedit) = tree
.nodes
.get_mut(id)
.and_then(|node| node.handlers.on_ime_preedit.as_mut())
else {
return false;
};
on_ime_preedit(text, cursor);
true
});
}
/// Runs `deliver` on the focused node and its ancestors, deepest first,
/// and stops at the first handler that runs.
///
/// The event starts at the node that held the focus when it arrived. A
/// focused node that became disabled or hidden delivers nothing, but the
/// event keeps bubbling past it, like a key does.
fn route_from_focus(&mut self, mut deliver: impl FnMut(&mut Self, WidgetId) -> bool) {
let start = match self.focused {
Some(focused) => Some(focused),
None => self
.stale_focus
.take()
.and_then(|anchor| self.nodes.get(anchor)?.parent),
};
self.drop_invalid_focus();
let mut next = start;
while let Some(id) = next {
next = self.nodes.get(id).and_then(|node| node.parent);
if self.nodes.get(id).is_some_and(|node| {
node.interaction.has(Interaction::DISABLED) || node.resolved.layout.hidden
}) {
continue;
}
let Some(owner) = self.owner(id).cloned() else {
continue;
};
let mut ran = false;
owner.with(|| {
ran = deliver(self, id);
});
if ran {
return;
}
}
}
/// Moves the focus to the next focusable node in tree order, or to the
/// previous one when `backwards` is set, wrapping at both ends.
fn move_focus(&mut self, backwards: bool) {
let next = self.next_focusable(self.focused, backwards);
self.set_focus(next);
self.scroll_focus_into_view();
}
/// Focuses `id`, or clears the focus when `id` is `None`.
///
/// The node that loses the focus clears its focused bit and hears
/// `on_focus` with `false` before the node that gains it is marked and
/// hears `on_focus` with `true`. A node that already holds the focus keeps
/// it without hearing `on_focus` again. The keyboard focus bit follows the
/// keyboard navigation mode. Any change consumes the key anchor a dropped
/// focus left behind, because the focus has moved on purpose.
pub(super) fn set_focus(&mut self, id: Option<WidgetId>) {
if self.focused == id {
return;
}
let old = self.focused;
self.stale_focus = None;
if let Some(old) = self.focused {
self.set_bit(old, Interaction::FOCUSED, false);
self.set_bit(old, Interaction::KEYBOARD_FOCUS, false);
}
self.focused = id;
if let Some(new) = id {
self.set_bit(new, Interaction::FOCUSED, true);
if self.keyboard_nav {
self.set_bit(new, Interaction::KEYBOARD_FOCUS, true);
}
}
if let Some(old) = old {
self.push_a11y(old);
}
if let Some(new) = id {
self.push_a11y(new);
}
}
/// Focuses `id` on behalf of an accessibility action, and returns whether
/// it took the focus.
///
/// A node that is not focusable, or that cannot hold the focus because it
/// is disabled or hidden, is left alone and `false` is returned. A node
/// that already holds the focus keeps it.
pub(crate) fn request_focus(&mut self, id: WidgetId) -> bool {
if !self.focusable(id) || !self.can_hold_focus(id) {
return false;
}
self.set_focus(Some(id));
true
}
/// Turns the keyboard navigation mode on or off.
///
/// The focused node carries the keyboard focus bit only while the mode is
/// on, so a style can react to keyboard focus alone. A pointer press
/// clears the mode, which clears the bit of a node that stays focused.
pub(super) fn set_keyboard_nav(&mut self, on: bool) {
if self.keyboard_nav == on {
return;
}
self.keyboard_nav = on;
if let Some(focused) = self.focused {
self.set_bit(focused, Interaction::KEYBOARD_FOCUS, on);
}
}
/// Returns the node that holds the keyboard focus, when one does.
pub(crate) fn focused(&self) -> Option<WidgetId> {
self.focused
}
/// Clears the focus when the node that holds it can no longer keep it.
///
/// A node that became disabled or hidden loses the focus at once, so a
/// focus style never draws on a control that is switched off, instead of
/// holding it until the next key event notices. The node hears
/// `on_focus` with `false` as it goes. The node stays as the anchor the
/// next key event bubbles from, so a shortcut above it keeps working.
pub(super) fn drop_invalid_focus(&mut self) {
let Some(focused) = self
.focused
.filter(|focused| !self.can_hold_focus(*focused))
else {
return;
};
self.set_focus(None);
self.stale_focus = Some(focused);
}
/// Returns whether the keyboard navigation mode is on.
pub(crate) fn keyboard_nav(&self) -> bool {
self.keyboard_nav
}
/// Returns the area the focused widget reports to the input method, when
/// it reports one.
///
/// The widget paints in paint coordinates, so the rectangle it reports is
/// in paint coordinates too, and this translates it back into window
/// coordinates with the node's shift. A focused node that has since
/// become disabled or hidden reports none, as it would hear no key.
pub(crate) fn ime_area(&self) -> Option<Rect> {
let focused = self.focused.filter(|&id| self.can_hold_focus(id))?;
let node = self.nodes.get(focused)?;
let shift = node.paint_shift;
let widget: &dyn Widget = node.widget.as_ref();
widget.ime_area().map(|area| {
area.translated(Point {
x: -shift.x,
y: -shift.y,
})
})
}
/// Returns the next focusable node in tree order after `from`, or the
/// previous one when `backwards` is set, wrapping at both ends.
///
/// Without a focused node, forward navigation starts at the first
/// focusable node and backward navigation at the last. A hidden node and
/// its subtree are skipped, and a disabled node is skipped while its
/// children are still visited, matching hit testing.
fn next_focusable(&self, from: Option<WidgetId>, backwards: bool) -> Option<WidgetId> {
let mut order = Vec::new();
if let Some(root) = self.root {
self.collect_focusable(root, &mut order);
}
if order.is_empty() {
return None;
}
match from.and_then(|id| order.iter().position(|&candidate| candidate == id)) {
Some(index) if backwards => Some(order[(index + order.len() - 1) % order.len()]),
Some(index) => Some(order[(index + 1) % order.len()]),
None if backwards => order.last().copied(),
None => order.first().copied(),
}
}
/// Appends the focusable nodes of the subtree, in paint order.
fn collect_focusable(&self, id: WidgetId, order: &mut Vec<WidgetId>) {
let Some(node) = self.nodes.get(id) else {
return;
};
if node.resolved.layout.hidden {
return;
}
let focusable = node.interaction.has(Interaction::FOCUSABLE)
&& !node.interaction.has(Interaction::DISABLED);
if focusable {
order.push(id);
}
for &child in &node.children {
self.collect_focusable(child, order);
}
}
/// Returns whether `id` may keep the focus: it is not disabled, and
/// neither it nor any ancestor is hidden.
///
/// A node only clipped out of view, for example scrolled away, keeps it.
fn can_hold_focus(&self, id: WidgetId) -> bool {
let Some(node) = self.nodes.get(id) else {
return false;
};
if node.interaction.has(Interaction::DISABLED) {
return false;
}
let mut next = Some(id);
while let Some(current) = next {
let Some(node) = self.nodes.get(current) else {
return false;
};
if node.resolved.layout.hidden {
return false;
}
next = node.parent;
}
true
}
/// Scrolls the nearest scrolling ancestor so the focused node is in view.
///
/// Nothing scrolls yet: scroll offsets arrive with the scroll view, and
/// focus movement calls this hook so the behavior has one home.
fn scroll_focus_into_view(&mut self) {}
}