Windows: Inform accessibility system about the focused child item

When a complex object (i.e. one with children that are themselves not
fully exposed objects) gets focus, then we need to inform the
accessibility system about which child object actually has focus. This
was only done for item views, but not for other complex widgets.

An editable QComboBoxes is the focus proxy for its line edit. The line
edit never gets focus itself (QComboBox forwards relevant events),
and is the accessible child item with index 1. So when an editable
combobox gets focus, it needs to raise the automation event for the
line edit child.

Implement QAccessibleComboBox::focusChild to return the interface to the
lineedit for editable comboboxes so that the UI Automation bridge can
correctly notify about the focus being moved to an editable text input
field.

Fixes: QTBUG-107572
Pick-to: 6.4 6.2
Change-Id: Id60e2791ec859365255baa9bfd01547979cd2b44
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
This commit is contained in:
Volker Hilsheimer 2022-11-07 12:45:09 +01:00
parent de16300661
commit b58876c296
4 changed files with 55 additions and 2 deletions

View File

@ -71,10 +71,11 @@ QWindowsUiaMainProvider::~QWindowsUiaMainProvider()
void QWindowsUiaMainProvider::notifyFocusChange(QAccessibleEvent *event)
{
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
// If this is a table/tree/list, raise event for the focused cell/item instead.
if (accessible->tableInterface())
// If this is a complex element, raise event for the focused child instead.
if (accessible->childCount()) {
if (QAccessibleInterface *child = accessible->focusChild())
accessible = child;
}
if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible))
QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_AutomationFocusChangedEventId);
}

View File

@ -320,6 +320,16 @@ int QAccessibleComboBox::indexOfChild(const QAccessibleInterface *child) const
return -1;
}
QAccessibleInterface *QAccessibleComboBox::focusChild() const
{
// The editable combobox is the focus proxy of its lineedit, so the
// lineedit itself never gets focus. But it is the accessible focus
// child of an editable combobox.
if (comboBox()->isEditable())
return child(1);
return nullptr;
}
/*! \reimp */
QString QAccessibleComboBox::text(QAccessible::Text t) const
{

View File

@ -99,6 +99,7 @@ public:
QAccessibleInterface *childAt(int x, int y) const override;
int indexOfChild(const QAccessibleInterface *child) const override;
QAccessibleInterface* child(int index) const override;
QAccessibleInterface* focusChild() const override;
QString text(QAccessible::Text t) const override;

View File

@ -4250,6 +4250,47 @@ void tst_QAccessibility::focusChild()
QVERIFY(child);
QCOMPARE(child->text(QAccessible::Name), QStringLiteral("Klimt"));
}
{
QWidget window;
// takes the initial focus
QLineEdit lineEdit;
QComboBox comboBox;
comboBox.addItems({"One", "Two", "Three"});
QComboBox editableComboBox;
editableComboBox.setEditable(true);
editableComboBox.addItems({"A", "B", "C"});
QVBoxLayout vbox;
vbox.addWidget(&lineEdit);
vbox.addWidget(&comboBox);
vbox.addWidget(&editableComboBox);
window.setLayout(&vbox);
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QTestAccessibility::clearEvents();
QAccessibleInterface *iface = nullptr;
comboBox.setFocus();
{
QAccessibleEvent focusEvent(&comboBox, QAccessible::Focus);
QVERIFY(QTestAccessibility::containsEvent(&focusEvent));
}
iface = QAccessible::queryAccessibleInterface(&comboBox);
QVERIFY(iface);
QCOMPARE(iface->focusChild(), nullptr);
editableComboBox.setFocus();
// Qt updates about the editable combobox, not the lineedit, as the
// combobox is the lineedit's focus proxy.
{
QAccessibleEvent focusEvent(&editableComboBox, QAccessible::Focus);
QVERIFY(QTestAccessibility::containsEvent(&focusEvent));
}
iface = QAccessible::queryAccessibleInterface(&editableComboBox);
QVERIFY(iface);
QVERIFY(iface->focusChild());
QCOMPARE(iface->focusChild()->role(), QAccessible::EditableText);
}
}
void tst_QAccessibility::messageBoxTest_data()