Windows QPA: Add support to UiaRaiseNotificationEvent()

This change adds support to UiaRaiseNotificationEvent() in the
UI Automation-based accessibility code, and uses it to notify changes
in string-typed values, allowing Narrator, NVDA and other screen readers
to notice changes in the application state that were previously missed.

Fixes: QTBUG-75003
Change-Id: I646ca3a851ab7b69817d900b002eb91a3bf607a6
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
This commit is contained in:
Andre de la Rocha 2020-03-02 20:30:13 +01:00
parent 295a16b0d3
commit 566def740e
4 changed files with 49 additions and 6 deletions

View File

@ -53,6 +53,7 @@ QWindowsUiaWrapper::QWindowsUiaWrapper()
m_pUiaHostProviderFromHwnd = reinterpret_cast<PtrUiaHostProviderFromHwnd>(uiaLib.resolve("UiaHostProviderFromHwnd"));
m_pUiaRaiseAutomationPropertyChangedEvent = reinterpret_cast<PtrUiaRaiseAutomationPropertyChangedEvent>(uiaLib.resolve("UiaRaiseAutomationPropertyChangedEvent"));
m_pUiaRaiseAutomationEvent = reinterpret_cast<PtrUiaRaiseAutomationEvent>(uiaLib.resolve("UiaRaiseAutomationEvent"));
m_pUiaRaiseNotificationEvent = reinterpret_cast<PtrUiaRaiseNotificationEvent>(uiaLib.resolve("UiaRaiseNotificationEvent"));
m_pUiaClientsAreListening = reinterpret_cast<PtrUiaClientsAreListening>(uiaLib.resolve("UiaClientsAreListening"));
}
}
@ -68,7 +69,7 @@ QWindowsUiaWrapper *QWindowsUiaWrapper::instance()
return &wrapper;
}
// True if all symbols resolved.
// True if most symbols resolved (UiaRaiseNotificationEvent is optional).
BOOL QWindowsUiaWrapper::ready()
{
return m_pUiaReturnRawElementProvider
@ -113,5 +114,12 @@ HRESULT QWindowsUiaWrapper::raiseAutomationEvent(IRawElementProviderSimple *pPro
return m_pUiaRaiseAutomationEvent(pProvider, id);
}
HRESULT QWindowsUiaWrapper::raiseNotificationEvent(IRawElementProviderSimple *provider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId)
{
if (!m_pUiaRaiseNotificationEvent)
return UIA_E_NOTSUPPORTED;
return m_pUiaRaiseNotificationEvent(provider, notificationKind, notificationProcessing, displayString, activityId);
}
QT_END_NAMESPACE

View File

@ -80,17 +80,20 @@ public:
HRESULT hostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **ppProvider);
HRESULT raiseAutomationPropertyChangedEvent(IRawElementProviderSimple *pProvider, PROPERTYID id, VARIANT oldValue, VARIANT newValue);
HRESULT raiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id);
HRESULT raiseNotificationEvent(IRawElementProviderSimple *provider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId);
private:
typedef LRESULT (WINAPI *PtrUiaReturnRawElementProvider)(HWND, WPARAM, LPARAM, IRawElementProviderSimple *);
typedef HRESULT (WINAPI *PtrUiaHostProviderFromHwnd)(HWND, IRawElementProviderSimple **);
typedef HRESULT (WINAPI *PtrUiaRaiseAutomationPropertyChangedEvent)(IRawElementProviderSimple *, PROPERTYID, VARIANT, VARIANT);
typedef HRESULT (WINAPI *PtrUiaRaiseAutomationEvent)(IRawElementProviderSimple *, EVENTID);
typedef HRESULT (WINAPI *PtrUiaRaiseNotificationEvent)(IRawElementProviderSimple *, NotificationKind, NotificationProcessing, BSTR, BSTR);
typedef BOOL (WINAPI *PtrUiaClientsAreListening)();
PtrUiaReturnRawElementProvider m_pUiaReturnRawElementProvider = nullptr;
PtrUiaHostProviderFromHwnd m_pUiaHostProviderFromHwnd = nullptr;
PtrUiaRaiseAutomationPropertyChangedEvent m_pUiaRaiseAutomationPropertyChangedEvent = nullptr;
PtrUiaRaiseAutomationEvent m_pUiaRaiseAutomationEvent = nullptr;
PtrUiaRaiseNotificationEvent m_pUiaRaiseNotificationEvent = nullptr;
PtrUiaClientsAreListening m_pUiaClientsAreListening = nullptr;
};

View File

@ -162,6 +162,22 @@ enum ExpandCollapseState {
ExpandCollapseState_LeafNode = 3
};
enum NotificationKind {
NotificationKind_ItemAdded = 0,
NotificationKind_ItemRemoved = 1,
NotificationKind_ActionCompleted = 2,
NotificationKind_ActionAborted = 3,
NotificationKind_Other = 4
};
enum NotificationProcessing {
NotificationProcessing_ImportantAll = 0,
NotificationProcessing_ImportantMostRecent = 1,
NotificationProcessing_All = 2,
NotificationProcessing_MostRecent = 3,
NotificationProcessing_CurrentThenMostRecent = 4
};
struct UiaRect {
double left;
double top;

View File

@ -166,11 +166,27 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve
}
if (event->value().type() == QVariant::String) {
if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
// Notifies changes in string values.
// Tries to notify the change using UiaRaiseNotificationEvent(), which is only available on
// Windows 10 version 1709 or newer. Otherwise uses UiaRaiseAutomationPropertyChangedEvent().
BSTR displayString = bStrFromQString(event->value().toString());
BSTR activityId = bStrFromQString(QString());
HRESULT hr = QWindowsUiaWrapper::instance()->raiseNotificationEvent(provider, NotificationKind_Other,
NotificationProcessing_ImportantMostRecent,
displayString, activityId);
::SysFreeString(displayString);
::SysFreeString(activityId);
if (hr == static_cast<HRESULT>(UIA_E_NOTSUPPORTED)) {
VARIANT oldVal, newVal;
clearVariant(&oldVal);
setVariantString(event->value().toString(), &newVal);
QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal);
::SysFreeString(newVal.bstrVal);
}
}
} else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {