summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Weghorn <m.weghorn@posteo.de>2023-07-14 11:50:17 +0100
committerMichael Weghorn <m.weghorn@posteo.de>2023-07-18 22:49:53 +0200
commit3bace699bf6f5a8187e61d2f99b7dcb033c0ea63 (patch)
treec187bbcb0dc699695ff76057af2ec7baa7158943
parent90af40ccd07fda57f966107c5fd8ffcbd955b384 (diff)
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 9d16d5e2245c26e5746fd7609300b84a2a983457. 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>
-rw-r--r--src/gui/accessible/windows/apisupport/uiapatternids_p.h1
-rw-r--r--src/gui/accessible/windows/apisupport/uiaserverinterfaces_p.h18
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp1
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp114
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h20
5 files changed, 152 insertions, 2 deletions
diff --git a/src/gui/accessible/windows/apisupport/uiapatternids_p.h b/src/gui/accessible/windows/apisupport/uiapatternids_p.h
index 0ff463cd36..676c3fe24e 100644
--- a/src/gui/accessible/windows/apisupport/uiapatternids_p.h
+++ b/src/gui/accessible/windows/apisupport/uiapatternids_p.h
@@ -49,5 +49,6 @@
#define UIA_DropTargetPatternId 10031
#define UIA_TextEditPatternId 10032
#define UIA_CustomNavigationPatternId 10033
+#define UIA_SelectionPattern2Id 10034
#endif
diff --git a/src/gui/accessible/windows/apisupport/uiaserverinterfaces_p.h b/src/gui/accessible/windows/apisupport/uiaserverinterfaces_p.h
index 6cf15cacb0..244176921b 100644
--- a/src/gui/accessible/windows/apisupport/uiaserverinterfaces_p.h
+++ b/src/gui/accessible/windows/apisupport/uiaserverinterfaces_p.h
@@ -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__
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index 23beeae283..93b9d705c0 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -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) {
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp
index 45f3b20552..b5bf6c12e8 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp
@@ -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)
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h
index 9a187c9413..8717e54ee7 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h
@@ -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