From 5a515b2545d265a4e2b16adb96dda6251251675e Mon Sep 17 00:00:00 2001 From: Reza Bakhshi Laktasaraei Date: Mon, 24 Aug 2026 11:48:28 +0330 Subject: [PATCH] Say what announces a focused accessibility child The platform raises a focus event when a widget takes keyboard focus and resolves it through focusChild(), so a painted list that also announces its current child from focusInEvent has it read twice - write that down next to accessibilityChildFocused(), where the mistake is made. The one container SetFocus we do handle ourselves is not about announcing: a selection list keeps keyboard focus on its items, so focus has to reach the selected one for the arrow keys and the Tab-stop to work. --- ui/accessible/ui_accessible_widget.cpp | 10 +++++----- ui/rp_widget.h | 9 +++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ui/accessible/ui_accessible_widget.cpp b/ui/accessible/ui_accessible_widget.cpp index aa538f74..780103b0 100644 --- a/ui/accessible/ui_accessible_widget.cpp +++ b/ui/accessible/ui_accessible_widget.cpp @@ -269,11 +269,11 @@ QStringList Widget::actionNames() const { } void Widget::doAction(const QString &actionName) { - // On Qt 5 the Windows UIA bridge redirects a container's SetFocus to - // focusChild() only for an element exposing a table interface, not for a - // List - so focus would land on the inert container. Forward SetFocus to - // the selected item's widget directly instead (no extra Qt patch needed). - // Opt-in, so only a selection list (the folder strip) is affected. + // The items of a selection list are real widgets and it is they, not the + // container, that keep the keyboard focus: they handle the arrow keys and + // carry the list's single Tab-stop. So a container SetFocus has to land on + // the selected item - taking focus there announces it by itself. Opt-in, + // so only a selection list (the folder strip) is affected. if (actionName == QAccessibleActionInterface::setFocusAction() && rp()->accessibilitySelectionList()) { if (const auto selected = selectedItem(0)) { diff --git a/ui/rp_widget.h b/ui/rp_widget.h index 1d10351e..d5276473 100644 --- a/ui/rp_widget.h +++ b/ui/rp_widget.h @@ -463,6 +463,15 @@ class RpWidget : public RpWidgetBase { [[nodiscard]] virtual QAccessible::Role accessibilityChildSubItemRole() const; [[nodiscard]] virtual QString accessibilityChildSubItemName(int row, int column) const; [[nodiscard]] virtual QString accessibilityChildSubItemValue(int row, int column) const; + + // Announces a child as the one holding accessible focus. Meant for a + // browse position that moves while keyboard focus stays put - arrow keys + // inside a painted list, say. Taking keyboard focus needs no call: that + // raises a focus event of its own which the platform resolves through + // focusChild(), so announcing the child here as well reads it twice; make + // sure focusChild() points at the right one before focus-in returns + // instead. (On Windows that resolution needs Qt 6.2 or the focus child + // notification backported into the 5.15 build.) void accessibilityChildFocused(int index); // Per-child opt-in for the accessibility action interface (SetFocus /