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 <volker.hilsheimer@qt.io>bb10
parent
3c93dedc06
commit
9ec1de2528
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue