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;