vcl/inc/qt5/QtAccessibleWidget.hxx |    7 ++++-
 vcl/qt5/QtAccessibleWidget.cxx     |   46 ++++++++++++++++++++++++++++---------
 vcl/qt5/QtInstanceBuilder.cxx      |   10 ++++++--
 vcl/qt5/QtInstanceDrawingArea.cxx  |    4 +--
 4 files changed, 50 insertions(+), 17 deletions(-)

New commits:
commit 7782a0d87bfdfea8af1c24b90939b0e75edce641
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Thu May 1 23:45:34 2025 +0200
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Fri May 2 09:32:42 2025 +0200

    tdf#130857 qt weld a11y: Drop 2 DrawingArea a11y asserts
    
    No longer assert in QtInstanceDrawingArea::get_accessible_parent
    and QtInstanceDrawingArea::get_accessible_relation_set, but just
    let them return nullptr.
    
    With
    
        Change-Id: I4686f1d6c5862fea38af626facff785ed5e9f9dd
        Author: Michael Weghorn <m.wegh...@posteo.de>
        Date:   Thu May 1 23:21:55 2025 +0200
    
            tdf#130857 qt weld a11y: Support custom DrawingArea a11y
    
    in place, those now get called for the
    SAL_VCL_QT_USE_WELDED_WIDGETS=1 case in
    a WIP branch adding support for the spelling
    dialog when the Orca screen reader is running,
    because QtAccessibleWidget::parent and
    QtAccessibleWidget::relations call them.
    
    However, QtAccessibleWidget::parent already
    falls back to the native QWidget/QObject
    hierarchy to find the accessible parent, which
    works for this case, too. (Adjust the comment
    a bit to mention the new scenario of native weld::Widget
    instances as well.)
    
    Relations should generally also be handled directly
    by QtAccessibleWidget, not the underlying XAccessible,
    as they are set in the .ui file which is evaluated
    by QtBuilder. If something is still missing there,
    then the logic still needs to be refined elsewhere.
    Come back to this later as needed.
    
    gtk3's GtkInstanceDrawingArea implementations also
    don't return anything particular here, but in fact
    simply trigger an assert when called, mentioning
    that this shouldn't be reached for the implementation
    there as everything is handled natively on the ATK
    layer.
    
    Change-Id: I6c9fa487614b5cb984888842e6c137801ab97b8a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184880
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/vcl/qt5/QtAccessibleWidget.cxx b/vcl/qt5/QtAccessibleWidget.cxx
index c45e12decb7d..e1f6dcea5814 100644
--- a/vcl/qt5/QtAccessibleWidget.cxx
+++ b/vcl/qt5/QtAccessibleWidget.cxx
@@ -330,8 +330,8 @@ QAccessibleInterface* QtAccessibleWidget::parent() const
         return QAccessible::queryAccessibleInterface(
             QtAccessibleRegistry::getQObject(xAc->getAccessibleParent()));
 
-    // go via the QObject hierarchy; some a11y objects like the application
-    // (at the root of the a11y hierarchy) are handled solely by Qt and have
+    // go via the QObject hierarchy; a11y objects like the application object
+    // and native weld::Widgets are handled solely by Qt and have
     // no LO-internal a11y objects associated with them
     QObject* pObj = object();
     if (pObj && pObj->parent())
diff --git a/vcl/qt5/QtInstanceDrawingArea.cxx 
b/vcl/qt5/QtInstanceDrawingArea.cxx
index 86929c836d9a..35cc1b47d36f 100644
--- a/vcl/qt5/QtInstanceDrawingArea.cxx
+++ b/vcl/qt5/QtInstanceDrawingArea.cxx
@@ -71,13 +71,13 @@ OutputDevice& QtInstanceDrawingArea::get_ref_device() { 
return *m_xDevice; }
 
 a11yref QtInstanceDrawingArea::get_accessible_parent()
 {
-    assert(false && "Not implemented yet");
+    // parent is handled via native QWidget hierarchy
     return nullptr;
 }
 
 a11yrelationset QtInstanceDrawingArea::get_accessible_relation_set()
 {
-    assert(false && "Not implemented yet");
+    // relations are handled natively in Qt
     return nullptr;
 }
 
commit 78c628cd2fcfdcf8699167c136551db6183a1f97
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Thu May 1 23:21:55 2025 +0200
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Fri May 2 09:32:35 2025 +0200

    tdf#130857 qt weld a11y: Support custom DrawingArea a11y
    
    Add support for using a custom accessibility
    interface/implementation for weld::DrawingLayer
    using native Qt widgets with
    SAL_VCL_QT_USE_WELDED_WIDGETS=1:
    
    Handle the a11yref/uno::Reference<XAccessbible> param
    passed to QtInstanceBuilder::weld_drawing_area by
    implementing logic to set a custom QAccessibleInterface
    implementation for the native QLabel that is used
    in the QtInstanceDrawingArea:
    
    Delete the QtAccessibleInterface currently set for
    the label and create a QtAccessibleWidget that basically
    wraps the XAccessible and is already used for non-weld
    objects, too. QtAccessibleWidget::customFactory is already
    registered as an a11y factory for Qt, so extend it to
    evaluate a QObject property to transport the custom
    accessible interface implmentation into that method
    from QtAccessibleWidget::setCustomAccessible.
    
    This, together with some further changes in a WIP
    branch implementing support for the spelling dialog
    result in the "Not in dictionary" edit view becoming
    accessible, i.e. expose its paragraph children on
    the a11y layer, support the AT-SPI Text interface
    and emit caret-changed events when moving the cursor
    within the edit view.
    
    Change-Id: I4686f1d6c5862fea38af626facff785ed5e9f9dd
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184879
    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 0e8f37500c3e..2c53f02e9081 100644
--- a/vcl/inc/qt5/QtAccessibleWidget.hxx
+++ b/vcl/inc/qt5/QtAccessibleWidget.hxx
@@ -189,6 +189,9 @@ public:
 
     // Factory
     static QAccessibleInterface* customFactory(const QString& classname, 
QObject* object);
+    static void
+    setCustomAccessible(QObject& rObject,
+                        const 
css::uno::Reference<css::accessibility::XAccessible>& rxAccessible);
 
 private:
     css::uno::Reference<css::accessibility::XAccessible> m_xAccessible;
diff --git a/vcl/qt5/QtAccessibleWidget.cxx b/vcl/qt5/QtAccessibleWidget.cxx
index efd288f41185..c45e12decb7d 100644
--- a/vcl/qt5/QtAccessibleWidget.cxx
+++ b/vcl/qt5/QtAccessibleWidget.cxx
@@ -64,6 +64,10 @@ using namespace css;
 using namespace css::accessibility;
 using namespace css::uno;
 
+// property used to specify the QtAccessibleWidget* that should be used as the
+// custom accessible interface for an object
+const char* const PROPERTY_ACCESSIBLE = "accessible-interface";
+
 QtAccessibleWidget::QtAccessibleWidget(const Reference<XAccessible>& 
xAccessible, QObject& rObject)
     : m_xAccessible(xAccessible)
     , m_rObject(rObject)
@@ -804,6 +808,10 @@ QAccessibleInterface* 
QtAccessibleWidget::customFactory(const QString& classname
     if (!pObject)
         return nullptr;
 
+    const QVariant aAccVariant = pObject->property(PROPERTY_ACCESSIBLE);
+    if (aAccVariant.isValid() && aAccVariant.canConvert<QtAccessibleWidget*>())
+        return aAccVariant.value<QtAccessibleWidget*>();
+
     if (classname == QLatin1String("QtWidget") && pObject->isWidgetType())
     {
         QtWidget* pWidget = static_cast<QtWidget*>(pObject);
@@ -834,6 +842,22 @@ QAccessibleInterface* 
QtAccessibleWidget::customFactory(const QString& classname
     return nullptr;
 }
 
+void QtAccessibleWidget::setCustomAccessible(QObject& rObject,
+                                             const 
uno::Reference<XAccessible>& rxAccessible)
+{
+    assert(rxAccessible.is());
+
+    // unset/delete the current/default accessible of the object
+    QAccessibleInterface* pOldAccessible = 
QAccessible::queryAccessibleInterface(&rObject);
+    const QAccessible::Id nId = QAccessible::uniqueId(pOldAccessible);
+    QAccessible::deleteAccessibleInterface(nId);
+
+    // let QtAccessibleWidget::customFactory set the custom accessible
+    rObject.setProperty(PROPERTY_ACCESSIBLE,
+                        QVariant::fromValue(new 
QtAccessibleWidget(rxAccessible, rObject)));
+    QAccessible::queryAccessibleInterface(&rObject);
+}
+
 // QAccessibleActionInterface
 QStringList QtAccessibleWidget::actionNames() const
 {
diff --git a/vcl/qt5/QtInstanceBuilder.cxx b/vcl/qt5/QtInstanceBuilder.cxx
index 4d251115a6c3..a92182740ff0 100644
--- a/vcl/qt5/QtInstanceBuilder.cxx
+++ b/vcl/qt5/QtInstanceBuilder.cxx
@@ -11,6 +11,7 @@
 
 #include <unordered_set>
 
+#include <QtAccessibleWidget.hxx>
 #include <QtBuilder.hxx>
 #include <QtInstanceAssistant.hxx>
 #include <QtInstanceBox.hxx>
@@ -414,11 +415,16 @@ std::unique_ptr<weld::Expander> 
QtInstanceBuilder::weld_expander(const OUString&
     return std::make_unique<QtInstanceExpander>(pExpander);
 }
 
-std::unique_ptr<weld::DrawingArea>
-QtInstanceBuilder::weld_drawing_area(const OUString& rId, const a11yref&, 
FactoryFunction, void*)
+std::unique_ptr<weld::DrawingArea> QtInstanceBuilder::weld_drawing_area(const 
OUString& rId,
+                                                                        const 
a11yref& rA11yImpl,
+                                                                        
FactoryFunction, void*)
 {
     QLabel* pLabel = m_xBuilder->get<QLabel>(rId);
     assert(pLabel);
+
+    if (rA11yImpl.is())
+        QtAccessibleWidget::setCustomAccessible(*pLabel, rA11yImpl);
+
     return std::make_unique<QtInstanceDrawingArea>(pLabel);
 }
 
commit f0a60807d2cccd3e443635db91140fe326226495
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Thu May 1 22:45:49 2025 +0200
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Fri May 2 09:32:28 2025 +0200

    qt: Switch QtAccessibleWidget::m_pObject to reference
    
    ... and rename to `m_rObject`. This makes clear it's
    always non-null, and allows to drop a corresponding
    assert.
    
    Change-Id: Iae67e9371a1bce397e52758314d229498449a5c5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184878
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>
    Tested-by: Jenkins

diff --git a/vcl/inc/qt5/QtAccessibleWidget.hxx 
b/vcl/inc/qt5/QtAccessibleWidget.hxx
index bc1ab406fe70..0e8f37500c3e 100644
--- a/vcl/inc/qt5/QtAccessibleWidget.hxx
+++ b/vcl/inc/qt5/QtAccessibleWidget.hxx
@@ -59,7 +59,7 @@ class QtAccessibleWidget final : public QObject,
 
 public:
     QtAccessibleWidget(const 
css::uno::Reference<css::accessibility::XAccessible>& xAccessible,
-                       QObject* pObject);
+                       QObject& rObject);
 
     void invalidate();
 
@@ -203,7 +203,7 @@ private:
         return xInterface.is();
     }
 
-    QObject* m_pObject;
+    QObject& m_rObject;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/vcl/qt5/QtAccessibleWidget.cxx b/vcl/qt5/QtAccessibleWidget.cxx
index 1298c33068f0..efd288f41185 100644
--- a/vcl/qt5/QtAccessibleWidget.cxx
+++ b/vcl/qt5/QtAccessibleWidget.cxx
@@ -64,9 +64,9 @@ using namespace css;
 using namespace css::accessibility;
 using namespace css::uno;
 
-QtAccessibleWidget::QtAccessibleWidget(const Reference<XAccessible>& 
xAccessible, QObject* pObject)
+QtAccessibleWidget::QtAccessibleWidget(const Reference<XAccessible>& 
xAccessible, QObject& rObject)
     : m_xAccessible(xAccessible)
-    , m_pObject(pObject)
+    , m_rObject(rObject)
 {
     Reference<XAccessibleContext> xContext = 
xAccessible->getAccessibleContext();
     Reference<XAccessibleEventBroadcaster> xBroadcaster(xContext, UNO_QUERY);
@@ -129,11 +129,10 @@ QtAccessibleWidget::getAccessibleTableForParent() const
 
 QWindow* QtAccessibleWidget::window() const
 {
-    assert(m_pObject);
-    if (m_pObject->isWidgetType())
+    if (m_rObject.isWidgetType())
     {
-        QWidget* pWidget = static_cast<QWidget*>(m_pObject);
-        QWidget* pWindow = pWidget->window();
+        QWidget& rWidget = static_cast<QWidget&>(m_rObject);
+        QWidget* pWindow = rWidget.window();
         if (pWindow)
             return pWindow->windowHandle();
     }
@@ -782,7 +781,7 @@ bool QtAccessibleWidget::isValid() const
     return xAc.is();
 }
 
-QObject* QtAccessibleWidget::object() const { return m_pObject; }
+QObject* QtAccessibleWidget::object() const { return &m_rObject; }
 
 void QtAccessibleWidget::setText(QAccessible::Text /* t */, const QString& /* 
text */) {}
 
@@ -816,7 +815,7 @@ QAccessibleInterface* 
QtAccessibleWidget::customFactory(const QString& classname
             // insert into registry so the association between the XAccessible 
and the QtWidget
             // is remembered rather than creating a different QtXAccessible 
when a QObject is needed later
             QtAccessibleRegistry::insert(xAcc, pObject);
-            return new QtAccessibleWidget(xAcc, pObject);
+            return new QtAccessibleWidget(xAcc, *pObject);
         }
     }
     if (classname == QLatin1String("QtXAccessible"))
@@ -824,7 +823,8 @@ QAccessibleInterface* 
QtAccessibleWidget::customFactory(const QString& classname
         QtXAccessible* pXAccessible = static_cast<QtXAccessible*>(pObject);
         if (pXAccessible->m_xAccessible.is())
         {
-            QtAccessibleWidget* pRet = new 
QtAccessibleWidget(pXAccessible->m_xAccessible, pObject);
+            QtAccessibleWidget* pRet
+                = new QtAccessibleWidget(pXAccessible->m_xAccessible, 
*pObject);
             // clear the reference in the QtXAccessible, no longer needed now 
that the QtAccessibleWidget holds one
             pXAccessible->m_xAccessible.clear();
             return pRet;

Reply via email to