Windows QPA: Implement Selection UIA pattern for QTabBar

Adding Selection pattern for tab bars and SelectionItem pattern for
tab items, which are required for accessibility compliance.

Fixes: QTBUG-104740
Change-Id: I0e3b1cfbf4838d8bc8b5bc2e2d7c9d372ac8b99d
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
(cherry picked from commit 083ff27518a735e39563d1bae9c40ffbc7c1d527)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
André de la Rocha 2022-07-06 03:22:53 +02:00 committed by Qt Cherry-pick Bot
parent 4ef77e81b3
commit c9c3f57883
3 changed files with 37 additions and 23 deletions

View File

@ -295,14 +295,16 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow
break; break;
case UIA_SelectionPatternId: case UIA_SelectionPatternId:
// Lists of items. // Lists of items.
if (accessible->role() == QAccessible::List) { if (accessible->role() == QAccessible::List
|| accessible->role() == QAccessible::PageTabList) {
*pRetVal = new QWindowsUiaSelectionProvider(id()); *pRetVal = new QWindowsUiaSelectionProvider(id());
} }
break; break;
case UIA_SelectionItemPatternId: case UIA_SelectionItemPatternId:
// Items within a list and radio buttons. // Items within a list and radio buttons.
if ((accessible->role() == QAccessible::RadioButton) if ((accessible->role() == QAccessible::RadioButton)
|| (accessible->role() == QAccessible::ListItem)) { || (accessible->role() == QAccessible::ListItem)
|| (accessible->role() == QAccessible::PageTab)) {
*pRetVal = new QWindowsUiaSelectionItemProvider(id()); *pRetVal = new QWindowsUiaSelectionItemProvider(id());
} }
break; break;

View File

@ -40,8 +40,8 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::Select()
if (!actionInterface) if (!actionInterface)
return UIA_E_ELEMENTNOTAVAILABLE; return UIA_E_ELEMENTNOTAVAILABLE;
if (accessible->role() == QAccessible::RadioButton) { if (accessible->role() == QAccessible::RadioButton || accessible->role() == QAccessible::PageTab) {
// For radio buttons we just invoke the selection action; others are automatically deselected. // For radio buttons/tabs we just invoke the selection action; others are automatically deselected.
actionInterface->doAction(QAccessibleActionInterface::pressAction()); actionInterface->doAction(QAccessibleActionInterface::pressAction());
} else { } else {
// Toggle list item if not already selected. It must be done first to support all selection modes. // Toggle list item if not already selected. It must be done first to support all selection modes.
@ -77,8 +77,8 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::AddToSelection()
if (!actionInterface) if (!actionInterface)
return UIA_E_ELEMENTNOTAVAILABLE; return UIA_E_ELEMENTNOTAVAILABLE;
if (accessible->role() == QAccessible::RadioButton) { if (accessible->role() == QAccessible::RadioButton || accessible->role() == QAccessible::PageTab) {
// For radio buttons we invoke the selection action. // For radio buttons and tabs we invoke the selection action.
actionInterface->doAction(QAccessibleActionInterface::pressAction()); actionInterface->doAction(QAccessibleActionInterface::pressAction());
} else { } else {
// Toggle list item if not already selected. // Toggle list item if not already selected.
@ -102,7 +102,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::RemoveFromSelection(
if (!actionInterface) if (!actionInterface)
return UIA_E_ELEMENTNOTAVAILABLE; return UIA_E_ELEMENTNOTAVAILABLE;
if (accessible->role() != QAccessible::RadioButton) { if (accessible->role() != QAccessible::RadioButton && accessible->role() != QAccessible::PageTab) {
if (accessible->state().selected) { if (accessible->state().selected) {
actionInterface->doAction(QAccessibleActionInterface::toggleAction()); actionInterface->doAction(QAccessibleActionInterface::toggleAction());
} }
@ -126,6 +126,8 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_IsSelected(BOOL
if (accessible->role() == QAccessible::RadioButton) if (accessible->role() == QAccessible::RadioButton)
*pRetVal = accessible->state().checked; *pRetVal = accessible->state().checked;
else if (accessible->role() == QAccessible::PageTab)
*pRetVal = accessible->state().focused;
else else
*pRetVal = accessible->state().selected; *pRetVal = accessible->state().selected;
return S_OK; return S_OK;
@ -149,11 +151,10 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_SelectionContain
return UIA_E_ELEMENTNOTAVAILABLE; return UIA_E_ELEMENTNOTAVAILABLE;
// Radio buttons do not require a container. // Radio buttons do not require a container.
if (accessible->role() == QAccessible::ListItem) { if (QAccessibleInterface *parent = accessible->parent()) {
if (QAccessibleInterface *parent = accessible->parent()) { if ((accessible->role() == QAccessible::ListItem && parent->role() == QAccessible::List)
if (parent->role() == QAccessible::List) { || (accessible->role() == QAccessible::PageTab && parent->role() == QAccessible::PageTabList)) {
*pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent); *pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent);
}
} }
} }
return S_OK; return S_OK;

View File

@ -45,8 +45,14 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::GetSelection(SAFEARRAY *
QList<QAccessibleInterface *> selectedList; QList<QAccessibleInterface *> selectedList;
for (int i = 0; i < accessible->childCount(); ++i) { for (int i = 0; i < accessible->childCount(); ++i) {
if (QAccessibleInterface *child = accessible->child(i)) { if (QAccessibleInterface *child = accessible->child(i)) {
if (child->state().selected) { if (accessible->role() == QAccessible::PageTabList) {
selectedList.append(child); if (child->role() == QAccessible::PageTab && child->state().focused) {
selectedList.append(child);
}
} else {
if (child->state().selected) {
selectedList.append(child);
}
} }
} }
} }
@ -90,18 +96,23 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_IsSelectionRequired(
if (!accessible) if (!accessible)
return UIA_E_ELEMENTNOTAVAILABLE; return UIA_E_ELEMENTNOTAVAILABLE;
// Initially returns false if none are selected. After the first selection, it may be required. if (accessible->role() == QAccessible::PageTabList) {
bool anySelected = false; *pRetVal = TRUE;
for (int i = 0; i < accessible->childCount(); ++i) { } else {
if (QAccessibleInterface *child = accessible->child(i)) {
if (child->state().selected) { // Initially returns false if none are selected. After the first selection, it may be required.
anySelected = true; bool anySelected = false;
break; for (int i = 0; i < accessible->childCount(); ++i) {
if (QAccessibleInterface *child = accessible->child(i)) {
if (child->state().selected) {
anySelected = true;
break;
}
} }
} }
}
*pRetVal = anySelected && !accessible->state().multiSelectable && !accessible->state().extSelectable; *pRetVal = anySelected && !accessible->state().multiSelectable && !accessible->state().extSelectable;
}
return S_OK; return S_OK;
} }