a11y uia: Support ISelectionProvider2
Support UIA's ISelectionProvider2 interface [1]
in addition to ISelectionProvider.
The ISelectionProvider2 interface inherits from the
ISelectionProvider interface.
A follow-up commit that will introduce bridging the
QAccessibleSelectionInterface, introduced in commit
9d16d5e224.
While at it, also reserve space for the amount
of children in the QList in QWindowsUiaSelectionProvider::GetSelection
before inserting them one by one, to avoid reallocations.
Sample use of the ISelectionProvider2 interface from NVDA's
Python console [2] with this commit in place:
1) start NVDA
2) run the gallery example (examples\widgets\gallery\gallery.exe)
3) click on the "Style" listbox at the top
4) press Numpad_insert+control+z to start the NVDA Python console and
capture snapshot variables
5) query and use the interface using NVDA's Python console
>>> import UIAHandler
>>> iselection2 = focus.parent.UIAElement.GetCurrentPattern(10034).QueryInterface(UIAHandler.IUIAutomationSelectionPattern2)
>>> iselection2.CurrentItemCount
1
>>> iselection2.CurrentFirstSelectedItem.CurrentName
'windowsvista'
>>> iselection2.CurrentLastSelectedItem.CurrentName
'windowsvista'
[1] https://learn.microsoft.com/en-us/windows/win32/api/uiautomationcore/nn-uiautomationcore-iselectionprovider2
[2] https://www.nvaccess.org/files/nvda/documentation/developerGuide.html#PythonConsole
Change-Id: I43642e9e39b63c65da97af976cc322a8e5868170
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
bb10
parent
90af40ccd0
commit
3bace699bf
|
|
@ -49,5 +49,6 @@
|
|||
#define UIA_DropTargetPatternId 10031
|
||||
#define UIA_TextEditPatternId 10032
|
||||
#define UIA_CustomNavigationPatternId 10033
|
||||
#define UIA_SelectionPattern2Id 10034
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -236,6 +236,24 @@ __CRT_UUID_DECL(ISelectionProvider, 0xfb8b03af, 0x3bdf, 0x48d4, 0xbd,0x36, 0x1a,
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __ISelectionProvider2_INTERFACE_DEFINED__
|
||||
#define __ISelectionProvider2_INTERFACE_DEFINED__
|
||||
DEFINE_GUID(IID_ISelectionProvider2, 0x14f68475, 0xee1c, 0x44f6, 0xa8, 0x69, 0xd2, 0x39, 0x38, 0x1f, 0x0f, 0xe7);
|
||||
MIDL_INTERFACE("14f68475-ee1c-44f6-a869-d239381f0fe7")
|
||||
ISelectionProvider2 : public ISelectionProvider
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE get_FirstSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **retVal) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE get_LastSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **retVal) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE get_CurrentSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **retVal) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE get_ItemCount(__RPC__out int *retVal) = 0;
|
||||
};
|
||||
|
||||
#ifdef __CRT_UUID_DECL
|
||||
__CRT_UUID_DECL(ISelectionProvider2, 0x14f68475, 0xee1c, 0x44f6, 0xa8, 0x69, 0xd2, 0x39, 0x38, 0x1f, 0x0f, 0xe7)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __ISelectionItemProvider_INTERFACE_DEFINED__
|
||||
#define __ISelectionItemProvider_INTERFACE_DEFINED__
|
||||
|
|
|
|||
|
|
@ -304,6 +304,7 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow
|
|||
*pRetVal = new QWindowsUiaToggleProvider(id());
|
||||
break;
|
||||
case UIA_SelectionPatternId:
|
||||
case UIA_SelectionPattern2Id:
|
||||
// Lists of items.
|
||||
if (accessible->role() == QAccessible::List
|
||||
|| accessible->role() == QAccessible::PageTabList) {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::GetSelection(SAFEARRAY *
|
|||
|
||||
// First put selected items in a list, then build a safe array with the right size.
|
||||
QList<QAccessibleInterface *> selectedList;
|
||||
for (int i = 0; i < accessible->childCount(); ++i) {
|
||||
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) {
|
||||
|
|
@ -116,6 +118,116 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_IsSelectionRequired(
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_FirstSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = nullptr;
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (!accessible)
|
||||
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)
|
||||
firstSelectedChild = child;
|
||||
} else if (child->state().selected) {
|
||||
firstSelectedChild = child;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!firstSelectedChild)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(firstSelectedChild))
|
||||
{
|
||||
*pRetVal = static_cast<IRawElementProviderSimple *>(childProvider);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_LastSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = nullptr;
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (!accessible)
|
||||
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)
|
||||
lastSelectedChild = child;
|
||||
} else if (child->state().selected) {
|
||||
lastSelectedChild = child;
|
||||
}
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
||||
if (!lastSelectedChild)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(lastSelectedChild))
|
||||
{
|
||||
*pRetVal = static_cast<IRawElementProviderSimple *>(childProvider);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_CurrentSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__;
|
||||
return get_FirstSelectedItem(pRetVal);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_ItemCount(__RPC__out int *pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = -1;
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
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)
|
||||
selectedCount++;
|
||||
} else if (child->state().selected) {
|
||||
selectedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*pRetVal = selectedCount;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_CONFIG(accessibility)
|
||||
|
|
|
|||
|
|
@ -13,17 +13,35 @@ QT_BEGIN_NAMESPACE
|
|||
|
||||
// Implements the Selection control pattern provider. Used for Lists.
|
||||
class QWindowsUiaSelectionProvider : public QWindowsUiaBaseProvider,
|
||||
public QWindowsComBase<ISelectionProvider>
|
||||
public QWindowsComBase<ISelectionProvider2>
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(QWindowsUiaSelectionProvider)
|
||||
public:
|
||||
explicit QWindowsUiaSelectionProvider(QAccessible::Id id);
|
||||
virtual ~QWindowsUiaSelectionProvider();
|
||||
|
||||
// override to support ISelectionProvider and ISelectionProvider2 at the same time
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override
|
||||
{
|
||||
HRESULT res = QWindowsComBase<ISelectionProvider2>::QueryInterface(id, iface);
|
||||
// QWindowsComBase<ISelectionProvider2>::QueryInterface doesn't handle ISelectionProvider,
|
||||
// from which ISelectionProvider2 inherits
|
||||
if (res == E_NOINTERFACE)
|
||||
res = qWindowsComQueryInterface<ISelectionProvider>(this, id, iface) ? S_OK : E_NOINTERFACE;
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
// ISelectionProvider
|
||||
HRESULT STDMETHODCALLTYPE GetSelection(SAFEARRAY **pRetVal) override;
|
||||
HRESULT STDMETHODCALLTYPE get_CanSelectMultiple(BOOL *pRetVal) override;
|
||||
HRESULT STDMETHODCALLTYPE get_IsSelectionRequired(BOOL *pRetVal) override;
|
||||
|
||||
// ISelectionProvider2
|
||||
HRESULT STDMETHODCALLTYPE get_FirstSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) override;
|
||||
HRESULT STDMETHODCALLTYPE get_LastSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) override;
|
||||
HRESULT STDMETHODCALLTYPE get_CurrentSelectedItem(__RPC__deref_out_opt IRawElementProviderSimple **pRetVal) override;
|
||||
HRESULT STDMETHODCALLTYPE get_ItemCount(__RPC__out int *pRetVal) override;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
Loading…
Reference in New Issue