cui/source/inc/chardlg.hxx | 2 cui/source/tabpages/chardlg.cxx | 52 +++++++++++------------- vcl/Library_vclplug_qt5.mk | 2 vcl/Library_vclplug_qt6.mk | 2 vcl/inc/qt5/QtAccessibleInterimChildWidget.hxx | 41 ++++++++++++++++++ vcl/inc/qt5/QtAccessibleInterimParentWidget.hxx | 43 +++++++++++++++++++ vcl/inc/qt5/QtAccessibleWidget.hxx | 26 ++++++------ vcl/inc/qt6/QtAccessibleInterimChildWidget.hxx | 12 +++++ vcl/inc/qt6/QtAccessibleInterimParentWidget.hxx | 12 +++++ vcl/qt5/QtAccessibleInterimChildWidget.cxx | 25 +++++++++++ vcl/qt5/QtAccessibleInterimParentWidget.cxx | 38 +++++++++++++++++ vcl/qt5/QtAccessibleWidget.cxx | 33 +++++++++++++++ vcl/qt5/QtInstance.cxx | 10 ++++ vcl/qt6/QtAccessibleInterimChildWidget.cxx | 12 +++++ vcl/qt6/QtAccessibleInterimParentWidget.cxx | 12 +++++ 15 files changed, 281 insertions(+), 41 deletions(-)
New commits: commit f175b90aeb5758916ed0e56a8d9d7fc0791d2794 Author: Michael Weghorn <[email protected]> AuthorDate: Wed Feb 4 15:30:04 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Wed Feb 4 21:45:28 2026 +0100 cui: Return ref in SvxCharNamePage::GetFontList The method always returns a non-null FontList*. Return by reference instead, and drop now no longer nededed assert/DBG_ASSERT. Change-Id: Id2aa688b2aaef59a5fdd1669bb205bb63e508775 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198693 Reviewed-by: Michael Weghorn <[email protected]> Tested-by: Jenkins diff --git a/cui/source/inc/chardlg.hxx b/cui/source/inc/chardlg.hxx index 3a357660bf93..e3f1240774f6 100644 --- a/cui/source/inc/chardlg.hxx +++ b/cui/source/inc/chardlg.hxx @@ -109,7 +109,7 @@ private: ScopedVclPtrInstance<VirtualDevice> m_xVDev; void Initialize(); - const FontList* GetFontList() const; + const FontList& GetFontList() const; void UpdatePreview_Impl(); void FillStyleBox_Impl(const weld::Widget& rBox); void FillSizeBox_Impl(const weld::Widget& rBox); diff --git a/cui/source/tabpages/chardlg.cxx b/cui/source/tabpages/chardlg.cxx index 5507e176436f..cf36f32c01b2 100644 --- a/cui/source/tabpages/chardlg.cxx +++ b/cui/source/tabpages/chardlg.cxx @@ -338,7 +338,7 @@ void SvxCharNamePage::Initialize() m_pImpl->m_aUpdateIdle.SetInvokeHandler( LINK( this, SvxCharNamePage, UpdateHdl_Impl ) ); } -const FontList* SvxCharNamePage::GetFontList() const +const FontList& SvxCharNamePage::GetFontList() const { if ( !m_pImpl->m_pFontList ) { @@ -358,7 +358,7 @@ const FontList* SvxCharNamePage::GetFontList() const } } - return m_pImpl->m_pFontList.get(); + return *m_pImpl->m_pFontList; } @@ -442,28 +442,28 @@ void SvxCharNamePage::UpdatePreview_Impl() SvxFont& rCJKFont = GetPreviewCJKFont(); SvxFont& rCTLFont = GetPreviewCTLFont(); // Font - const FontList* pFontList = GetFontList(); + const FontList& rFontList = GetFontList(); FontMetric aWestFontMetric = calcFontMetrics(rFont, this, m_xWestFontNameLB.get(), m_xWestFontStyleLB.get(), m_xWestFontSizeLB.get(), m_xWestFontLanguageLB.get(), - pFontList, GetWhich(SID_ATTR_CHAR_FONT), + &rFontList, GetWhich(SID_ATTR_CHAR_FONT), GetWhich(SID_ATTR_CHAR_FONTHEIGHT)); - m_xWestFontTypeFT->set_label(pFontList->GetFontMapText(aWestFontMetric)); + m_xWestFontTypeFT->set_label(rFontList.GetFontMapText(aWestFontMetric)); FontMetric aEastFontMetric = calcFontMetrics(rCJKFont, this, m_xEastFontNameLB.get(), m_xEastFontStyleLB.get(), m_xEastFontSizeLB.get(), m_xEastFontLanguageLB.get(), - pFontList, GetWhich(SID_ATTR_CHAR_CJK_FONT), + &rFontList, GetWhich(SID_ATTR_CHAR_CJK_FONT), GetWhich(SID_ATTR_CHAR_CJK_FONTHEIGHT)); - m_xEastFontTypeFT->set_label(pFontList->GetFontMapText(aEastFontMetric)); + m_xEastFontTypeFT->set_label(rFontList.GetFontMapText(aEastFontMetric)); FontMetric aCTLFontMetric = calcFontMetrics(rCTLFont, this, m_xCTLFontNameLB.get(), m_xCTLFontStyleLB.get(), m_xCTLFontSizeLB.get(), - m_xCTLFontLanguageLB.get(), pFontList, GetWhich(SID_ATTR_CHAR_CTL_FONT), + m_xCTLFontLanguageLB.get(), &rFontList, GetWhich(SID_ATTR_CHAR_CTL_FONT), GetWhich(SID_ATTR_CHAR_CTL_FONTHEIGHT)); - m_xCTLFontTypeFT->set_label(pFontList->GetFontMapText(aCTLFontMetric)); + m_xCTLFontTypeFT->set_label(rFontList.GetFontMapText(aCTLFontMetric)); m_aPreviewWin.Invalidate(); } @@ -499,8 +499,7 @@ void SvxCharNamePage::EnableFeatureButton(const weld::Widget& rNameBox) void SvxCharNamePage::FillStyleBox_Impl(const weld::Widget& rNameBox) { - const FontList* pFontList = GetFontList(); - assert(pFontList && "no fontlist"); + const FontList& rFontList = GetFontList(); FontStyleBox* pStyleBox = nullptr; OUString sFontName; @@ -526,7 +525,7 @@ void SvxCharNamePage::FillStyleBox_Impl(const weld::Widget& rNameBox) return; } - pStyleBox->Fill(sFontName, pFontList); + pStyleBox->Fill(sFontName, &rFontList); if ( !m_pImpl->m_bInSearchMode ) return; @@ -535,18 +534,17 @@ void SvxCharNamePage::FillStyleBox_Impl(const weld::Widget& rNameBox) // "not bold" and "not italic" OUString aEntry = m_pImpl->m_aNoStyleText; const char sS[] = "%1"; - aEntry = aEntry.replaceFirst( sS, pFontList->GetBoldStr() ); + aEntry = aEntry.replaceFirst(sS, rFontList.GetBoldStr()); m_pImpl->m_nExtraEntryPos = pStyleBox->get_count(); pStyleBox->append_text( aEntry ); aEntry = m_pImpl->m_aNoStyleText; - aEntry = aEntry.replaceFirst( sS, pFontList->GetItalicStr() ); + aEntry = aEntry.replaceFirst(sS, rFontList.GetItalicStr()); pStyleBox->append_text(aEntry); } void SvxCharNamePage::FillSizeBox_Impl(const weld::Widget& rNameBox) { - const FontList* pFontList = GetFontList(); - DBG_ASSERT( pFontList, "no fontlist" ); + const FontList& rFontList = GetFontList(); FontSizeBox* pSizeBox = nullptr; @@ -568,7 +566,7 @@ void SvxCharNamePage::FillSizeBox_Impl(const weld::Widget& rNameBox) return; } - pSizeBox->Fill( pFontList ); + pSizeBox->Fill(&rFontList); } namespace @@ -635,8 +633,8 @@ void SvxCharNamePage::Reset_Impl( const SfxItemSet& rSet, LanguageGroup eLangGrp break; } - const FontList* pFontList = GetFontList(); - FillFontNames(*pNameBox, *pFontList); + const FontList& rFontList = GetFontList(); + FillFontNames(*pNameBox, rFontList); const SvxFontItem* pFontItem = nullptr; SfxItemState eState = rSet.GetItemState( nWhich ); @@ -698,8 +696,8 @@ void SvxCharNamePage::Reset_Impl( const SfxItemSet& rSet, LanguageGroup eLangGrp // currently chosen font if ( bStyle && pFontItem ) { - FontMetric aFontMetric = pFontList->Get( pFontItem->GetFamilyName(), eWeight, eItalic ); - pStyleBox->set_active_text( pFontList->GetStyleName( aFontMetric ) ); + FontMetric aFontMetric = rFontList.Get(pFontItem->GetFamilyName(), eWeight, eItalic); + pStyleBox->set_active_text(rFontList.GetStyleName(aFontMetric)); } else if ( !m_pImpl->m_bInSearchMode || !bStyle ) { @@ -707,8 +705,8 @@ void SvxCharNamePage::Reset_Impl( const SfxItemSet& rSet, LanguageGroup eLangGrp } else if ( bStyle ) { - FontMetric aFontMetric = pFontList->Get( OUString(), eWeight, eItalic ); - pStyleBox->set_active_text( pFontList->GetStyleName( aFontMetric ) ); + FontMetric aFontMetric = rFontList.Get(OUString(), eWeight, eItalic); + pStyleBox->set_active_text(rFontList.GetStyleName(aFontMetric)); } if (!bStyleAvailable) { @@ -793,8 +791,8 @@ void SvxCharNamePage::Reset_Impl( const SfxItemSet& rSet, LanguageGroup eLangGrp break; } - OUString sMapText(pFontList->GetFontMapText( - pFontList->Get(pNameBox->get_active_text(), pStyleBox->get_active_text()))); + OUString sMapText(rFontList.GetFontMapText( + rFontList.Get(pNameBox->get_active_text(), pStyleBox->get_active_text()))); switch (eLangGrp) { @@ -865,12 +863,12 @@ bool SvxCharNamePage::FillItemSet_Impl( SfxItemSet& rSet, LanguageGroup eLangGrp bool bChanged = true; const OUString aFontName = pNameBox->get_active_text(); - const FontList* pFontList = GetFontList(); + const FontList& rFontList = GetFontList(); OUString aStyleBoxText = pStyleBox->get_active_text(); int nEntryPos = pStyleBox->find_text(aStyleBoxText); if (nEntryPos >= m_pImpl->m_nExtraEntryPos) aStyleBoxText.clear(); - FontMetric aInfo( pFontList->Get( aFontName, aStyleBoxText ) ); + FontMetric aInfo(rFontList.Get( aFontName, aStyleBoxText)); SvxFontItem aFontItem( aInfo.GetFamilyTypeMaybeAskConfig(), aInfo.GetFamilyName(), aInfo.GetStyleName(), aInfo.GetPitchMaybeAskConfig(), aInfo.GetCharSet(), nWhich ); pOld = GetOldItem( rSet, nSlot ); commit 848ea40b419a04bd50f2e79f5d5d7c1401ef7bb9 Author: Michael Weghorn <[email protected]> AuthorDate: Wed Feb 4 14:52:17 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Wed Feb 4 21:45:21 2026 +0100 tdf#130857 qt weld a11y: Include Qt widgets in a11y tree for interim case In case of only native Qt widgets or only vcl widgets being used for a window/dialog, the corresponding QWidget or vcl::Window a11y logic ensures that all child widgets are included in the a11y tree as needed. However, in the case where native Qt widgets are used inside of/as children of vcl widgets, i.e. the interim builder case, this is not the case. Neither the vcl::Window that hosts the QWidget tree reports the QWidget's a11y implementation as its child, nor does the QWidget's a11y implementation report the vcl::Window's a11y implementation as its parent, unless corresponding logic is implemented. This commit implements the corresponding logic for this to work as expected, by adding 2 new a11y implementations used for the Qt-based VCL plugins: * QtAccessibleInterimParentWidget is the a11y implementation used for the vcl widget that hosts the QWidget subtree and takes care of reporting the topmost QWidget as its (only) child. * QtAccessibleInterimChildWidget is the a11y implementation for the topmost Qt widget hosted inside the vcl widget and takes care of reporting the corresponding parent. Both of these are created in QtAccessibleWidget::customFactory. QtInstance::CreateInterimBuilder sets a property on the topmost Qt widget so that the a11y parent can be retrieved inside QtAccessibleWidget::customFactory. Sample scenario to test with the dialog newly supported since commit 4acb564b7683e9394926b3c509c904bd21a80b8d Author: Michael Weghorn <[email protected]> Date: Tue Feb 3 15:56:17 2026 +0100 tdf#130857 qt weld: Support Form Navigator : * start LO writer with the qt6 VCL plugin and SAL_VCL_QT_USE_WELDED_WIDGETS=1 * "Form" -> "Form Navigator..." * start Accerciser and verify that all widgets (including the tree view) are contained in the dialog's a11y tree * For all objects, ensure that the parent/child relationship is correct/consistent, by selecting the object in Accerciser's treeview and using Accerciser's IPython console: In [46]: acc.parent.get_child_at_index(acc.get_index_in_parent()) == acc Out[46]: True Change-Id: I76e6b6c85c1604bd2b16d8f94e1921889b8da91a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198688 Tested-by: Jenkins Reviewed-by: Michael Weghorn <[email protected]> diff --git a/vcl/Library_vclplug_qt5.mk b/vcl/Library_vclplug_qt5.mk index dec8ed5afadd..f8b173eb80e6 100644 --- a/vcl/Library_vclplug_qt5.mk +++ b/vcl/Library_vclplug_qt5.mk @@ -78,6 +78,8 @@ endif $(eval $(call gb_Library_add_exception_objects,vclplug_qt5,\ vcl/qt5/QtAccessibleEventListener \ + vcl/qt5/QtAccessibleInterimChildWidget \ + vcl/qt5/QtAccessibleInterimParentWidget \ vcl/qt5/QtAccessibleRegistry \ vcl/qt5/QtAccessibleWidget \ vcl/qt5/QtBitmap \ diff --git a/vcl/Library_vclplug_qt6.mk b/vcl/Library_vclplug_qt6.mk index 7bb5302f876e..52afca3ddb07 100644 --- a/vcl/Library_vclplug_qt6.mk +++ b/vcl/Library_vclplug_qt6.mk @@ -77,6 +77,8 @@ endif $(eval $(call gb_Library_add_exception_objects,vclplug_qt6,\ vcl/qt6/QtAccessibleEventListener \ + vcl/qt6/QtAccessibleInterimChildWidget \ + vcl/qt6/QtAccessibleInterimParentWidget \ vcl/qt6/QtAccessibleRegistry \ vcl/qt6/QtAccessibleWidget \ vcl/qt6/QtBitmap \ diff --git a/vcl/inc/qt5/QtAccessibleInterimChildWidget.hxx b/vcl/inc/qt5/QtAccessibleInterimChildWidget.hxx new file mode 100644 index 000000000000..9d6c19e50d03 --- /dev/null +++ b/vcl/inc/qt5/QtAccessibleInterimChildWidget.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <QtWidgets/QAccessibleWidget> + +/** + * Accessibility implementation for Qt widget that is the (single) direct + * accessible child of a VCL widget when a native Qt widget (sub)tree is used + * inside vcl widgets. + * + * This is used to ensure that the vcl widget is reported as the Qt widget's + * accessible parent. + * + * The counterpart to ensure that the Qt widget is reported as the vcl widget's + * accessible child is QtAccessibleInterimParentWidget. + * + * See also QtInstance::CreateInterimBuilder. + */ +class QtAccessibleInterimChildWidget : public QAccessibleWidget +{ + QObject* const m_pParent; + +public: + static constexpr const char* PROPERTY_INTERIM_PARENT = "interim-parent"; + + QtAccessibleInterimChildWidget(QWidget* w, QObject* pParent, + QAccessible::Role eRole = QAccessible::Client, + const QString& rName = QString()); + + virtual QAccessibleInterface* parent() const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/inc/qt5/QtAccessibleInterimParentWidget.hxx b/vcl/inc/qt5/QtAccessibleInterimParentWidget.hxx new file mode 100644 index 000000000000..2d187ef3ee87 --- /dev/null +++ b/vcl/inc/qt5/QtAccessibleInterimParentWidget.hxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "QtAccessibleWidget.hxx" + +#include <QtWidgets/QWidget> + +/** + * Accessibility implementation for the vcl widget that is the (accessible) + * parent of a Qt widget when a native Qt widget (sub)tree is used + * inside vcl widgets. + * + * This is used to ensure that the Qt widget is reported as the vcl widget's + * accessible child. + * + * The counterpart to ensure that the vcl widget is reported as the Qt widget's + * accessible parent is QtAccessibleInterimChildWidget. + * + * See also QtInstance::CreateInterimBuilder. + */ +class QtAccessibleInterimParentWidget : public QtAccessibleWidget +{ + QWidget* m_pNativeChild = nullptr; + +public: + QtAccessibleInterimParentWidget( + const css::uno::Reference<css::accessibility::XAccessible>& xAccessible, QObject& rObject, + QWidget* pNativeChild); + + int childCount() const override; + QAccessibleInterface* child(int index) const override; + int indexOfChild(const QAccessibleInterface* pChild) const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/inc/qt5/QtAccessibleWidget.hxx b/vcl/inc/qt5/QtAccessibleWidget.hxx index f8b0f10769e2..6abc9fd0c3fa 100644 --- a/vcl/inc/qt5/QtAccessibleWidget.hxx +++ b/vcl/inc/qt5/QtAccessibleWidget.hxx @@ -40,24 +40,24 @@ class XAccessibleTable; class QtFrame; class QtWidget; -class QtAccessibleWidget final : public QObject, - public QAccessibleInterface, - public QAccessibleActionInterface, +class QtAccessibleWidget : public QObject, + public QAccessibleInterface, + public QAccessibleActionInterface, #ifndef Q_MOC_RUN #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) - public QAccessibleAttributesInterface, + public QAccessibleAttributesInterface, #endif #endif - public QAccessibleTextInterface, - public QAccessibleEditableTextInterface, + public QAccessibleTextInterface, + public QAccessibleEditableTextInterface, #ifndef Q_MOC_RUN #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) - public QAccessibleSelectionInterface, + public QAccessibleSelectionInterface, #endif #endif - public QAccessibleTableCellInterface, - public QAccessibleTableInterface, - public QAccessibleValueInterface + public QAccessibleTableCellInterface, + public QAccessibleTableInterface, + public QAccessibleValueInterface { Q_OBJECT diff --git a/vcl/inc/qt6/QtAccessibleInterimChildWidget.hxx b/vcl/inc/qt6/QtAccessibleInterimChildWidget.hxx new file mode 100644 index 000000000000..7be6573672b8 --- /dev/null +++ b/vcl/inc/qt6/QtAccessibleInterimChildWidget.hxx @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "../qt5/QtAccessibleInterimChildWidget.hxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/inc/qt6/QtAccessibleInterimParentWidget.hxx b/vcl/inc/qt6/QtAccessibleInterimParentWidget.hxx new file mode 100644 index 000000000000..56ed1abd39e7 --- /dev/null +++ b/vcl/inc/qt6/QtAccessibleInterimParentWidget.hxx @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "../qt5/QtAccessibleInterimParentWidget.hxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/qt5/QtAccessibleInterimChildWidget.cxx b/vcl/qt5/QtAccessibleInterimChildWidget.cxx new file mode 100644 index 000000000000..323b785c25fc --- /dev/null +++ b/vcl/qt5/QtAccessibleInterimChildWidget.cxx @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <QtAccessibleInterimChildWidget.hxx> + +QtAccessibleInterimChildWidget::QtAccessibleInterimChildWidget(QWidget* w, QObject* pParent, + QAccessible::Role eRole, + const QString& rName) + : QAccessibleWidget(w, eRole, rName) + , m_pParent(pParent) +{ +} + +QAccessibleInterface* QtAccessibleInterimChildWidget::parent() const +{ + return QAccessible::queryAccessibleInterface(m_pParent); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/qt5/QtAccessibleInterimParentWidget.cxx b/vcl/qt5/QtAccessibleInterimParentWidget.cxx new file mode 100644 index 000000000000..46f225e60841 --- /dev/null +++ b/vcl/qt5/QtAccessibleInterimParentWidget.cxx @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <QtAccessibleInterimParentWidget.hxx> + +QtAccessibleInterimParentWidget::QtAccessibleInterimParentWidget( + const css::uno::Reference<css::accessibility::XAccessible>& xAccessible, QObject& rObject, + QWidget* pNativeChild) + : QtAccessibleWidget(xAccessible, rObject) + , m_pNativeChild(pNativeChild) +{ +} + +int QtAccessibleInterimParentWidget::childCount() const { return 1; } + +QAccessibleInterface* QtAccessibleInterimParentWidget::child(int index) const +{ + if (index == 0) + return QAccessible::queryAccessibleInterface(m_pNativeChild); + + return nullptr; +} + +int QtAccessibleInterimParentWidget::indexOfChild(const QAccessibleInterface* pChild) const +{ + if (pChild && pChild->object() == m_pNativeChild) + return 0; + + return -1; +} + +/* 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 41f14ab35ad5..ee74404091df 100644 --- a/vcl/qt5/QtAccessibleWidget.cxx +++ b/vcl/qt5/QtAccessibleWidget.cxx @@ -23,6 +23,8 @@ #include <QtGui/QAccessibleInterface> #include <QtAccessibleEventListener.hxx> +#include <QtAccessibleInterimChildWidget.hxx> +#include <QtAccessibleInterimParentWidget.hxx> #include <QtAccessibleRegistry.hxx> #include <QtFrame.hxx> #include <QtTools.hxx> @@ -56,7 +58,9 @@ #include <comphelper/AccessibleImplementationHelper.hxx> #include <sal/log.hxx> #include <vcl/accessibility/AccessibleTextAttributeHelper.hxx> +#include <vcl/accessibility/vclxaccessiblecomponent.hxx> #include <vcl/qt/QtUtils.hxx> +#include <vcl/syschild.hxx> using namespace css; using namespace css::accessibility; @@ -806,6 +810,16 @@ QAccessibleInterface* QtAccessibleWidget::customFactory(const QString& classname if (!pObject) return nullptr; + QVariant aInterimParentProp + = pObject->property(QtAccessibleInterimChildWidget::PROPERTY_INTERIM_PARENT); + if (!aInterimParentProp.isNull()) + { + assert(aInterimParentProp.canConvert<QObject*>()); + QObject* pParent = aInterimParentProp.value<QObject*>(); + assert(pObject->isWidgetType()); + return new QtAccessibleInterimChildWidget(static_cast<QWidget*>(pObject), pParent); + } + const QVariant aAccVariant = pObject->property(PROPERTY_ACCESSIBLE); if (aAccVariant.isValid() && aAccVariant.canConvert<QtAccessibleWidget*>()) { @@ -833,6 +847,25 @@ QAccessibleInterface* QtAccessibleWidget::customFactory(const QString& classname QtXAccessible* pXAccessible = static_cast<QtXAccessible*>(pObject); if (pXAccessible->m_xAccessible.is()) { + if (VCLXAccessibleComponent* pVCLAccComponent + = dynamic_cast<VCLXAccessibleComponent*>(pXAccessible->m_xAccessible.get())) + { + VclPtr<vcl::Window> pWindow = pVCLAccComponent->GetWindow(); + if (pWindow && pWindow->GetType() == WindowType::SYSTEMCHILDWINDOW) + { + // when native Qt widgets are used inside vcl widgets, make sure those + // are included in the a11y tree, see also QtInstance::CreateInterimBuilder + const SystemEnvData* pEnvData + = static_cast<SystemChildWindow*>(pWindow.get())->GetSystemData(); + if (QWidget* pNativeChild + = pEnvData ? static_cast<QWidget*>(pEnvData->pWidget) : nullptr) + { + return new QtAccessibleInterimParentWidget(pXAccessible->m_xAccessible, + *pObject, pNativeChild); + } + } + } + QtAccessibleWidget* pRet = new QtAccessibleWidget(pXAccessible->m_xAccessible, *pObject); // clear the reference in the QtXAccessible, no longer needed now that the QtAccessibleWidget holds one diff --git a/vcl/qt5/QtInstance.cxx b/vcl/qt5/QtInstance.cxx index 37786c5d7e49..0077c3acaba6 100644 --- a/vcl/qt5/QtInstance.cxx +++ b/vcl/qt5/QtInstance.cxx @@ -22,6 +22,8 @@ #include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <QtAccessibleRegistry.hxx> +#include <QtAccessibleInterimChildWidget.hxx> #include <QtBitmap.hxx> #include <QtClipboard.hxx> #include <QtData.hxx> @@ -950,6 +952,14 @@ std::unique_ptr<weld::Builder> QtInstance::CreateInterimBuilder(vcl::Window* pPa assert(pEnvData); QWidget* pWidget = static_cast<QWidget*>(pEnvData->pWidget); + + // set property to identify native Qt widget's a11y parent in QtAccessibleWidget::customFactory + rtl::Reference<comphelper::OAccessible> pParentAccessible = pEmbedWindow->GetAccessible(); + QObject* pParentObject = QtAccessibleRegistry::getQObject(pParentAccessible); + assert(pParentObject); + pWidget->setProperty(QtAccessibleInterimChildWidget::PROPERTY_INTERIM_PARENT, + QVariant::fromValue(pParentObject)); + pWidget->show(); return std::make_unique<QtInstanceBuilder>(pWidget, rUIRoot, rUIFile); diff --git a/vcl/qt6/QtAccessibleInterimChildWidget.cxx b/vcl/qt6/QtAccessibleInterimChildWidget.cxx new file mode 100644 index 000000000000..640b90278996 --- /dev/null +++ b/vcl/qt6/QtAccessibleInterimChildWidget.cxx @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "../qt5/QtAccessibleInterimChildWidget.cxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/qt6/QtAccessibleInterimParentWidget.cxx b/vcl/qt6/QtAccessibleInterimParentWidget.cxx new file mode 100644 index 000000000000..8ed4c4e7ec4f --- /dev/null +++ b/vcl/qt6/QtAccessibleInterimParentWidget.cxx @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "../qt5/QtAccessibleInterimParentWidget.cxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ commit 2da4cc3f1a62abc0c34437dc10bb4f3a97c62075 Author: Michael Weghorn <[email protected]> AuthorDate: Wed Feb 4 11:46:28 2026 +0100 Commit: Michael Weghorn <[email protected]> CommitDate: Wed Feb 4 21:45:13 2026 +0100 qt a11y: Move QtAccessibleWidget member vars to one place In order to improve readability, don't spread them across multiple places in the class declaration. Change-Id: I12be2d5d7d4a6cacf332be5eae7913e2e0ba6c7c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198667 Reviewed-by: Michael Weghorn <[email protected]> Tested-by: Jenkins diff --git a/vcl/inc/qt5/QtAccessibleWidget.hxx b/vcl/inc/qt5/QtAccessibleWidget.hxx index ce29c8af052e..f8b0f10769e2 100644 --- a/vcl/inc/qt5/QtAccessibleWidget.hxx +++ b/vcl/inc/qt5/QtAccessibleWidget.hxx @@ -61,6 +61,9 @@ class QtAccessibleWidget final : public QObject, { Q_OBJECT + css::uno::Reference<css::accessibility::XAccessible> m_xAccessible; + QObject& m_rObject; + public: QtAccessibleWidget(const css::uno::Reference<css::accessibility::XAccessible>& xAccessible, QObject& rObject); @@ -197,7 +200,6 @@ public: const rtl::Reference<comphelper::OAccessible>& rAccessible); private: - css::uno::Reference<css::accessibility::XAccessible> m_xAccessible; css::uno::Reference<css::accessibility::XAccessibleContext> getAccessibleContextImpl() const; css::uno::Reference<css::accessibility::XAccessibleTable> getAccessibleTableForParent() const; @@ -208,8 +210,6 @@ private: css::uno::Reference<Interface> xInterface(xContext, css::uno::UNO_QUERY); return xInterface.is(); } - - QObject& m_rObject; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
