4#include <QtGui/qtguiglobal.h>
5#if QT_CONFIG(accessibility)
25#include <QtCore/qloggingcategory.h>
26#include <QtGui/qaccessible.h>
27#include <QtGui/qguiapplication.h>
28#include <QtGui/qwindow.h>
29#include <qpa/qplatforminputcontextfactory_p.h>
31#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
35#include <QtCore/qt_windows.h>
39using namespace QWindowsUiAutomation;
41QMutex QWindowsUiaMainProvider::m_mutex;
44QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessibleInterface *accessible)
51 QAccessible::Id
id = QAccessible::uniqueId(accessible);
52 QWindowsUiaProviderCache *providerCache = QWindowsUiaProviderCache::instance();
53 auto *provider = qobject_cast<QWindowsUiaMainProvider *>(providerCache->providerForId(
id));
58 provider =
new QWindowsUiaMainProvider(accessible);
59 providerCache->insert(
id, provider);
64QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *
a)
69QWindowsUiaMainProvider::~QWindowsUiaMainProvider()
73void QWindowsUiaMainProvider::notifyFocusChange(QAccessibleEvent *
event)
75 if (QAccessibleInterface *accessible =
event->accessibleInterface()) {
77 if (accessible->childCount()) {
78 if (QAccessibleInterface *
child = accessible->focusChild())
81 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible))
82 UiaRaiseAutomationEvent(provider, UIA_AutomationFocusChangedEventId);
86void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *
event)
88 if (QAccessibleInterface *accessible =
event->accessibleInterface()) {
89 if (
event->changedStates().checked ||
event->changedStates().checkStateMixed) {
91 if (accessible->role() == QAccessible::CheckBox) {
92 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
93 VARIANT oldVal, newVal;
94 clearVariant(&oldVal);
95 int toggleState = ToggleState_Off;
96 if (accessible->state().checked)
97 toggleState = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On;
98 setVariantI4(toggleState, &newVal);
99 UiaRaiseAutomationPropertyChangedEvent(provider, UIA_ToggleToggleStatePropertyId, oldVal, newVal);
103 if (
event->changedStates().active) {
104 if (accessible->role() == QAccessible::Window) {
106 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
107 if (accessible->state().active) {
108 UiaRaiseAutomationEvent(provider, UIA_Window_WindowOpenedEventId);
109 if (QAccessibleInterface *focused = accessible->focusChild()) {
110 if (QWindowsUiaMainProvider *focusedProvider = providerForAccessible(focused))
111 UiaRaiseAutomationEvent(focusedProvider, UIA_AutomationFocusChangedEventId);
114 UiaRaiseAutomationEvent(provider, UIA_Window_WindowClosedEventId);
122void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *
event)
124 if (QAccessibleInterface *accessible =
event->accessibleInterface()) {
125 if (accessible->role() == QAccessible::ComboBox && accessible->childCount() > 0) {
126 QAccessibleInterface *listacc = accessible->child(0);
127 if (listacc && listacc->role() == QAccessible::List) {
128 int count = listacc->childCount();
130 QAccessibleInterface *
item = listacc->child(
i);
131 if (
item &&
item->isValid() &&
item->text(QAccessible::Name) ==
event->value()) {
132 if (!
item->state().selected) {
133 if (QAccessibleActionInterface *actionInterface =
item->actionInterface())
134 actionInterface->doAction(QAccessibleActionInterface::toggleAction());
141 if (
event->value().typeId() == QMetaType::QString) {
142 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
144 VARIANT oldVal, newVal;
145 clearVariant(&oldVal);
146 setVariantString(
event->value().toString(), &newVal);
147 UiaRaiseAutomationPropertyChangedEvent(provider, UIA_ValueValuePropertyId, oldVal, newVal);
149 }
else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
150 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
152 VARIANT oldVal, newVal;
153 clearVariant(&oldVal);
154 setVariantDouble(valueInterface->currentValue().toDouble(), &newVal);
155 UiaRaiseAutomationPropertyChangedEvent(provider, UIA_RangeValueValuePropertyId, oldVal, newVal);
161void QWindowsUiaMainProvider::notifyNameChange(QAccessibleEvent *
event)
163 if (QAccessibleInterface *accessible =
event->accessibleInterface()) {
166 if (accessible->role() == QAccessible::ComboBox) {
167 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
168 VARIANT oldVal, newVal;
169 clearVariant(&oldVal);
170 setVariantString(accessible->text(QAccessible::Name), &newVal);
171 UiaRaiseAutomationPropertyChangedEvent(provider, UIA_NamePropertyId, oldVal, newVal);
172 ::SysFreeString(newVal.bstrVal);
178void QWindowsUiaMainProvider::notifySelectionChange(QAccessibleEvent *
event)
180 if (QAccessibleInterface *accessible =
event->accessibleInterface()) {
181 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
182 UiaRaiseAutomationEvent(provider, UIA_SelectionItem_ElementSelectedEventId);
188void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *
event)
190 if (QAccessibleInterface *accessible =
event->accessibleInterface()) {
191 if (accessible->textInterface()) {
192 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
193 if (
event->type() == QAccessible::TextSelectionChanged) {
194 UiaRaiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
195 }
else if (
event->type() == QAccessible::TextCaretMoved) {
196 if (!accessible->state().readOnly) {
197 UiaRaiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
200 UiaRaiseAutomationEvent(provider, UIA_Text_TextChangedEventId);
207void QWindowsUiaMainProvider::raiseNotification(QAccessibleAnnouncementEvent *
event)
209 if (QAccessibleInterface *accessible =
event->accessibleInterface()) {
210 if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
212 QAccessible::AnnouncementPriority prio =
event->priority();
213 NotificationProcessing processing = (prio == QAccessible::AnnouncementPriority::Assertive)
214 ? NotificationProcessing_ImportantAll
215 : NotificationProcessing_All;
217 UiaRaiseNotificationEvent(provider, NotificationKind_Other, processing,
message, activityId);
220 ::SysFreeString(activityId);
225HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface)
229 if (SUCCEEDED(
result) && iid == __uuidof(IRawElementProviderFragmentRoot)) {
230 QAccessibleInterface *accessible = accessibleInterface();
231 if (accessible && hwndForAccessible(accessible)) {
242ULONG STDMETHODCALLTYPE QWindowsUiaMainProvider::Release()
246 return QComObject::Release();
249HRESULT QWindowsUiaMainProvider::get_ProviderOptions(ProviderOptions *pRetVal)
254 *pRetVal =
static_cast<ProviderOptions
>(ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading);
259HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknown **pRetVal)
261 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idPattern;
267 QAccessibleInterface *accessible = accessibleInterface();
269 return UIA_E_ELEMENTNOTAVAILABLE;
272 case UIA_WindowPatternId:
273 if (accessible->parent() && (accessible->parent()->role() == QAccessible::Application)) {
274 *pRetVal =
new QWindowsUiaWindowProvider(
id());
277 case UIA_TextPatternId:
278 case UIA_TextPattern2Id:
280 if (accessible->textInterface()) {
281 *pRetVal =
new QWindowsUiaTextProvider(
id());
284 case UIA_ValuePatternId:
286 if (accessible->role() != QAccessible::StaticText)
287 *pRetVal =
new QWindowsUiaValueProvider(
id());
289 case UIA_RangeValuePatternId:
291 if (accessible->valueInterface()) {
292 *pRetVal =
new QWindowsUiaRangeValueProvider(
id());
295 case UIA_TogglePatternId:
297 if (accessible->state().checkable)
298 *pRetVal =
new QWindowsUiaToggleProvider(
id());
300 case UIA_SelectionPatternId:
301 case UIA_SelectionPattern2Id:
303 if (accessible->selectionInterface()
304 || accessible->role() == QAccessible::List
305 || accessible->role() == QAccessible::PageTabList) {
306 *pRetVal =
new QWindowsUiaSelectionProvider(
id());
309 case UIA_SelectionItemPatternId:
311 if ((accessible->parent() && accessible->parent()->selectionInterface())
312 || (accessible->role() == QAccessible::RadioButton)
313 || (accessible->role() == QAccessible::ListItem)
314 || (accessible->role() == QAccessible::PageTab)) {
315 *pRetVal =
new QWindowsUiaSelectionItemProvider(
id());
318 case UIA_TablePatternId:
320 if (accessible->tableInterface()
321 && ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
322 *pRetVal =
new QWindowsUiaTableProvider(
id());
325 case UIA_TableItemPatternId:
327 if (accessible->tableCellInterface()
328 && ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
329 *pRetVal =
new QWindowsUiaTableItemProvider(
id());
332 case UIA_GridPatternId:
334 if (accessible->tableInterface()
335 && ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
336 *pRetVal =
new QWindowsUiaGridProvider(
id());
339 case UIA_GridItemPatternId:
341 if (accessible->tableCellInterface()
342 && ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
343 *pRetVal =
new QWindowsUiaGridItemProvider(
id());
346 case UIA_InvokePatternId:
348 if (accessible->actionInterface()) {
349 *pRetVal =
new QWindowsUiaInvokeProvider(
id());
352 case UIA_ExpandCollapsePatternId:
354 if ((accessible->role() == QAccessible::MenuItem
355 && accessible->childCount() > 0
356 && accessible->child(0)->role() == QAccessible::PopupMenu)
357 || accessible->role() == QAccessible::ComboBox
358 || (accessible->role() == QAccessible::TreeItem && accessible->state().expandable)) {
359 *pRetVal =
new QWindowsUiaExpandCollapseProvider(
id());
369void QWindowsUiaMainProvider::fillVariantArrayForRelation(QAccessibleInterface* accessible,
370 QAccessible::Relation relation, VARIANT *pRetVal)
374 typedef QPair<QAccessibleInterface*, QAccessible::Relation> RelationPair;
375 const QList<RelationPair> relationInterfaces = accessible->relations(relation);
376 if (relationInterfaces.empty())
379 SAFEARRAY *
elements = SafeArrayCreateVector(VT_UNKNOWN, 0, relationInterfaces.size());
380 for (LONG
i = 0;
i < relationInterfaces.size(); ++
i) {
381 if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(relationInterfaces.at(
i).first)) {
382 SafeArrayPutElement(
elements, &
i,
static_cast<IRawElementProviderSimple*
>(childProvider));
383 childProvider->Release();
387 pRetVal->vt = VT_UNKNOWN | VT_ARRAY;
391HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal)
393 qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idProp;
397 clearVariant(pRetVal);
399 QAccessibleInterface *accessible = accessibleInterface();
401 return UIA_E_ELEMENTNOTAVAILABLE;
403 bool topLevelWindow = accessible->parent() && (accessible->parent()->role() == QAccessible::Application);
406 case UIA_ProcessIdPropertyId:
408 setVariantI4(
int(GetCurrentProcessId()), pRetVal);
410 case UIA_AccessKeyPropertyId:
412 setVariantString(accessible->text(QAccessible::Accelerator), pRetVal);
414 case UIA_AutomationIdPropertyId:
416 setVariantString(automationIdForAccessible(accessible), pRetVal);
418 case UIA_ClassNamePropertyId:
420 if (
QObject *
o = accessible->object()) {
425 case UIA_DescribedByPropertyId:
426 fillVariantArrayForRelation(accessible, QAccessible::DescriptionFor, pRetVal);
428 case UIA_FlowsFromPropertyId:
429 fillVariantArrayForRelation(accessible, QAccessible::FlowsTo, pRetVal);
431 case UIA_FlowsToPropertyId:
432 fillVariantArrayForRelation(accessible, QAccessible::FlowsFrom, pRetVal);
434 case UIA_FrameworkIdPropertyId:
437 case UIA_ControlTypePropertyId:
438 if (topLevelWindow) {
440 setVariantI4(UIA_WindowControlTypeId, pRetVal);
443 auto controlType = roleToControlTypeId(accessible->role());
452 if (controlType == UIA_EditControlTypeId && (!imModuleEmpty || nativeVKDisabled))
453 controlType = UIA_TextControlTypeId;
455 setVariantI4(controlType, pRetVal);
458 case UIA_HelpTextPropertyId:
459 setVariantString(accessible->text(QAccessible::Help), pRetVal);
461 case UIA_HasKeyboardFocusPropertyId:
462 if (topLevelWindow) {
464 setVariantBool(accessible->state().active, pRetVal);
466 setVariantBool(accessible->state().focused, pRetVal);
469 case UIA_IsKeyboardFocusablePropertyId:
470 if (topLevelWindow) {
472 setVariantBool(
true, pRetVal);
474 setVariantBool(accessible->state().focusable, pRetVal);
477 case UIA_IsOffscreenPropertyId:
478 setVariantBool(accessible->state().offscreen, pRetVal);
480 case UIA_IsContentElementPropertyId:
481 setVariantBool(
true, pRetVal);
483 case UIA_IsControlElementPropertyId:
484 setVariantBool(
true, pRetVal);
486 case UIA_IsEnabledPropertyId:
487 setVariantBool(!accessible->state().disabled, pRetVal);
489 case UIA_IsPasswordPropertyId:
490 setVariantBool(accessible->role() == QAccessible::EditableText
491 && accessible->state().passwordEdit, pRetVal);
493 case UIA_IsPeripheralPropertyId:
500 case UIA_IsDialogPropertyId:
501 setVariantBool(accessible->role() == QAccessible::Dialog
502 || accessible->role() == QAccessible::AlertMessage, pRetVal);
504 case UIA_FullDescriptionPropertyId:
505 setVariantString(accessible->text(QAccessible::Description), pRetVal);
507 case UIA_NamePropertyId: {
508 QString name = accessible->text(QAccessible::Name);
509 if (
name.isEmpty() && topLevelWindow)
511 setVariantString(
name, pRetVal);
521QString QWindowsUiaMainProvider::automationIdForAccessible(
const QAccessibleInterface *accessible)
539HRESULT QWindowsUiaMainProvider::get_HostRawElementProvider(IRawElementProviderSimple **pRetVal)
541 qCDebug(lcQpaUiAutomation) << __FUNCTION__ <<
this;
548 if (QAccessibleInterface *accessible = accessibleInterface()) {
549 if (HWND hwnd = hwndForAccessible(accessible)) {
550 return UiaHostProviderFromHwnd(hwnd, pRetVal);
557HRESULT QWindowsUiaMainProvider::Navigate(NavigateDirection
direction, IRawElementProviderFragment **pRetVal)
559 qCDebug(lcQpaUiAutomation) << __FUNCTION__ <<
direction <<
" this: " <<
this;
565 QAccessibleInterface *accessible = accessibleInterface();
567 return UIA_E_ELEMENTNOTAVAILABLE;
569 QAccessibleInterface *targetacc =
nullptr;
571 if (
direction == NavigateDirection_Parent) {
572 if (QAccessibleInterface *parent = accessible->parent()) {
574 if (parent->isValid() && parent->role() != QAccessible::Application) {
579 QAccessibleInterface *parent =
nullptr;
583 case NavigateDirection_FirstChild:
588 case NavigateDirection_LastChild:
590 index = accessible->childCount() - 1;
593 case NavigateDirection_NextSibling:
594 if ((parent = accessible->parent()))
595 index = parent->indexOfChild(accessible) + 1;
598 case NavigateDirection_PreviousSibling:
599 if ((parent = accessible->parent()))
600 index = parent->indexOfChild(accessible) - 1;
608 if (parent && parent->isValid()) {
610 if (QAccessibleInterface *
child = parent->child(
index)) {
611 if (
child->isValid() && !
child->state().invisible) {
621 *pRetVal = providerForAccessible(targetacc);
626HRESULT QWindowsUiaMainProvider::GetRuntimeId(SAFEARRAY **pRetVal)
628 qCDebug(lcQpaUiAutomation) << __FUNCTION__ <<
this;
634 QAccessibleInterface *accessible = accessibleInterface();
636 return UIA_E_ELEMENTNOTAVAILABLE;
640 int rtId[] = { UiaAppendRuntimeId, int(
id()) };
642 if ((*pRetVal = SafeArrayCreateVector(VT_I4, 0, 2))) {
643 for (LONG
i = 0;
i < 2; ++
i)
644 SafeArrayPutElement(*pRetVal, &
i, &rtId[
i]);
650HRESULT QWindowsUiaMainProvider::get_BoundingRectangle(UiaRect *pRetVal)
652 qCDebug(lcQpaUiAutomation) << __FUNCTION__ <<
this;
657 QAccessibleInterface *accessible = accessibleInterface();
659 return UIA_E_ELEMENTNOTAVAILABLE;
663 return UIA_E_ELEMENTNOTAVAILABLE;
665 rectToNativeUiaRect(accessible->rect(),
window, pRetVal);
669HRESULT QWindowsUiaMainProvider::GetEmbeddedFragmentRoots(SAFEARRAY **pRetVal)
671 qCDebug(lcQpaUiAutomation) << __FUNCTION__ <<
this;
681HRESULT QWindowsUiaMainProvider::SetFocus()
683 qCDebug(lcQpaUiAutomation) << __FUNCTION__ <<
this;
685 QAccessibleInterface *accessible = accessibleInterface();
687 return UIA_E_ELEMENTNOTAVAILABLE;
689 QAccessibleActionInterface *actionInterface = accessible->actionInterface();
690 if (!actionInterface)
691 return UIA_E_ELEMENTNOTAVAILABLE;
693 actionInterface->doAction(QAccessibleActionInterface::setFocusAction());
697HRESULT QWindowsUiaMainProvider::get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal)
699 qCDebug(lcQpaUiAutomation) << __FUNCTION__ <<
this;
707 if (QAccessibleInterface *accessible = accessibleInterface()) {
709 if (QAccessibleInterface *rootacc =
window->accessibleRoot()) {
710 *pRetVal = providerForAccessible(rootacc);
718HRESULT QWindowsUiaMainProvider::ElementProviderFromPoint(
double x,
double y, IRawElementProviderFragment **pRetVal)
720 qCDebug(lcQpaUiAutomation) << __FUNCTION__ <<
x <<
y;
727 QAccessibleInterface *accessible = accessibleInterface();
729 return UIA_E_ELEMENTNOTAVAILABLE;
733 return UIA_E_ELEMENTNOTAVAILABLE;
736 UiaPoint uiaPoint = {
x,
y};
738 nativeUiaPointToPoint(uiaPoint,
window, &point);
740 QAccessibleInterface *targetacc = accessible->childAt(point.
x(), point.
y());
743 QAccessibleInterface *acc = targetacc;
748 if (targetacc->textInterface())
break;
749 acc = acc->childAt(point.
x(), point.
y());
751 *pRetVal = providerForAccessible(targetacc);
757HRESULT QWindowsUiaMainProvider::GetFocus(IRawElementProviderFragment **pRetVal)
759 qCDebug(lcQpaUiAutomation) << __FUNCTION__ <<
this;
765 if (QAccessibleInterface *accessible = accessibleInterface()) {
766 if (QAccessibleInterface *focusacc = accessible->focusChild()) {
767 *pRetVal = providerForAccessible(focusacc);
The QAccessible class provides enums and static functions related to accessibility.
static bool testAttribute(Qt::ApplicationAttribute attribute)
Returns true if attribute attribute is set; otherwise returns false.
QString applicationName
the name of this application
static QStringList requested()
\inmodule QtCore\reentrant
constexpr int x() const noexcept
Returns the x coordinate of this point.
constexpr int y() const noexcept
Returns the y coordinate of this point.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QString & prepend(QChar c)
Combined button and popup list for selecting options.
constexpr QBindableInterface iface
@ AA_DisableNativeVirtualKeyboard
#define qCDebug(category,...)
GLint GLint GLint GLint GLint x
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLenum GLsizei count
GLuint GLsizei const GLchar * message
#define QStringLiteral(str)
static const QTextHtmlElement elements[Html_NumElements]
const char className[16]
[1]