a11y: Add new BlockQuote role

Add a new QAccessible::BlockQuote role that can be used
to report quoted content to assistive technology
as such (e.g. by browsers or document editors like
LibreOffice).

This matches the role as specified in e.g. ARIA [1],
IAccessible2 [2] and AT-SPI [3].

Map the role to platform APIs as described in the
The W3C Core Accessibility mappings spec [4]:

* Linux/AT-SPI: ATSPI_ROLE_BLOCK_QUOTE
* Windows/UIA: UIA_GroupControlTypeId with a
  UIA_LocalizedControlTypePropertyId of
  "blockquote"
* macOS/NSAccessibility: NSAccessibilityGroupRole

In the QAccessible::Role enum, also add comments
for the other IAccessible2 roles that have been
added in the meantime, to match the existing approach.
(Note however that Qt itself doesn't even use
IAccessible2 for its Windows a11y bridge any more since
2017 commit 0cf6297c15be45d852be98c862bd0211e6de1aa2,
so it's a bit unclear to me whether there's really
value in keeping those IAccessible2 references there
in the long run.)

[1] https://w3c.github.io/aria/#blockquote
[2] 2b8c2c7941/api/AccessibleRole.idl (L318)
[3] https://docs.gtk.org/atspi2/enum.Role.html#block_quote
[4] https://www.w3.org/TR/core-aam-1.2/#role-map-blockquote

[ChangeLog][QtGui][QAccessible] Added new BlockQuote
role that can be used to report quoted content as
such to assistive technology.

Fixes: QTBUG-128870
Change-Id: I8707d9d77640b67416f726d266b4cbec5ac09422
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Michael Weghorn 2024-09-12 17:21:37 +02:00 committed by Volker Hilsheimer
parent 87c6d142d2
commit 0b5874bc96
6 changed files with 19 additions and 0 deletions

View File

@ -247,6 +247,8 @@ static RoleMapping map[] = {
//: Role of an accessible object //: Role of an accessible object
{ QAccessible::Notification, ATSPI_ROLE_NOTIFICATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "notification") }, { QAccessible::Notification, ATSPI_ROLE_NOTIFICATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "notification") },
//: Role of an accessible object //: Role of an accessible object
{ QAccessible::BlockQuote, ATSPI_ROLE_BLOCK_QUOTE, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "block quote") },
//: Role of an accessible object
{ QAccessible::UserRole, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "unknown") } { QAccessible::UserRole, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "unknown") }
}; };

View File

@ -278,6 +278,7 @@ Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\value Animation An object that displays an animation. \value Animation An object that displays an animation.
\value Application The application's main window. \value Application The application's main window.
\value Assistant An object that provides interactive help. \value Assistant An object that provides interactive help.
\value [since 6.9] BlockQuote A section of content that is quoted from another source.
\value Border An object that represents a border. \value Border An object that represents a border.
\value ButtonDropDown A button that drops down a list of items. \value ButtonDropDown A button that drops down a list of items.
\value ButtonDropGrid A button that drops down a grid. \value ButtonDropGrid A button that drops down a grid.

View File

@ -314,6 +314,14 @@ public:
// IA2_ROLE_TOGGLE_BUTTON = 0x42A, // IA2_ROLE_TOGGLE_BUTTON = 0x42A,
// IA2_ROLE_VIEW_PORT = 0x42B, // IA2_ROLE_VIEW_PORT = 0x42B,
ComplementaryContent = 0x42C, ComplementaryContent = 0x42C,
// IA2_ROLE_LANDMARK = 0x42D,
// IA2_ROLE_LEVEL_BAR = 0x42E,
// IA2_ROLE_CONTENT_DELETION = 0x42F,
// IA2_ROLE_CONTENT_INSERTION = 0x430,
BlockQuote = 0x431,
// IA2_ROLE_MARK = 0x432,
// IA2_ROLE_SUGGESTION = 0x433,
// IA2_ROLE_COMMENT = = 0x434,
UserRole = 0x0000ffff UserRole = 0x0000ffff
}; };

View File

@ -157,6 +157,7 @@ static void populateRoleMap()
roleMap[QAccessible::ComplementaryContent] = NSAccessibilityGroupRole; roleMap[QAccessible::ComplementaryContent] = NSAccessibilityGroupRole;
roleMap[QAccessible::Graphic] = NSAccessibilityImageRole; roleMap[QAccessible::Graphic] = NSAccessibilityImageRole;
roleMap[QAccessible::Tree] = NSAccessibilityOutlineRole; roleMap[QAccessible::Tree] = NSAccessibilityOutlineRole;
roleMap[QAccessible::BlockQuote] = NSAccessibilityGroupRole;
} }
/* /*

View File

@ -588,6 +588,12 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
case UIA_FullDescriptionPropertyId: case UIA_FullDescriptionPropertyId:
*pRetVal = QComVariant{ accessible->text(QAccessible::Description) }.release(); *pRetVal = QComVariant{ accessible->text(QAccessible::Description) }.release();
break; break;
case UIA_LocalizedControlTypePropertyId:
// see Core Accessibility API Mappings spec:
// https://www.w3.org/TR/core-aam-1.2/#role-map-blockquote
if (accessible->role() == QAccessible::BlockQuote)
*pRetVal = QComVariant{ tr("blockquote") }.release();
break;
case UIA_NamePropertyId: { case UIA_NamePropertyId: {
QString name = accessible->text(QAccessible::Name); QString name = accessible->text(QAccessible::Name);
if (name.isEmpty() && topLevelWindow) if (name.isEmpty() && topLevelWindow)

View File

@ -150,6 +150,7 @@ long roleToControlTypeId(QAccessible::Role role)
{QAccessible::Paragraph, UIA_TextControlTypeId}, {QAccessible::Paragraph, UIA_TextControlTypeId},
{QAccessible::WebDocument, UIA_DocumentControlTypeId}, {QAccessible::WebDocument, UIA_DocumentControlTypeId},
{QAccessible::Heading, UIA_TextControlTypeId}, {QAccessible::Heading, UIA_TextControlTypeId},
{QAccessible::BlockQuote, UIA_GroupControlTypeId},
}; };
return mapping.value(role, UIA_CustomControlTypeId); return mapping.value(role, UIA_CustomControlTypeId);