summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Trillmann <[email protected]>2024-03-13 14:00:38 +0100
committerJens Trillmann <[email protected]>2024-06-01 22:58:20 +0200
commit9ec1de2528b871099d416d15592fcc5ef9242a64 (patch)
treecd16f1254e0685f410012dd72605ba133ae9450f
parent3c93dedc063bf453bcda581b4e9ccab5a810c80f (diff)
Add Identifier role to QAccessible and use it in OS interfaces
* Unify the default identifier creation for QAccessibleInterface on all platforms to be the same as the previous identifier on Linux. This may change some identifiers on Windows. [ChangeLog][QAccessible][QAccessibleInterface] Add possibility to add unique identifier to QAccessibleInterface to give a11y elements consistent identifiers. Task-number: QTBUG-123361 Change-Id: I8c42956a4c497e71909d71dcb27bc87433937b69 Reviewed-by: Volker Hilsheimer <[email protected]>
-rw-r--r--src/gui/accessible/linux/atspiadaptor.cpp23
-rw-r--r--src/gui/accessible/qaccessible.cpp25
-rw-r--r--src/gui/accessible/qaccessible_base.h2
-rw-r--r--src/gui/accessible/qaccessiblebridgeutils.cpp22
-rw-r--r--src/gui/accessible/qaccessiblebridgeutils_p.h2
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.cpp4
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm6
-rw-r--r--src/plugins/platforms/ios/quiaccessibilityelement.mm13
-rw-r--r--src/plugins/platforms/wasm/qwasmaccessibility.cpp4
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp22
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h1
11 files changed, 74 insertions, 50 deletions
diff --git a/src/gui/accessible/linux/atspiadaptor.cpp b/src/gui/accessible/linux/atspiadaptor.cpp
index 5e7a452a335..722723207a3 100644
--- a/src/gui/accessible/linux/atspiadaptor.cpp
+++ b/src/gui/accessible/linux/atspiadaptor.cpp
@@ -1352,6 +1352,7 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
case QAccessible::HelpChanged:
case QAccessible::DefaultActionChanged:
case QAccessible::AcceleratorChanged:
+ case QAccessible::IdentifierChanged:
case QAccessible::InvalidEvent:
break;
}
@@ -1555,26 +1556,6 @@ void AtSpiAdaptor::registerApplication()
delete registry;
}
-namespace {
-QString accessibleIdForAccessible(QAccessibleInterface *accessible)
-{
- QString result;
- while (accessible) {
- if (!result.isEmpty())
- result.prepend(u'.');
- if (auto obj = accessible->object()) {
- const QString name = obj->objectName();
- if (!name.isEmpty())
- result.prepend(name);
- else
- result.prepend(QString::fromUtf8(obj->metaObject()->className()));
- }
- accessible = accessible->parent();
- }
- return result;
-}
-} // namespace
-
// Accessible
bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
{
@@ -1665,7 +1646,7 @@ bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QS
connection.send(message.createReply(QVariant::fromValue(children)));
} else if (function == "GetAccessibleId"_L1) {
sendReply(connection, message,
- QVariant::fromValue(QDBusVariant(accessibleIdForAccessible(interface))));
+ QVariant::fromValue(QDBusVariant(QAccessibleBridgeUtils::accessibleId(interface))));
} else {
qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::accessibleInterface does not implement" << function << message.path();
return false;
diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp
index eec779efb10..b75712c3e86 100644
--- a/src/gui/accessible/qaccessible.cpp
+++ b/src/gui/accessible/qaccessible.cpp
@@ -206,6 +206,7 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
clicked or via a key press.
\value HypertextLinkSelected A hypertext link has been selected.
\value HypertextNLinksChanged
+ \value [since 6.8] IdentifierChanged The identifier of an object has changed.
\value LocationChanged An object's location on the screen has changed.
\value MenuCommand A menu item is triggered.
\value MenuEnd A menu has been closed (Qt uses PopupMenuEnd for all
@@ -389,14 +390,15 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
This enum specifies string information that an accessible object
returns.
- \value Name The name of the object. This can be used both
- as an identifier or a short description by
- accessible clients.
- \value Description A short text describing the object.
- \value Value The value of the object.
- \value Help A longer text giving information about how to use the object.
- \value Accelerator The keyboard shortcut that executes the object's default action.
- \value UserText The first value to be used for user defined text.
+ \value Name The name of the object. This can be used both
+ as an identifier or a short description by
+ accessible clients.
+ \value Description A short text describing the object.
+ \value Value The value of the object.
+ \value Help A longer text giving information about how to use the object.
+ \value Accelerator The keyboard shortcut that executes the object's default action.
+ \value UserText The first value to be used for user defined text.
+ \value [since 6.8] Identifier An identifier for the object for e.g. UI tests.
\omitvalue DebugDescription
*/
@@ -1249,6 +1251,13 @@ QAccessibleInterface *QAccessibleInterface::focusChild() const
tool buttons also have shortcut keys and usually display them in
their tooltip.
+ The \l QAccessible::Identifier can be explicitly set to provide an
+ ID to assistive technologies. This can be especially useful for UI tests.
+ If no identifier has been explicitly set, the identifier is set by the
+ respective interface to an ID based on \l QObject::objectName or its
+ class name and \l QObject::objectName or class name of the parents
+ in its parents chain.
+
All objects provide a string for \l QAccessible::Name.
\sa role(), state()
diff --git a/src/gui/accessible/qaccessible_base.h b/src/gui/accessible/qaccessible_base.h
index 1ca3dadc36d..e164a2ad6bb 100644
--- a/src/gui/accessible/qaccessible_base.h
+++ b/src/gui/accessible/qaccessible_base.h
@@ -102,6 +102,7 @@ public:
DefaultActionChanged = 0x80B0,
AcceleratorChanged = 0x80C0,
Announcement = 0x80D0,
+ IdentifierChanged = 0x80E0,
InvalidEvent
};
@@ -324,6 +325,7 @@ public:
Help,
Accelerator,
DebugDescription,
+ Identifier,
UserText = 0x0000ffff
};
diff --git a/src/gui/accessible/qaccessiblebridgeutils.cpp b/src/gui/accessible/qaccessiblebridgeutils.cpp
index 994f95fee9f..0f91927d4c1 100644
--- a/src/gui/accessible/qaccessiblebridgeutils.cpp
+++ b/src/gui/accessible/qaccessiblebridgeutils.cpp
@@ -72,6 +72,28 @@ bool performEffectiveAction(QAccessibleInterface *iface, const QString &actionNa
return true;
}
+QString accessibleId(QAccessibleInterface *accessible) {
+ QString result;
+ if (!accessible)
+ return result;
+ result = accessible->text(QAccessible::Identifier);
+ if (!result.isEmpty())
+ return result;
+ while (accessible) {
+ if (!result.isEmpty())
+ result.prepend(u'.');
+ if (auto obj = accessible->object()) {
+ const QString name = obj->objectName();
+ if (!name.isEmpty())
+ result.prepend(name);
+ else
+ result.prepend(QString::fromUtf8(obj->metaObject()->className()));
+ }
+ accessible = accessible->parent();
+ }
+ return result;
+}
+
} //namespace
QT_END_NAMESPACE
diff --git a/src/gui/accessible/qaccessiblebridgeutils_p.h b/src/gui/accessible/qaccessiblebridgeutils_p.h
index b65a4d0b6cb..d7b88eaa58e 100644
--- a/src/gui/accessible/qaccessiblebridgeutils_p.h
+++ b/src/gui/accessible/qaccessiblebridgeutils_p.h
@@ -17,6 +17,7 @@
#include <QtGui/private/qtguiglobal_p.h>
+#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
#include <QtGui/qaccessible.h>
@@ -27,6 +28,7 @@ QT_BEGIN_NAMESPACE
namespace QAccessibleBridgeUtils {
Q_GUI_EXPORT QStringList effectiveActionNames(QAccessibleInterface *iface);
Q_GUI_EXPORT bool performEffectiveAction(QAccessibleInterface *iface, const QString &actionName);
+ Q_GUI_EXPORT QString accessibleId(QAccessibleInterface *accessible);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp
index 805616e481a..2010a9e03b2 100644
--- a/src/plugins/platforms/android/androidjniaccessibility.cpp
+++ b/src/plugins/platforms/android/androidjniaccessibility.cpp
@@ -470,6 +470,7 @@ namespace QtAndroidAccessibility
QAccessible::Role role;
QStringList actions;
QString description;
+ QString identifier;
bool hasTextSelection = false;
int selectionStart = 0;
int selectionEnd = 0;
@@ -485,6 +486,7 @@ namespace QtAndroidAccessibility
info.role = iface->role();
info.actions = QAccessibleBridgeUtils::effectiveActionNames(iface);
info.description = descriptionForInterface(iface);
+ info.identifier = QAccessibleBridgeUtils::accessibleId(iface);
QAccessibleTextInterface *textIface = iface->textInterface();
if (textIface && (textIface->selectionCount() > 0)) {
info.hasTextSelection = true;
@@ -550,6 +552,8 @@ namespace QtAndroidAccessibility
//CALL_METHOD(node, "setText", "(Ljava/lang/CharSequence;)V", jdesc)
env->CallVoidMethod(node, m_setContentDescriptionMethodID, jdesc);
+ QJniObject(node).callMethod<void>("setViewIdResourceName", info.identifier);
+
return true;
}
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
index 8d4d6d683d6..b319dd072e8 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
@@ -520,6 +520,12 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
return nil;
}
+- (NSString*) accessibilityIdentifier {
+ if (QAccessibleInterface *iface = self.qtInterface)
+ return QAccessibleBridgeUtils::accessibleId(iface).toNSString();
+ return nil;
+}
+
- (BOOL) isAccessibilityEnabled {
if (QAccessibleInterface *iface = self.qtInterface)
return !iface->state().disabled;
diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm
index 39b2cb8a50f..fa54f61967e 100644
--- a/src/plugins/platforms/ios/quiaccessibilityelement.mm
+++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm
@@ -10,6 +10,8 @@
#include "uistrings_p.h"
#include "qioswindow.h"
+#include <QtGui/private/qaccessiblebridgeutils_p.h>
+
QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement);
@implementation QMacAccessibilityElement
@@ -70,6 +72,17 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement);
return iface->text(QAccessible::Name).toNSString();
}
+
+- (NSString*)accessibilityIdentifier
+{
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (!iface) {
+ qWarning() << "invalid accessible interface for: " << self.axid;
+ return @"";
+ }
+ return QAccessibleBridgeUtils::accessibleId(iface).toNSString();
+}
+
- (NSString*)accessibilityHint
{
QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.cpp b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
index 4c3cb46ba3d..2e430176bec 100644
--- a/src/plugins/platforms/wasm/qwasmaccessibility.cpp
+++ b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
@@ -284,6 +284,10 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac
element = document.call<emscripten::val>("createElement", std::string("div"));
}
+ QString id = QAccessibleBridgeUtils::accessibleId(iface);
+ if (iface->role() != QAccessible::PageTabList)
+ element.call<void>("setAttribute", std::string("id"), id.toStdString());
+
return element;
}();
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index be88ab4ae8d..07cd5227463 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -23,6 +23,7 @@
#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>
@@ -503,7 +504,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
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.
@@ -610,25 +611,6 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
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;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
index dafe8779741..8ea343e4253 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
@@ -59,7 +59,6 @@ public:
HRESULT STDMETHODCALLTYPE GetFocus(IRawElementProviderFragment **pRetVal) override;
private:
- QString automationIdForAccessible(const QAccessibleInterface *accessible);
static void fillVariantArrayForRelation(QAccessibleInterface *accessible, QAccessible::Relation relation, VARIANT *pRetVal);
static void setAriaProperties(QAccessibleInterface *accessible, VARIANT *pRetVal);
static void setStyle(QAccessibleInterface *accessible, VARIANT *pRetVal);