a11y uia: Bridge QAccessibleAttributesInterface to UIA
This bridges the 2 currently existing attribute types for the newly added QAccessibleAttributesInterface to UIA. As described in the AT-SPI bridge implementation already, QAccessible::Attribute::Level semantically matches the "aria-level" ARIA attribute, and is mapped to a "level" property for the UIA_AriaPropertiesPropertyId as described in the Core Accessibility API Mappings specification for both, headings [1] and non-headings [2]. Map all attributes set in QAccessible::Attribute::Custom to that property as well, keeping key and value as they are. As described in the UIA Element Properties Identifiers doc [3] for UIA_AriaPropertiesPropertyId: > AriaProperties is a collection of Name/Value pairs with > delimiters of = (equals) and ; (semicolon), for example, > "checked=true;disabled=false". In addition, if the level attribute is set for an object of role heading, report the corresponding StyleId_Heading for UIA_StyleIdAttributeId, as also described in the Core Accessibility API Mappings spec [1]. For MingW, add UIA_StyleIdAttributeId and StyleId_Heading<NUMBER> defines to qwindowsuiautomation.h, as the MingW headers apparently don't have them yet (see log of failed MingW builds without those defines: [4] [5]). [1] https://www.w3.org/TR/core-aam-1.2/#ariaLevelHeading [2] https://www.w3.org/TR/core-aam-1.2/#ariaLevel [3] https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-automation-element-propids [4]bb10051e46739b/build_1716364832/log.txt.gz[5]b8e6241678/build_1716370445/log.txt.gzFixes: QTBUG-119057 Change-Id: I00b15e95c35c0f31ba34161bc061a3085fc28682 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
parent
2d4993899f
commit
bd0a6e8307
|
|
@ -388,6 +388,93 @@ void QWindowsUiaMainProvider::fillVariantArrayForRelation(QAccessibleInterface*
|
|||
pRetVal->parray = elements;
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::setAriaProperties(QAccessibleInterface *accessible, VARIANT *pRetVal)
|
||||
{
|
||||
Q_ASSERT(accessible);
|
||||
|
||||
QAccessibleAttributesInterface *attributesIface = accessible->attributesInterface();
|
||||
if (!attributesIface)
|
||||
return;
|
||||
|
||||
QString ariaString;
|
||||
const QList<QAccessible::Attribute> attrKeys = attributesIface->attributeKeys();
|
||||
for (qsizetype i = 0; i < attrKeys.size(); ++i) {
|
||||
if (i != 0)
|
||||
ariaString += QStringLiteral(";");
|
||||
const QAccessible::Attribute key = attrKeys.at(i);
|
||||
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 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()) {
|
||||
if (name != *attrMap.keyBegin())
|
||||
ariaString += QStringLiteral(";");
|
||||
ariaString += name + QStringLiteral("=") + val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QAccessible::Attribute::Level:
|
||||
Q_ASSERT(value.canConvert<int>());
|
||||
ariaString += QStringLiteral("level=") + QString::number(value.toInt());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setVariantString(ariaString, pRetVal);
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::setStyle(QAccessibleInterface *accessible, VARIANT *pRetVal)
|
||||
{
|
||||
Q_ASSERT(accessible);
|
||||
|
||||
QAccessibleAttributesInterface *attributesIface = accessible->attributesInterface();
|
||||
if (!attributesIface)
|
||||
return;
|
||||
|
||||
// currently, only heading styles are implemented here
|
||||
if (accessible->role() != QAccessible::Role::Heading)
|
||||
return;
|
||||
|
||||
const QVariant levelVariant = attributesIface->attributeValue(QAccessible::Attribute::Level);
|
||||
if (!levelVariant.isValid())
|
||||
return;
|
||||
|
||||
Q_ASSERT(levelVariant.canConvert<int>());
|
||||
// UIA only has styles for heading levels 1-9
|
||||
const int level = levelVariant.toInt();
|
||||
if (level < 1 || level > 9)
|
||||
return;
|
||||
|
||||
const int styleId = styleIdForHeadingLevel(level);
|
||||
setVariantI4(styleId, pRetVal);
|
||||
}
|
||||
|
||||
int QWindowsUiaMainProvider::styleIdForHeadingLevel(int headingLevel)
|
||||
{
|
||||
// only heading levels 1-9 have a corresponding UIA style ID
|
||||
Q_ASSERT(headingLevel > 0 && headingLevel <= 9);
|
||||
|
||||
static constexpr int styles[] = {
|
||||
StyleId_Heading1,
|
||||
StyleId_Heading2,
|
||||
StyleId_Heading3,
|
||||
StyleId_Heading4,
|
||||
StyleId_Heading5,
|
||||
StyleId_Heading6,
|
||||
StyleId_Heading7,
|
||||
StyleId_Heading8,
|
||||
StyleId_Heading9,
|
||||
};
|
||||
|
||||
return styles[headingLevel - 1];
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idProp;
|
||||
|
|
@ -411,6 +498,9 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
|
|||
// Accelerator key.
|
||||
setVariantString(accessible->text(QAccessible::Accelerator), pRetVal);
|
||||
break;
|
||||
case UIA_AriaPropertiesPropertyId:
|
||||
setAriaProperties(accessible, pRetVal);
|
||||
break;
|
||||
case UIA_AutomationIdPropertyId:
|
||||
// Automation ID, which can be used by tools to select a specific control in the UI.
|
||||
setVariantString(automationIdForAccessible(accessible), pRetVal);
|
||||
|
|
@ -511,6 +601,9 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
|
|||
setVariantString(name, pRetVal);
|
||||
break;
|
||||
}
|
||||
case UIA_StyleIdAttributeId:
|
||||
setStyle(accessible, pRetVal);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@ public:
|
|||
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);
|
||||
/** Returns the UIA style ID for a heading level from 1 to 9. */
|
||||
static int styleIdForHeadingLevel(int headingLevel);
|
||||
static QMutex m_mutex;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,19 @@
|
|||
#define UIA_SelectionPattern2Id 10034
|
||||
#define UIA_IsReadOnlyAttributeId 40015
|
||||
#define UIA_StrikethroughStyleAttributeId 40026
|
||||
#define UIA_StyleIdAttributeId 40034
|
||||
#define UIA_CaretPositionAttributeId 40038
|
||||
|
||||
#define StyleId_Heading1 70001
|
||||
#define StyleId_Heading2 70002
|
||||
#define StyleId_Heading3 70003
|
||||
#define StyleId_Heading4 70004
|
||||
#define StyleId_Heading5 70005
|
||||
#define StyleId_Heading6 70006
|
||||
#define StyleId_Heading7 70007
|
||||
#define StyleId_Heading8 70008
|
||||
#define StyleId_Heading9 70009
|
||||
|
||||
enum CaretPosition {
|
||||
CaretPosition_Unknown = 0,
|
||||
CaretPosition_EndOfLine = 1,
|
||||
|
|
|
|||
Loading…
Reference in New Issue