vcl/inc/qt5/QtAccessibleWidget.hxx | 12 +++++ vcl/qt5/QtAccessibleWidget.cxx | 77 +++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+)
New commits: commit 1b6963cda0a209548249dd43c5c1fa705ef32bcd Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Thu Feb 29 10:20:42 2024 +0100 Commit: Michael Weghorn <m.wegh...@posteo.de> CommitDate: Thu Feb 29 12:34:37 2024 +0100 tdf#158030 qt a11y: Implement new QAccessibleAttributesInterface Implement the new `QAccessibleAttributesInterface` just added upstream to Qt in qtbase commit [1] commit fb5ffe862688a87cfc136113e067bcba0c49a7ae Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Fri Nov 10 18:25:02 2023 +0100 Commit: Volker Hilsheimer <volker.hilshei...@qt.io> CommitDate: Thu Feb 29 04:44:22 2024 +0000 a11y: Add new QAccessibleAttributesInterface , see also QTBUG-119057. [2] This API is available with Qt >= 6.8. That interface makes it possible to report object attributes that are bridged to the platform a11y layers. Use it to bridge the attributes retrieved from the `XAccessibleExtendedAttributes` UNO interface. Together with the pending upstream qtbase change that implements the AT-SPI bridge for Linux [3], the "level" AT-SPI object attribute for headings in Writer is correctly reported to AT-SPI, making the Orca screen reader announce "Heading level N" as expected. For now, map not explicitly handled attributes as key-value pairs to Qt via the special `QAccessible::Attribute::Custom` attribute, which causes them to be mapped to AT-SPI unchanged, and can e.g. be used for testing the tdf#158030 scenario. For common attributes - like those specified in the Core Accessibility API Mappings specification [4] - suggesting to add new enum values to the `QAccessible::Attribute` enum to upstream Qt and using those instead should be considered for the future. Related commit for gtk4: commit 3aca2d9776a871f15009a1aa70628ba3a03ee147 Author: Michael Weghorn <m.wegh...@posteo.de> Date: Thu Nov 9 15:31:57 2023 +0100 gtk4 a11y: Handle the "level" object attribute [1] https://code.qt.io/cgit/qt/qtbase.git/commit/?id=fb5ffe862688a87cfc136113e067bcba0c49a7ae [2] https://bugreports.qt.io/browse/QTBUG-119057 [3] https://codereview.qt-project.org/c/qt/qtbase/+/517526 [4] https://www.w3.org/TR/core-aam-1.2/ Change-Id: Ibe357bfd72bb2dc6e44ad941e62737d5cac21e1c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159309 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.wegh...@posteo.de> diff --git a/vcl/inc/qt5/QtAccessibleWidget.hxx b/vcl/inc/qt5/QtAccessibleWidget.hxx index 8d71ecd0ea77..46d7be26582d 100644 --- a/vcl/inc/qt5/QtAccessibleWidget.hxx +++ b/vcl/inc/qt5/QtAccessibleWidget.hxx @@ -38,6 +38,9 @@ class QtWidget; class QtAccessibleWidget final : public QAccessibleInterface, public QAccessibleActionInterface, +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + public QAccessibleAttributesInterface, +#endif public QAccessibleTextInterface, public QAccessibleEditableTextInterface, #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) @@ -85,6 +88,15 @@ public: void doAction(const QString& actionName) override; QStringList keyBindingsForAction(const QString& actionName) const override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + // helper method for QAccessibleAttributesInterface + QHash<QAccessible::Attribute, QVariant> attributes() const; + + // QAccessibleAttributesInterface + QList<QAccessible::Attribute> attributeKeys() const override; + QVariant attributeValue(QAccessible::Attribute key) const override; +#endif + // QAccessibleTextInterface void addSelection(int startOffset, int endOffset) override; QString attributes(int offset, int* startOffset, int* endOffset) const override; diff --git a/vcl/qt5/QtAccessibleWidget.cxx b/vcl/qt5/QtAccessibleWidget.cxx index 7eadc3313834..790e200929b1 100644 --- a/vcl/qt5/QtAccessibleWidget.cxx +++ b/vcl/qt5/QtAccessibleWidget.cxx @@ -39,6 +39,7 @@ #include <com/sun/star/accessibility/XAccessibleEditableText.hpp> #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> #include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp> #include <com/sun/star/accessibility/XAccessibleKeyBinding.hpp> #include <com/sun/star/accessibility/XAccessibleRelationSet.hpp> #include <com/sun/star/accessibility/XAccessibleSelection.hpp> @@ -741,6 +742,10 @@ void* QtAccessibleWidget::interface_cast(QAccessible::InterfaceType t) #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) if (t == QAccessible::SelectionInterface && accessibleProvidesInterface<XAccessibleSelection>()) return static_cast<QAccessibleSelectionInterface*>(this); +#endif +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + if (t == QAccessible::AttributesInterface) + return static_cast<QAccessibleAttributesInterface*>(this); #endif return nullptr; } @@ -855,6 +860,78 @@ QStringList QtAccessibleWidget::keyBindingsForAction(const QString& actionName) return keyBindings; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + +// QAccessibleAttributesInterface helpers +namespace +{ +void lcl_insertAttribute(QHash<QAccessible::Attribute, QVariant>& rQtAttrs, const OUString& rName, + const OUString& rValue) +{ + if (rName == u"level"_ustr) + { + rQtAttrs.insert(QAccessible::Attribute::Level, + QVariant::fromValue(static_cast<int>(rValue.toInt32()))); + } + else + { + // for now, leave not explicitly handled attributes as they are and report + // via QAccessible::Attribute::Custom, but should consider suggesting to + // add more specific attributes on Qt side and use those instead + const QVariant aVariant = rQtAttrs.value(QAccessible::Attribute::Custom, + QVariant::fromValue(QHash<QString, QString>())); + assert((aVariant.canConvert<QHash<QString, QString>>())); + QHash<QString, QString> aAttrs = aVariant.value<QHash<QString, QString>>(); + aAttrs.insert(toQString(rName), toQString(rValue)); + rQtAttrs.insert(QAccessible::Attribute::Custom, QVariant::fromValue(aAttrs)); + } +} +} + +QHash<QAccessible::Attribute, QVariant> QtAccessibleWidget::attributes() const +{ + Reference<XAccessibleContext> xContext = getAccessibleContextImpl(); + if (!xContext.is()) + return {}; + + Reference<XAccessibleExtendedAttributes> xAttributes(xContext, UNO_QUERY); + if (!xAttributes.is()) + return {}; + + OUString sAttrs; + xAttributes->getExtendedAttributes() >>= sAttrs; + + QHash<QAccessible::Attribute, QVariant> aQtAttrs; + sal_Int32 nIndex = 0; + do + { + const OUString sAttribute = sAttrs.getToken(0, ';', nIndex); + sal_Int32 nColonPos = 0; + const OUString sName = sAttribute.getToken(0, ':', nColonPos); + const OUString sValue = sAttribute.getToken(0, ':', nColonPos); + assert(nColonPos == -1 + && "Too many colons in attribute that should have \"name:value\" syntax"); + if (!sName.isEmpty()) + lcl_insertAttribute(aQtAttrs, sName, sValue); + } while (nIndex >= 0); + + return aQtAttrs; +} + +// QAccessibleAttributesInterface +QList<QAccessible::Attribute> QtAccessibleWidget::attributeKeys() const +{ + const QHash<QAccessible::Attribute, QVariant> aAttributes = attributes(); + return aAttributes.keys(); +} + +QVariant QtAccessibleWidget::attributeValue(QAccessible::Attribute eAttribute) const +{ + const QHash<QAccessible::Attribute, QVariant> aAllAttributes = attributes(); + return aAllAttributes.value(eAttribute); +} +#endif + // QAccessibleTextInterface void QtAccessibleWidget::addSelection(int /* startOffset */, int /* endOffset */) {