diff options
Diffstat (limited to 'src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp')
-rw-r--r-- | src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp | 168 |
1 files changed, 126 insertions, 42 deletions
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp index 29cd1c6237..07cd522746 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp @@ -18,12 +18,12 @@ #include "qwindowsuiagriditemprovider.h" #include "qwindowsuiawindowprovider.h" #include "qwindowsuiaexpandcollapseprovider.h" -#include "qwindowscombase.h" #include "qwindowscontext.h" #include "qwindowsuiautils.h" #include "qwindowsuiaprovidercache.h" #include <QtCore/qloggingcategory.h> +#include <QtGui/private/qaccessiblebridgeutils_p.h> #include <QtGui/qaccessible.h> #include <QtGui/qguiapplication.h> #include <QtGui/qwindow.h> @@ -62,9 +62,8 @@ QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessi return provider; } -QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *a, int initialRefCount) - : QWindowsUiaBaseProvider(QAccessible::uniqueId(a)), - m_ref(initialRefCount) +QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *a) + : QWindowsUiaBaseProvider(QAccessible::uniqueId(a)) { } @@ -206,35 +205,46 @@ void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event) } } -HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface) +void QWindowsUiaMainProvider::raiseNotification(QAccessibleAnnouncementEvent *event) { - if (!iface) - return E_INVALIDARG; - *iface = nullptr; - - QAccessibleInterface *accessible = accessibleInterface(); + if (QAccessibleInterface *accessible = event->accessibleInterface()) { + if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) { + BSTR message = bStrFromQString(event->message()); + QAccessible::AnnouncementPriority prio = event->priority(); + NotificationProcessing processing = (prio == QAccessible::AnnouncementPriority::Assertive) + ? NotificationProcessing_ImportantAll + : NotificationProcessing_All; + BSTR activityId = bStrFromQString(QString::fromLatin1("")); + UiaRaiseNotificationEvent(provider, NotificationKind_Other, processing, message, activityId); - const bool result = qWindowsComQueryUnknownInterfaceMulti<IRawElementProviderSimple>(this, iid, iface) - || qWindowsComQueryInterface<IRawElementProviderSimple>(this, iid, iface) - || qWindowsComQueryInterface<IRawElementProviderFragment>(this, iid, iface) - || (accessible && hwndForAccessible(accessible) && qWindowsComQueryInterface<IRawElementProviderFragmentRoot>(this, iid, iface)); - return result ? S_OK : E_NOINTERFACE; + ::SysFreeString(message); + ::SysFreeString(activityId); + } + } } -ULONG QWindowsUiaMainProvider::AddRef() +HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface) { - return ++m_ref; + HRESULT result = QComObject::QueryInterface(iid, iface); + + if (SUCCEEDED(result) && iid == __uuidof(IRawElementProviderFragmentRoot)) { + QAccessibleInterface *accessible = accessibleInterface(); + if (accessible && hwndForAccessible(accessible)) { + result = S_OK; + } else { + result = E_NOINTERFACE; + iface = nullptr; + } + } + + return result; } ULONG STDMETHODCALLTYPE QWindowsUiaMainProvider::Release() { QMutexLocker locker(&m_mutex); - if (!--m_ref) { - delete this; - return 0; - } - return m_ref; + return QComObject::Release(); } HRESULT QWindowsUiaMainProvider::get_ProviderOptions(ProviderOptions *pRetVal) @@ -379,6 +389,93 @@ void QWindowsUiaMainProvider::fillVariantArrayForRelation(QAccessibleInterface* pRetVal->parray = elements; } +void QWindowsUiaMainProvider::setAriaProperties(QAccessibleInterface *accessible, VARIANT *pRetVal) +{ + Q_ASSERT(accessible); + + QAccessibleAttributesInterface *attributesIface = accessible->attributesInterface(); + if (!attributesIface) + return; + + QString ariaString; + const QList<QAccessible::Attribute> attrKeys = attributesIface->attributeKeys(); + for (qsizetype i = 0; i < attrKeys.size(); ++i) { + if (i != 0) + ariaString += QStringLiteral(";"); + const QAccessible::Attribute key = attrKeys.at(i); + const QVariant value = attributesIface->attributeValue(key); + // see "Core Accessibility API Mappings" spec: https://www.w3.org/TR/core-aam-1.2/ + switch (key) { + case QAccessible::Attribute::Custom: + { + // forward custom attributes as-is + Q_ASSERT((value.canConvert<QHash<QString, QString>>())); + const QHash<QString, QString> attrMap = value.value<QHash<QString, QString>>(); + for (auto [name, val] : attrMap.asKeyValueRange()) { + if (name != *attrMap.keyBegin()) + ariaString += QStringLiteral(";"); + ariaString += name + QStringLiteral("=") + val; + } + break; + } + case QAccessible::Attribute::Level: + Q_ASSERT(value.canConvert<int>()); + ariaString += QStringLiteral("level=") + QString::number(value.toInt()); + break; + default: + break; + } + } + + setVariantString(ariaString, pRetVal); +} + +void QWindowsUiaMainProvider::setStyle(QAccessibleInterface *accessible, VARIANT *pRetVal) +{ + Q_ASSERT(accessible); + + QAccessibleAttributesInterface *attributesIface = accessible->attributesInterface(); + if (!attributesIface) + return; + + // currently, only heading styles are implemented here + if (accessible->role() != QAccessible::Role::Heading) + return; + + const QVariant levelVariant = attributesIface->attributeValue(QAccessible::Attribute::Level); + if (!levelVariant.isValid()) + return; + + Q_ASSERT(levelVariant.canConvert<int>()); + // UIA only has styles for heading levels 1-9 + const int level = levelVariant.toInt(); + if (level < 1 || level > 9) + return; + + const int styleId = styleIdForHeadingLevel(level); + setVariantI4(styleId, pRetVal); +} + +int QWindowsUiaMainProvider::styleIdForHeadingLevel(int headingLevel) +{ + // only heading levels 1-9 have a corresponding UIA style ID + Q_ASSERT(headingLevel > 0 && headingLevel <= 9); + + static constexpr int styles[] = { + StyleId_Heading1, + StyleId_Heading2, + StyleId_Heading3, + StyleId_Heading4, + StyleId_Heading5, + StyleId_Heading6, + StyleId_Heading7, + StyleId_Heading8, + StyleId_Heading9, + }; + + return styles[headingLevel - 1]; +} + HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal) { qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idProp; @@ -402,9 +499,12 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR // Accelerator key. setVariantString(accessible->text(QAccessible::Accelerator), pRetVal); break; + case UIA_AriaPropertiesPropertyId: + setAriaProperties(accessible, pRetVal); + break; case UIA_AutomationIdPropertyId: // Automation ID, which can be used by tools to select a specific control in the UI. - setVariantString(automationIdForAccessible(accessible), pRetVal); + setVariantString(QAccessibleBridgeUtils::accessibleId(accessible), pRetVal); break; case UIA_ClassNamePropertyId: // Class name. @@ -502,31 +602,15 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR setVariantString(name, pRetVal); break; } + case UIA_StyleIdAttributeId: + setStyle(accessible, pRetVal); + break; default: break; } return S_OK; } -// Generates an ID based on the name of the controls and their parents. -QString QWindowsUiaMainProvider::automationIdForAccessible(const QAccessibleInterface *accessible) -{ - QString result; - if (accessible) { - QObject *obj = accessible->object(); - while (obj) { - QString name = obj->objectName(); - if (name.isEmpty()) - return result; - if (!result.isEmpty()) - result.prepend(u'.'); - result.prepend(name); - obj = obj->parent(); - } - } - return result; -} - HRESULT QWindowsUiaMainProvider::get_HostRawElementProvider(IRawElementProviderSimple **pRetVal) { qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this; |