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
//! Focus traversal helpers for [`UiState`](super::UiState).
use web_time::Instant;
use crate::event::UiTarget;
use crate::focus::focus_order;
use crate::tree::El;
use super::UiState;
impl UiState {
pub fn sync_focus_order(&mut self, root: &El) {
let order = focus_order(root, self);
self.focus.order = order;
if let Some(focused) = &self.focused {
if let Some(current) = self
.focus
.order
.iter()
.find(|t| t.node_id == focused.node_id)
{
self.focused = Some(current.clone());
return;
}
self.focused = None;
}
}
pub fn set_focus(&mut self, target: Option<UiTarget>) {
if let Some(target) =
target.filter(|t| self.focus.order.iter().any(|f| f.node_id == t.node_id))
{
let changed = self.focused.as_ref().map(|f| &f.node_id) != Some(&target.node_id);
self.focused = Some(target);
if changed {
self.bump_caret_activity(Instant::now());
}
}
}
/// Queue programmatic focus requests by key. Each entry is
/// resolved once per `prepare_layout`, after the focus order has
/// been rebuilt: matching keys focus the corresponding node;
/// unmatched keys are dropped silently. Hosts call this once per
/// frame from [`crate::event::App::drain_focus_requests`]; apps
/// that own a `Runner` can also push directly for tests.
pub fn push_focus_requests(&mut self, keys: Vec<String>) {
self.focus.pending_requests.extend(keys);
}
/// Drain the queued focus requests, resolving each by key against
/// the current focus order. The last successfully-resolved key
/// wins. Called by `prepare_layout` after `sync_popover_focus` so
/// explicit requests override popover auto-focus.
pub fn drain_focus_requests(&mut self) {
let keys = std::mem::take(&mut self.focus.pending_requests);
for key in keys {
if let Some(target) = self.focus.order.iter().find(|t| t.key == key).cloned() {
self.set_focus(Some(target));
}
}
}
/// Set whether the current focus should display its focus ring.
/// The runtime calls this from input-handling paths: pointer-down
/// clears it (`false`), Tab and arrow-nav raise it (`true`). Apps
/// that move focus programmatically can also flip it explicitly,
/// e.g. force the ring on after restoring focus from an off-screen
/// menu close. See [`UiState::focus_visible`].
pub fn set_focus_visible(&mut self, visible: bool) {
self.focus_visible = visible;
}
pub fn focus_next(&mut self) -> Option<&UiTarget> {
self.move_focus(1)
}
pub fn focus_prev(&mut self) -> Option<&UiTarget> {
self.move_focus(-1)
}
fn move_focus(&mut self, delta: isize) -> Option<&UiTarget> {
if self.focus.order.is_empty() {
self.focused = None;
return None;
}
let current = self.focused.as_ref().and_then(|target| {
self.focus
.order
.iter()
.position(|t| t.node_id == target.node_id)
});
let len = self.focus.order.len() as isize;
let next = match current {
Some(current) => (current as isize + delta).rem_euclid(len) as usize,
None if delta < 0 => self.focus.order.len() - 1,
None => 0,
};
self.focused = Some(self.focus.order[next].clone());
self.focused.as_ref()
}
}