From f2afdde302aeb895989a4fef58c6fa75fc93364f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Fri, 7 May 2021 10:07:50 +0200 Subject: [PATCH 2/2] Implement accessibility for QQuickWidget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The accessibility tree for the Qt Quick content should be rooted at the QQuickWidget, and not at the offscreen QQuickWindow. For this to be the case, several things must happen: - QQuickWindow must not report the child interfaces - QQuickWidget must report the child interfaces - The child interfaces must report the QQuickWidget as the parent Create accessibility interfaces for QQuickWidget and and QQuickWigetOffscreenWindow (which now gets a proper subclass), where the QQuickWidget interface reports the child interfaces and the QQuickWigetOffscreenWindow reports no children Change the code in QAccessibleQuickItem to use the true (visible) window, where needed. Fixes: QTBUG-67290 Change-Id: I387d0ef711138d248a8dd16eefc9839499b35eeb Reviewed-by: Jan Arve Sæther Reviewed-by: Volker Hilsheimer (cherry picked from commit 41926e08d73ea6c4bbfc87a1dd52d2cdbc435c27) --- src/quick/accessible/qaccessiblequickitem.cpp | 29 +++-- src/quick/accessible/qaccessiblequickview_p.h | 2 +- src/quickwidgets/qaccessiblequickwidget.cpp | 110 ++++++++++++++++++ src/quickwidgets/qaccessiblequickwidget.h | 84 +++++++++++++ .../qaccessiblequickwidgetfactory.cpp | 60 ++++++++++ .../qaccessiblequickwidgetfactory_p.h | 66 +++++++++++ src/quickwidgets/qquickwidget.cpp | 18 ++- src/quickwidgets/qquickwidget_p.h | 8 ++ src/quickwidgets/quickwidgets.pro | 8 +- 9 files changed, 368 insertions(+), 17 deletions(-) create mode 100644 src/quickwidgets/qaccessiblequickwidget.cpp create mode 100644 src/quickwidgets/qaccessiblequickwidget.h create mode 100644 src/quickwidgets/qaccessiblequickwidgetfactory.cpp create mode 100644 src/quickwidgets/qaccessiblequickwidgetfactory_p.h diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp index 85719fdc80..eb3df4d4cd 100644 --- a/src/quick/accessible/qaccessiblequickitem.cpp +++ b/src/quick/accessible/qaccessiblequickitem.cpp @@ -46,6 +46,7 @@ #include "QtQuick/private/qquicktextinput_p.h" #include "QtQuick/private/qquickaccessibleattached_p.h" #include "QtQuick/qquicktextdocument.h" +#include "QtQuick/qquickrendercontrol.h" QT_BEGIN_NAMESPACE #if QT_CONFIG(accessibility) @@ -57,7 +58,19 @@ QAccessibleQuickItem::QAccessibleQuickItem(QQuickItem *item) QWindow *QAccessibleQuickItem::window() const { - return item()->window(); + QQuickWindow *window = item()->window(); + + // For QQuickWidget the above window will be the offscreen QQuickWindow, + // which is not a part of the accessibility tree. Detect this case and + // return the window for the QQuickWidget instead. + if (window && !window->handle()) { + if (QQuickRenderControl *renderControl = QQuickWindowPrivate::get(window)->renderControl) { + if (QWindow *renderWindow = renderControl->renderWindow(nullptr)) + return renderWindow; + } + } + + return window; } int QAccessibleQuickItem::childCount() const @@ -113,19 +126,15 @@ QAccessibleInterface *QAccessibleQuickItem::childAt(int x, int y) const QAccessibleInterface *QAccessibleQuickItem::parent() const { QQuickItem *parent = item()->parentItem(); - QQuickWindow *window = item()->window(); - QQuickItem *ci = window ? window->contentItem() : nullptr; + QQuickWindow *itemWindow = item()->window(); + QQuickItem *ci = itemWindow ? itemWindow->contentItem() : nullptr; while (parent && !QQuickItemPrivate::get(parent)->isAccessible && parent != ci) parent = parent->parentItem(); if (parent) { if (parent == ci) { - // Jump out to the scene widget if the parent is the root item. - // There are two root items, QQuickWindow::rootItem and - // QQuickView::declarativeRoot. The former is the true root item, - // but is not a part of the accessibility tree. Check if we hit - // it here and return an interface for the scene instead. - return QAccessible::queryAccessibleInterface(window); + // Jump out to the window if the parent is the root item + return QAccessible::queryAccessibleInterface(window()); } else { while (parent && !parent->d_func()->isAccessible) parent = parent->parentItem(); @@ -188,7 +197,7 @@ QAccessible::State QAccessibleQuickItem::state() const QRect viewRect_ = viewRect(); QRect itemRect = rect(); - if (viewRect_.isNull() || itemRect.isNull() || !item()->window() || !item()->window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity())) + if (viewRect_.isNull() || itemRect.isNull() || !window() || !window()->isVisible() ||!item()->isVisible() || qFuzzyIsNull(item()->opacity())) state.invisible = true; if (!viewRect_.intersects(itemRect)) state.offscreen = true; diff --git a/src/quick/accessible/qaccessiblequickview_p.h b/src/quick/accessible/qaccessiblequickview_p.h index 39ffcaf39c..8baa01330c 100644 --- a/src/quick/accessible/qaccessiblequickview_p.h +++ b/src/quick/accessible/qaccessiblequickview_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE #if QT_CONFIG(accessibility) -class QAccessibleQuickWindow : public QAccessibleObject +class Q_QUICK_EXPORT QAccessibleQuickWindow : public QAccessibleObject { public: QAccessibleQuickWindow(QQuickWindow *object); diff --git a/src/quickwidgets/qaccessiblequickwidget.cpp b/src/quickwidgets/qaccessiblequickwidget.cpp new file mode 100644 index 0000000000..6f04d6693f --- /dev/null +++ b/src/quickwidgets/qaccessiblequickwidget.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblequickwidget.h" + +#include "qquickwidget_p.h" + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(accessibility) + +QAccessibleQuickWidget::QAccessibleQuickWidget(QQuickWidget* widget) +: QAccessibleWidget(widget) +, m_accessibleWindow(QQuickWidgetPrivate::get(widget)->offscreenWindow) +{ + // NOTE: m_accessibleWindow is a QAccessibleQuickWindow, and not a + // QAccessibleQuickWidgetOffscreenWindow (defined below). This means + // it will return the Quick item child interfaces, which is what's needed here + // (unlike QAccessibleQuickWidgetOffscreenWindow, which will report 0 children). +} + +QAccessibleInterface *QAccessibleQuickWidget::child(int index) const +{ + return m_accessibleWindow.child(index); +} + +int QAccessibleQuickWidget::childCount() const +{ + return m_accessibleWindow.childCount(); +} + +int QAccessibleQuickWidget::indexOfChild(const QAccessibleInterface *iface) const +{ + return m_accessibleWindow.indexOfChild(iface); +} + +QAccessibleInterface *QAccessibleQuickWidget::childAt(int x, int y) const +{ + return m_accessibleWindow.childAt(x, y); +} + +QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window) +:QAccessibleQuickWindow(window) +{ + +} + +QAccessibleInterface *QAccessibleQuickWidgetOffscreenWindow::child(int index) const +{ + Q_UNUSED(index); + return nullptr; +} + +int QAccessibleQuickWidgetOffscreenWindow::childCount() const +{ + return 0; +} + +int QAccessibleQuickWidgetOffscreenWindow::indexOfChild(const QAccessibleInterface *iface) const +{ + Q_UNUSED(iface); + return -1; +} + +QAccessibleInterface *QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow::childAt(int x, int y) const +{ + Q_UNUSED(x); + Q_UNUSED(y); + return nullptr; +} + +#endif // accessibility + +QT_END_NAMESPACE diff --git a/src/quickwidgets/qaccessiblequickwidget.h b/src/quickwidgets/qaccessiblequickwidget.h new file mode 100644 index 0000000000..1f52c78c46 --- /dev/null +++ b/src/quickwidgets/qaccessiblequickwidget.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACCESSIBLEQUICKWIDGET_H +#define QACCESSIBLEQUICKWIDGET_H + +#include "qquickwidget.h" +#include + +#include + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(accessibility) + +// These classes implement the QQuickWiget accessibility switcharoo, +// where the child items of the QQuickWidgetOffscreenWindow are reported +// as child accessible interfaces of the QAccessibleQuickWidget. +class QAccessibleQuickWidget: public QAccessibleWidget +{ +public: + QAccessibleQuickWidget(QQuickWidget* widget); + + QAccessibleInterface *child(int index) const override; + int childCount() const override; + int indexOfChild(const QAccessibleInterface *iface) const override; + QAccessibleInterface *childAt(int x, int y) const override; + +private: + QAccessibleQuickWindow m_accessibleWindow; + Q_DISABLE_COPY(QAccessibleQuickWidget) +}; + +class QAccessibleQuickWidgetOffscreenWindow: public QAccessibleQuickWindow +{ +public: + QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window); + QAccessibleInterface *child(int index) const override; + int childCount() const override; + int indexOfChild(const QAccessibleInterface *iface) const override; + QAccessibleInterface *childAt(int x, int y) const override; +}; + +#endif // accessibility + +QT_END_NAMESPACE + +#endif diff --git a/src/quickwidgets/qaccessiblequickwidgetfactory.cpp b/src/quickwidgets/qaccessiblequickwidgetfactory.cpp new file mode 100644 index 0000000000..3756d0c27c --- /dev/null +++ b/src/quickwidgets/qaccessiblequickwidgetfactory.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblequickwidgetfactory_p.h" +#include "qaccessiblequickwidget.h" + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(accessibility) + +QAccessibleInterface *qAccessibleQuickWidgetFactory(const QString &classname, QObject *object) +{ + if (classname == QLatin1String("QQuickWidget")) { + return new QAccessibleQuickWidget(qobject_cast(object)); + } else if (classname == QLatin1String("QQuickWidgetOffscreenWindow")) { + return new QAccessibleQuickWidgetOffscreenWindow(qobject_cast(object)); + } + return 0; +} + +#endif // accessibility + +QT_END_NAMESPACE + diff --git a/src/quickwidgets/qaccessiblequickwidgetfactory_p.h b/src/quickwidgets/qaccessiblequickwidgetfactory_p.h new file mode 100644 index 0000000000..8c63b09f81 --- /dev/null +++ b/src/quickwidgets/qaccessiblequickwidgetfactory_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifndef QACCESSIBLEQUICKWIDGETFACTORY_H +#define QACCESSIBLEQUICKWIDGETFACTORY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(accessibility) + +QAccessibleInterface *qAccessibleQuickWidgetFactory(const QString &classname, QObject *object); + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 223d91f579..9c97b43518 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -39,6 +39,7 @@ #include "qquickwidget.h" #include "qquickwidget_p.h" +#include "qaccessiblequickwidgetfactory_p.h" #include "private/qquickwindow_p.h" #include "private/qquickitem_p.h" @@ -75,9 +76,16 @@ QT_BEGIN_NAMESPACE +QQuickWidgetOffscreenWindow::QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control) +:QQuickWindow(dd, control) +{ + setTitle(QString::fromLatin1("Offscreen")); + setObjectName(QString::fromLatin1("QQuickOffScreenWindow")); +} + // override setVisble to prevent accidental offscreen window being created // by base class. -class QQuickOffcreenWindowPrivate: public QQuickWindowPrivate { +class QQuickWidgetOffscreenWindowPrivate: public QQuickWindowPrivate { public: void setVisible(bool visible) override { Q_Q(QWindow); @@ -105,10 +113,8 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) Q_Q(QQuickWidget); renderControl = new QQuickWidgetRenderControl(q); - offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(),renderControl); + offscreenWindow = new QQuickWidgetOffscreenWindow(*new QQuickWidgetOffscreenWindowPrivate(), renderControl); offscreenWindow->setScreen(q->screen()); - offscreenWindow->setTitle(QString::fromLatin1("Offscreen")); - offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow")); // Do not call create() on offscreenWindow. // Check if the Software Adaptation is being used @@ -139,6 +145,10 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) QWidget::connect(offscreenWindow, &QQuickWindow::focusObjectChanged, q, &QQuickWidget::propagateFocusObjectChanged); QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate())); QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate())); + +#if QT_CONFIG(accessibility) + QAccessible::installFactory(&qAccessibleQuickWidgetFactory); +#endif } void QQuickWidgetPrivate::ensureEngine() const diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index 881f7f9220..1a946bcc71 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.h @@ -148,6 +148,14 @@ public: bool forceFullUpdate; }; +class QQuickWidgetOffscreenWindow: public QQuickWindow +{ + Q_OBJECT + +public: + QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control); +}; + QT_END_NAMESPACE #endif // QQuickWidget_P_H diff --git a/src/quickwidgets/quickwidgets.pro b/src/quickwidgets/quickwidgets.pro index 2438e577ae..f46deb54ac 100644 --- a/src/quickwidgets/quickwidgets.pro +++ b/src/quickwidgets/quickwidgets.pro @@ -7,9 +7,13 @@ DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FO HEADERS += \ qquickwidget.h \ qquickwidget_p.h \ - qtquickwidgetsglobal.h + qtquickwidgetsglobal.h \ + qaccessiblequickwidget.h \ + qaccessiblequickwidgetfactory_p.h SOURCES += \ - qquickwidget.cpp + qquickwidget.cpp \ + qaccessiblequickwidget.cpp \ + qaccessiblequickwidgetfactory.cpp load(qt_module) -- 2.34.1