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 
*/)
 {

Reply via email to