diff options
author | Jan Arve Sæther <jan-arve.saether@qt.io> | 2021-01-14 18:46:17 +0100 |
---|---|---|
committer | Jan Arve Sæther <jan-arve.saether@qt.io> | 2021-02-02 09:45:11 +0100 |
commit | 180dbd050173d20c99f730eeacf2b0376fcb895e (patch) | |
tree | 30829dd47722419ec96bce940d5da382252349c8 | |
parent | 950f8bff7cbbdbd472234fd32ef659c9d0e8ba7c (diff) |
a11y: Fix ordering on header, content item and footer in Page
Because of the previous behavior, the footer could be read aloud by
the screen reader before the content item. And even worse, the footer
could be read aloud even before the header in some cases. This made
it hard for visually impaired people to use the application.
The Page type was used by the Dialog type, so it also affected that.
Fixes: QTBUG-75042
Change-Id: Ic3e8ec3f7dcf18af9262b1d35c986835c8da6900
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit 4b1acb290dc8869d0d2d1250dc1ed415d6b6e202)
-rw-r--r-- | src/imports/templates/qtquicktemplates2plugin.cpp | 2 | ||||
-rw-r--r-- | src/quicktemplates2/accessible/accessible.pri | 5 | ||||
-rw-r--r-- | src/quicktemplates2/accessible/qaccessiblequickpage.cpp | 81 | ||||
-rw-r--r-- | src/quicktemplates2/accessible/qaccessiblequickpage_p.h | 70 | ||||
-rw-r--r-- | src/quicktemplates2/qtquicktemplates2global.cpp | 63 | ||||
-rw-r--r-- | src/quicktemplates2/qtquicktemplates2global_p.h | 2 | ||||
-rw-r--r-- | src/quicktemplates2/quicktemplates2.pro | 4 | ||||
-rw-r--r-- | tests/auto/accessibility/accessibility.pro | 4 | ||||
-rw-r--r-- | tests/auto/accessibility/data/ordering/page.qml | 23 | ||||
-rw-r--r-- | tests/auto/accessibility/tst_accessibility.cpp | 30 |
10 files changed, 283 insertions, 1 deletions
diff --git a/src/imports/templates/qtquicktemplates2plugin.cpp b/src/imports/templates/qtquicktemplates2plugin.cpp index 961f2b7b..4bcb9bd4 100644 --- a/src/imports/templates/qtquicktemplates2plugin.cpp +++ b/src/imports/templates/qtquicktemplates2plugin.cpp @@ -142,6 +142,8 @@ private: QtQuickTemplates2Plugin::QtQuickTemplates2Plugin(QObject *parent) : QQmlExtensionPlugin(parent), registered(false) { + volatile auto initialization = &QQuickTemplates_initializeModule; + Q_UNUSED(initialization) #if QT_CONFIG(shortcut) originalContextMatcher = qt_quick_shortcut_context_matcher(); qt_quick_set_shortcut_context_matcher(QQuickShortcutContext::matcher); diff --git a/src/quicktemplates2/accessible/accessible.pri b/src/quicktemplates2/accessible/accessible.pri new file mode 100644 index 00000000..93660b9f --- /dev/null +++ b/src/quicktemplates2/accessible/accessible.pri @@ -0,0 +1,5 @@ +HEADERS += \ + $$PWD/qaccessiblequickpage_p.h + +SOURCES += \ + $$PWD/qaccessiblequickpage.cpp diff --git a/src/quicktemplates2/accessible/qaccessiblequickpage.cpp b/src/quicktemplates2/accessible/qaccessiblequickpage.cpp new file mode 100644 index 00000000..90ac49f9 --- /dev/null +++ b/src/quicktemplates2/accessible/qaccessiblequickpage.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblequickpage_p.h" +#include "qquickpage_p.h" + +QT_BEGIN_NAMESPACE + +QAccessibleQuickPage::QAccessibleQuickPage(QQuickPage *page) + : QAccessibleQuickItem(page) +{ +} + +QAccessibleInterface *QAccessibleQuickPage::child(int index) const +{ + const QList<QQuickItem*> kids = orderedChildItems(); + if (QQuickItem *item = kids.value(index)) + return QAccessible::queryAccessibleInterface(item); + return nullptr; +} + +int QAccessibleQuickPage::indexOfChild(const QAccessibleInterface *iface) const +{ + const QList<QQuickItem*> kids = orderedChildItems(); + return (int)kids.indexOf(static_cast<QQuickItem*>(iface->object())); +} + +QList<QQuickItem *> QAccessibleQuickPage::orderedChildItems() const +{ + // Just ensures that the header is first, and footer is last. Other existing order is kept. + const QQuickPage *p = page(); + QList<QQuickItem*> kids = childItems(); + const qsizetype hidx = kids.indexOf(p->header()); + if (hidx != -1) + kids.move(hidx, 0); + const qsizetype fidx = kids.indexOf(p->footer()); + if (fidx != -1) + kids.move(fidx, kids.count() - 1); + return kids; +} + +QQuickPage *QAccessibleQuickPage::page() const +{ + return static_cast<QQuickPage*>(object()); +} + +QT_END_NAMESPACE + diff --git a/src/quicktemplates2/accessible/qaccessiblequickpage_p.h b/src/quicktemplates2/accessible/qaccessiblequickpage_p.h new file mode 100644 index 00000000..9b208c14 --- /dev/null +++ b/src/quicktemplates2/accessible/qaccessiblequickpage_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACCESSIBLEQUICKPAGE_H +#define QACCESSIBLEQUICKPAGE_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. +// + +#include <QtQuick/private/qaccessiblequickitem_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickPage; + +class QAccessibleQuickPage : public QAccessibleQuickItem +{ +public: + QAccessibleQuickPage(QQuickPage *page); + QAccessibleInterface *child(int index) const override; + int indexOfChild(const QAccessibleInterface *iface) const override; +private: + QQuickPage *page() const; + QList<QQuickItem *> orderedChildItems() const; +}; + +QT_END_NAMESPACE + +#endif // QACCESSIBLEQUICKPAGE_H diff --git a/src/quicktemplates2/qtquicktemplates2global.cpp b/src/quicktemplates2/qtquicktemplates2global.cpp new file mode 100644 index 00000000..9b79d60c --- /dev/null +++ b/src/quicktemplates2/qtquicktemplates2global.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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 "qquickpage_p.h" +#include "accessible/qaccessiblequickpage_p.h" + +QT_BEGIN_NAMESPACE + +#if QT_CONFIG(accessibility) +QAccessibleInterface *qQuickAccessibleFactory(const QString &classname, QObject *object) +{ + if (classname == u"QQuickPage") { + return new QAccessibleQuickPage(qobject_cast<QQuickPage *>(object)); + } + return nullptr; +} +#endif + +void QQuickTemplates_initializeModule() +{ +#if QT_CONFIG(accessibility) + QAccessible::installFactory(&qQuickAccessibleFactory); +#endif +} + +Q_CONSTRUCTOR_FUNCTION(QQuickTemplates_initializeModule) + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qtquicktemplates2global_p.h b/src/quicktemplates2/qtquicktemplates2global_p.h index cec648d3..4bf6488a 100644 --- a/src/quicktemplates2/qtquicktemplates2global_p.h +++ b/src/quicktemplates2/qtquicktemplates2global_p.h @@ -64,6 +64,8 @@ QT_BEGIN_NAMESPACE # define Q_QUICKTEMPLATES2_PRIVATE_EXPORT #endif +Q_QUICKTEMPLATES2_PRIVATE_EXPORT void QQuickTemplates_initializeModule(); + QT_END_NAMESPACE #endif // QTQUICKTEMPLATES2GLOBAL_P_H diff --git a/src/quicktemplates2/quicktemplates2.pro b/src/quicktemplates2/quicktemplates2.pro index 8ed0151a..5138b610 100644 --- a/src/quicktemplates2/quicktemplates2.pro +++ b/src/quicktemplates2/quicktemplates2.pro @@ -10,5 +10,9 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII HEADERS += \ $$PWD/qtquicktemplates2global_p.h +SOURCES += \ + $$PWD/qtquicktemplates2global.cpp + include(quicktemplates2.pri) +include(accessible/accessible.pri) load(qt_module) diff --git a/tests/auto/accessibility/accessibility.pro b/tests/auto/accessibility/accessibility.pro index d8d5bb95..4cc101fb 100644 --- a/tests/auto/accessibility/accessibility.pro +++ b/tests/auto/accessibility/accessibility.pro @@ -12,5 +12,7 @@ include (../shared/util.pri) TESTDATA = data/* OTHER_FILES += \ - data/*.qml + data/defaults\*.qml \ + data/ordering\*.qml \ + data/override*.qml diff --git a/tests/auto/accessibility/data/ordering/page.qml b/tests/auto/accessibility/data/ordering/page.qml new file mode 100644 index 00000000..5eeb1530 --- /dev/null +++ b/tests/auto/accessibility/data/ordering/page.qml @@ -0,0 +1,23 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Page { + title: "Page" + Accessible.role: Accessible.Pane + + header: Label { + text: "Header" + } + + footer: Label { + text: "Footer" + } + + Label { + text: "Content item 1" + } + + Label { + text: "Content item 2" + } +} diff --git a/tests/auto/accessibility/tst_accessibility.cpp b/tests/auto/accessibility/tst_accessibility.cpp index 6e5a37df..30bd4757 100644 --- a/tests/auto/accessibility/tst_accessibility.cpp +++ b/tests/auto/accessibility/tst_accessibility.cpp @@ -60,6 +60,7 @@ private slots: void override_data(); void override(); + void ordering(); private: QQmlEngine engine; }; @@ -284,6 +285,35 @@ void tst_accessibility::override() Q_UNUSED(text) #endif } +template <typename Predicate> +void a11yDescendants(QAccessibleInterface *iface, Predicate pred) +{ + for (int i = 0; i < iface->childCount(); ++i) { + if (QAccessibleInterface *child = iface->child(i)) { + pred(child); + a11yDescendants(child, pred); + } + } +} + +void tst_accessibility::ordering() +{ + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("ordering/page.qml")); + + QScopedPointer<QObject> object(component.create()); + QVERIFY2(!object.isNull(), qPrintable(component.errorString())); + +#if QT_CONFIG(accessibility) + QQuickItem *item = findItem(object.data()); + QVERIFY(item); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(item); + QVERIFY(iface); + QStringList strings; + a11yDescendants(iface, [&](QAccessibleInterface *iface) {strings << iface->text(QAccessible::Name);}); + QCOMPARE(strings.join(QLatin1String(", ")), "Header, Content item 1, Content item 2, Footer"); +#endif +} QTEST_MAIN(tst_accessibility) |