a11y atspi: Bridge QAccessibleAttributesInterface to AT-SPI

This bridges the 2 currently existing attribute types for
the newly added QAccessibleAttributesInterface to AT-SPI2
by using the corresponding object attributes.

QAccessible::Attribute::Level semantically matches
the "aria-level" ARIA attribute, and is mapped to
the "level" AT-SPI object attribute as described in
the Core Accessibility API Mappings specification
for both, headings [1] and non-headings [2].

All of the key-value pairs set in the
QAccessible::Attribute::Custom attribute
are bridged to the AT-SPI level as object attributes
using the same names and values.

Together with a corresponding demo change [3] for LibreOffice
implementing support for the QAccessible::Attribute::Level
attribute in the qt6-based LibreOffice variant, this makes
the specific use-case for reporting the heading level
mentioned in QTBUG-119057 work with the Orca screen reader.

Also tweak the QAccessibleAttributesInterface::attributeValue
documentation to clarify the type to be returned in the
QVariant. (The newly added AT-SPI implementation has
corresponding asserts.)

[1] https://www.w3.org/TR/core-aam-1.2/#ariaLevelHeading
[2] https://www.w3.org/TR/core-aam-1.2/#ariaLevel
[3] https://gerrit.libreoffice.org/c/core/+/159309

Task-number: QTBUG-119057
Change-Id: I7ccdbbcd601c176319ca547d4bdf50b8f93bd7d8
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Michael Weghorn 2023-11-10 19:14:48 +01:00
parent 658939dae2
commit 2d3f49d797
3 changed files with 40 additions and 4 deletions

View File

@ -1647,7 +1647,7 @@ bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QS
sendReply(connection, message,
QVariant::fromValue(spiStateSetFromSpiStates(spiState)));
} else if (function == "GetAttributes"_L1) {
sendReply(connection, message, QVariant::fromValue(QSpiAttributeSet()));
sendReply(connection, message, QVariant::fromValue(getAttributes(interface)));
} else if (function == "GetRelationSet"_L1) {
sendReply(connection, message, QVariant::fromValue(relationSet(interface, connection)));
} else if (function == "GetApplication"_L1) {
@ -2305,6 +2305,38 @@ namespace
}
}
QSpiAttributeSet AtSpiAdaptor::getAttributes(QAccessibleInterface *interface) const
{
QSpiAttributeSet set;
QAccessibleAttributesInterface *attributesIface = interface->attributesInterface();
if (!attributesIface)
return set;
const QList<QAccessible::Attribute> attrKeys = attributesIface->attributeKeys();
for (QAccessible::Attribute key : attrKeys) {
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 to AT-SPI 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())
set.insert(name, val);
break;
}
case QAccessible::Attribute::Level:
Q_ASSERT(value.canConvert<int>());
set.insert(QStringLiteral("level"), QString::number(value.toInt()));
break;
default:
break;
}
}
return set;
}
// FIXME all attribute methods below should share code
QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int offset, bool includeDefaults) const
{

View File

@ -89,6 +89,7 @@ private:
// accessible helper functions
AtspiRole getRole(QAccessibleInterface *interface) const;
QSpiAttributeSet getAttributes(QAccessibleInterface *) const;
QSpiRelationArray relationSet(QAccessibleInterface *interface, const QDBusConnection &connection) const;
QStringList accessibleInterfaces(QAccessibleInterface *interface) const;

View File

@ -3192,7 +3192,7 @@ bool QAccessibleSelectionInterface::isSelected(QAccessibleInterface *childItem)
Attributes are key-value pairs. Values are stored in \l QVariant.
The \a QAccessible::Attributes enumeration describes the available keys and
The \l QAccessible::Attribute enumeration describes the available keys and
documents which type to use for the value of each key.
While the text-specific attributes handled by \l QAccessibleTextInterface::attributes
@ -3225,8 +3225,11 @@ QAccessibleAttributesInterface::~QAccessibleAttributesInterface()
Returns the value of the attribute \a key of this object.
If the specificed attribute is not set for this object, an invalid
\l QVariant is returned.
If the attribute is set for this object, a value of the type documented for the
given key in the documentation of the \l QAccessible::Attribute enumeration is
returned in the \l QVariant.
Otherwise, an invalid \l QVariant is returned.
*/
/*! \internal */