a11y uia: Bridge QAccessibleSelectionInterface
Bridge QAccessibleSelectionInterface that was introduced
in commit 9d16d5e224
to
UIA's ISelectionProvider and ISelectionProvider2 interfaces
by extending the existing WindowsUiaSelectionProvider
to make use of the QAccessibleSelectionInterface.
Also make use of that interface to implement handling
for the ISelectionProviderItem interface methods when
an object has a parent that implements the
QAccessibleSelectionInterface.
Sample use from NVDA's Python console [1] with this
commit in place:
1) start NVDA
2) run the interview example (examples\widgets\itemviews\interview\interview.exe)
3) select "Item 1:0", "Item 2:0" and "Item 3:0" by left-clicking
on "Item 1:0" in the left view, then shift+clicking on "Item 3:0".
4) press Numpad_insert+control+z to start the NVDA Python console and
capture snapshot variables
5) query and use the interfaces using NVDA's Python console:
>>> import UIAHandler
>>> selectionpattern2 = focus.parent.UIAElement.GetCurrentPattern(10034).QueryInterface(UIAHandler.IUIAutomationSelectionPattern2)
>>> selectionpattern2.CurrentFirstSelectedItem.CurrentName
'Item 1:0'
>>> selectionpattern2.CurrentLastSelectedItem.CurrentName
'Item 3:0'
>>> selectionpattern2.CurrentItemCount
3
>>> selectionitempattern = focus.UIAElement.GetCurrentPattern(10010).QueryInterface(UIAHandler.IUIAutomationSelectionItemPattern)
>>> selectionitempattern.CurrentIsSelected
1
>>> selectionitempattern.RemoveFromSelection()
0
>>> selectionitempattern.CurrentIsSelected
0
>>> selectionpattern2.CurrentItemCount
2
>>> selectionitempattern.Select()
0
>>> selectionitempattern.CurrentIsSelected
1
(Note that calling selectionitempattern.AddToSelection
in that example would also unselect all other currently
selected entries, because the underlying implementation
has a selection mode of QAbstractItemView::SingleSelection
set in QAccessibleTree::selectRow, which gets called
from QAccessibleTable::select via QAccessibleTableCell::selectCell.)
[1] https://www.nvaccess.org/files/nvda/documentation/developerGuide.html#PythonConsole
Change-Id: I7003bae5bbcfd5c685620bf710781165ed70f106
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
This commit is contained in:
parent
3bace699bf
commit
4f9c66131d
@ -305,15 +305,17 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow
|
||||
break;
|
||||
case UIA_SelectionPatternId:
|
||||
case UIA_SelectionPattern2Id:
|
||||
// Lists of items.
|
||||
if (accessible->role() == QAccessible::List
|
||||
// Selections via QAccessibleSelectionInterface or lists of items.
|
||||
if (accessible->selectionInterface()
|
||||
|| accessible->role() == QAccessible::List
|
||||
|| accessible->role() == QAccessible::PageTabList) {
|
||||
*pRetVal = new QWindowsUiaSelectionProvider(id());
|
||||
}
|
||||
break;
|
||||
case UIA_SelectionItemPatternId:
|
||||
// Items within a list and radio buttons.
|
||||
if ((accessible->role() == QAccessible::RadioButton)
|
||||
// Parent supports selection interface or items within a list and radio buttons.
|
||||
if ((accessible->parent() && accessible->parent()->selectionInterface())
|
||||
|| (accessible->role() == QAccessible::RadioButton)
|
||||
|| (accessible->role() == QAccessible::ListItem)
|
||||
|| (accessible->role() == QAccessible::PageTab)) {
|
||||
*pRetVal = new QWindowsUiaSelectionItemProvider(id());
|
||||
|
@ -36,6 +36,14 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::Select()
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (QAccessibleInterface *parent = accessible->parent()) {
|
||||
if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) {
|
||||
selectionInterface->clear();
|
||||
bool ok = selectionInterface->select(accessible);
|
||||
return ok ? S_OK : S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
QAccessibleActionInterface *actionInterface = accessible->actionInterface();
|
||||
if (!actionInterface)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
@ -73,6 +81,13 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::AddToSelection()
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (QAccessibleInterface *parent = accessible->parent()) {
|
||||
if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) {
|
||||
bool ok = selectionInterface->select(accessible);
|
||||
return ok ? S_OK : S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
QAccessibleActionInterface *actionInterface = accessible->actionInterface();
|
||||
if (!actionInterface)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
@ -98,6 +113,13 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::RemoveFromSelection(
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (QAccessibleInterface *parent = accessible->parent()) {
|
||||
if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) {
|
||||
bool ok = selectionInterface->unselect(accessible);
|
||||
return ok ? S_OK : S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
QAccessibleActionInterface *actionInterface = accessible->actionInterface();
|
||||
if (!actionInterface)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
@ -124,6 +146,14 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_IsSelected(BOOL
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (QAccessibleInterface *parent = accessible->parent()) {
|
||||
if (QAccessibleSelectionInterface *selectionInterface = parent->selectionInterface()) {
|
||||
bool selected = selectionInterface->isSelected(accessible);
|
||||
*pRetVal = selected ? TRUE : FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (accessible->role() == QAccessible::RadioButton)
|
||||
*pRetVal = accessible->state().checked;
|
||||
else if (accessible->role() == QAccessible::PageTab)
|
||||
@ -146,12 +176,18 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_SelectionContain
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
QAccessibleInterface *parent = accessible->parent();
|
||||
if (parent && parent->selectionInterface()) {
|
||||
*pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
QAccessibleActionInterface *actionInterface = accessible->actionInterface();
|
||||
if (!actionInterface)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
// Radio buttons do not require a container.
|
||||
if (QAccessibleInterface *parent = accessible->parent()) {
|
||||
if (parent) {
|
||||
if ((accessible->role() == QAccessible::ListItem && parent->role() == QAccessible::List)
|
||||
|| (accessible->role() == QAccessible::PageTab && parent->role() == QAccessible::PageTabList)) {
|
||||
*pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent);
|
||||
|
@ -41,19 +41,23 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::GetSelection(SAFEARRAY *
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
// First put selected items in a list, then build a safe array with the right size.
|
||||
// First get/create list of selected items, then build a safe array with the right size.
|
||||
QList<QAccessibleInterface *> selectedList;
|
||||
const int childCount = accessible->childCount();
|
||||
selectedList.reserve(childCount);
|
||||
for (int i = 0; i < childCount; ++i) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (accessible->role() == QAccessible::PageTabList) {
|
||||
if (child->role() == QAccessible::PageTab && child->state().focused) {
|
||||
selectedList.append(child);
|
||||
}
|
||||
} else {
|
||||
if (child->state().selected) {
|
||||
selectedList.append(child);
|
||||
if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) {
|
||||
selectedList = selectionInterface->selectedItems();
|
||||
} else {
|
||||
const int childCount = accessible->childCount();
|
||||
selectedList.reserve(childCount);
|
||||
for (int i = 0; i < childCount; ++i) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (accessible->role() == QAccessible::PageTabList) {
|
||||
if (child->role() == QAccessible::PageTab && child->state().focused) {
|
||||
selectedList.append(child);
|
||||
}
|
||||
} else {
|
||||
if (child->state().selected) {
|
||||
selectedList.append(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,11 +108,15 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_IsSelectionRequired(
|
||||
|
||||
// Initially returns false if none are selected. After the first selection, it may be required.
|
||||
bool anySelected = false;
|
||||
for (int i = 0; i < accessible->childCount(); ++i) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (child->state().selected) {
|
||||
anySelected = true;
|
||||
break;
|
||||
if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) {
|
||||
anySelected = selectionInterface->selectedItem(0) != nullptr;
|
||||
} else {
|
||||
for (int i = 0; i < accessible->childCount(); ++i) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (child->state().selected) {
|
||||
anySelected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -131,17 +139,23 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_FirstSelectedItem(__
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
QAccessibleInterface *firstSelectedChild = nullptr;
|
||||
int i = 0;
|
||||
while (!firstSelectedChild && i < accessible->childCount()) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (accessible->role() == QAccessible::PageTabList) {
|
||||
if (child->role() == QAccessible::PageTab && child->state().focused)
|
||||
if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) {
|
||||
firstSelectedChild = selectionInterface->selectedItem(0);
|
||||
if (!firstSelectedChild)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
} else {
|
||||
int i = 0;
|
||||
while (!firstSelectedChild && i < accessible->childCount()) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (accessible->role() == QAccessible::PageTabList) {
|
||||
if (child->role() == QAccessible::PageTab && child->state().focused)
|
||||
firstSelectedChild = child;
|
||||
} else if (child->state().selected) {
|
||||
firstSelectedChild = child;
|
||||
} else if (child->state().selected) {
|
||||
firstSelectedChild = child;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!firstSelectedChild)
|
||||
@ -169,17 +183,24 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_LastSelectedItem(__R
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
QAccessibleInterface *lastSelectedChild = nullptr;
|
||||
int i = accessible->childCount() - 1;
|
||||
while (!lastSelectedChild && i >= 0) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (accessible->role() == QAccessible::PageTabList) {
|
||||
if (child->role() == QAccessible::PageTab && child->state().focused)
|
||||
if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface()) {
|
||||
const int selectedItemCount = selectionInterface->selectedItemCount();
|
||||
if (selectedItemCount <= 0)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
lastSelectedChild = selectionInterface->selectedItem(selectedItemCount - 1);
|
||||
} else {
|
||||
int i = accessible->childCount() - 1;
|
||||
while (!lastSelectedChild && i >= 0) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (accessible->role() == QAccessible::PageTabList) {
|
||||
if (child->role() == QAccessible::PageTab && child->state().focused)
|
||||
lastSelectedChild = child;
|
||||
} else if (child->state().selected) {
|
||||
lastSelectedChild = child;
|
||||
} else if (child->state().selected) {
|
||||
lastSelectedChild = child;
|
||||
}
|
||||
}
|
||||
i--;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
||||
if (!lastSelectedChild)
|
||||
@ -212,19 +233,24 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_ItemCount(__RPC__out
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
int selectedCount = 0;
|
||||
for (int i = 0; i < accessible->childCount(); i++) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (accessible->role() == QAccessible::PageTabList) {
|
||||
if (child->role() == QAccessible::PageTab && child->state().focused)
|
||||
|
||||
if (QAccessibleSelectionInterface *selectionInterface = accessible->selectionInterface())
|
||||
*pRetVal = selectionInterface->selectedItemCount();
|
||||
else {
|
||||
int selectedCount = 0;
|
||||
for (int i = 0; i < accessible->childCount(); i++) {
|
||||
if (QAccessibleInterface *child = accessible->child(i)) {
|
||||
if (accessible->role() == QAccessible::PageTabList) {
|
||||
if (child->role() == QAccessible::PageTab && child->state().focused)
|
||||
selectedCount++;
|
||||
} else if (child->state().selected) {
|
||||
selectedCount++;
|
||||
} else if (child->state().selected) {
|
||||
selectedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
*pRetVal = selectedCount;
|
||||
}
|
||||
|
||||
*pRetVal = selectedCount;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user