1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 12:50:04 -04:00

Hand out an AccessKit focus request at the start of the pass

AccessKit asks for focus by node id, and `Focus::interested_in_focus` only picks
the request up once the widget itself runs. Everything that reads the focus
before the widget — styling it, for one — therefore saw the old focus, so a text
edit was styled unfocused in the very pass it got focused, and lost its focus
ring for a frame.

AccessKit names the widget by the node id it read from the last tree we sent, so
the widget has been here before and is in `focus_widgets_cache`: `begin_pass` can
hand out the focus right away, the same way a `request_focus` from the last pass
is handed out. Memory then holds the focus before any widget runs, and
`Ui::widget_style` needs to know nothing about focus at all.

For the same reason the request never has to outlive the pass it arrives in — a
widget that has not run yet cannot have been named by it — so the pending
`id_requested_by_accesskit` field becomes a local.

The four `text_edit_*` snapshots gain a caret along with the ring, because the
field is now focused for real in that pass rather than a pass later.

Also drops `fallback_gap` from `TextEdit`, which has no `gap` of its own to defer
to, and moves the `DragValue` `HasClasses` impl above its test module.
This commit is contained in:
Lucas Meurer
2026-08-25 16:11:28 +02:00
parent cf4b8de8bd
commit 80ff62e2be

View File

@@ -516,8 +516,6 @@ pub(crate) struct Focus {
/// The ID of a widget to give the focus to in the next frame.
id_next_frame: Option<Id>,
id_requested_by_accesskit: Option<accesskit::NodeId>,
/// If set, the next widget that is interested in focus will automatically get it.
/// Probably because the user pressed Tab.
give_to_next: bool,
@@ -575,10 +573,10 @@ impl Focus {
}
let event_filter = self.focused_widget.map(|w| w.filter).unwrap_or_default();
self.id_requested_by_accesskit = None;
self.focus_direction = FocusDirection::None;
let mut focus_requested_by_accesskit = None;
for event in &new_input.events {
if !event_filter.matches(event)
&& let crate::Event::Key {
@@ -615,32 +613,27 @@ impl Focus {
}) = event
&& *target_tree == accesskit::TreeId::ROOT
{
self.id_requested_by_accesskit = Some(*target_node);
focus_requested_by_accesskit = Some(*target_node);
}
}
// `interested_in_focus` only picks the request up once the widget runs, which is too late
// for anything that reads the focus before it — styling the widget, for one. A widget in
// the cache asked for focus in an earlier pass, so it is one we can hand focus to now.
let accesskit_focus = self.id_requested_by_accesskit.and_then(|node_id| {
// AccessKit names the widget by the node id it read from the last tree we sent, so the
// widget has been here before and is in the cache. Handing the focus out here, instead of
// waiting for the widget to ask for it, means everything that reads the focus before the
// widget runs — styling it, for one — already sees the new focus.
let newly_focused = focus_requested_by_accesskit.and_then(|node_id| {
self.focus_widgets_cache
.keys()
.find(|id| id.accesskit_id() == node_id)
.copied()
});
if let Some(id) = accesskit_focus {
self.take_focus_from_accesskit(id);
if let Some(id) = newly_focused {
self.focused_widget = Some(FocusWidget::new(id));
self.give_to_next = false;
self.reset_focus();
}
}
/// Give a widget the focus that AccessKit asked for.
fn take_focus_from_accesskit(&mut self, id: Id) {
self.focused_widget = Some(FocusWidget::new(id));
self.id_requested_by_accesskit = None;
self.give_to_next = false;
self.reset_focus();
}
pub(crate) fn end_pass(&mut self, used_ids: &IdMap<Rect>) {
if self.focus_direction.is_cardinal()
&& let Some(found_widget) = self.find_widget_in_direction(used_ids)
@@ -666,12 +659,6 @@ impl Focus {
}
fn interested_in_focus(&mut self, id: Id) {
// `begin_pass` hands out the request as soon as it can, but it can only recognize a widget
// that has been here before. This catches the first pass a widget is focusable in.
if self.id_requested_by_accesskit == Some(id.accesskit_id()) {
self.take_focus_from_accesskit(id);
}
// The rect is updated at the end of the frame.
self.focus_widgets_cache
.entry(id)