diff options
Diffstat (limited to 'tests/auto/quickcontrols2/focus')
-rw-r--r-- | tests/auto/quickcontrols2/focus/BLACKLIST | 3 | ||||
-rw-r--r-- | tests/auto/quickcontrols2/focus/CMakeLists.txt | 46 | ||||
-rw-r--r-- | tests/auto/quickcontrols2/focus/data/activeFocusOnTab.qml | 203 | ||||
-rw-r--r-- | tests/auto/quickcontrols2/focus/data/keyNavigation.qml | 260 | ||||
-rw-r--r-- | tests/auto/quickcontrols2/focus/data/visualFocus.qml | 64 | ||||
-rw-r--r-- | tests/auto/quickcontrols2/focus/tst_focus.cpp | 396 |
6 files changed, 972 insertions, 0 deletions
diff --git a/tests/auto/quickcontrols2/focus/BLACKLIST b/tests/auto/quickcontrols2/focus/BLACKLIST new file mode 100644 index 0000000000..730d384475 --- /dev/null +++ b/tests/auto/quickcontrols2/focus/BLACKLIST @@ -0,0 +1,3 @@ +# QTBUG-78261 +[policy] +opensuse-leap diff --git a/tests/auto/quickcontrols2/focus/CMakeLists.txt b/tests/auto/quickcontrols2/focus/CMakeLists.txt new file mode 100644 index 0000000000..74738cc2a5 --- /dev/null +++ b/tests/auto/quickcontrols2/focus/CMakeLists.txt @@ -0,0 +1,46 @@ +# Generated from focus.pro. + +##################################################################### +## tst_focus Test: +##################################################################### + +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_focus + SOURCES + ../shared/qtest_quickcontrols.h + ../shared/util.cpp ../shared/util.h + ../shared/visualtestutil.cpp ../shared/visualtestutil.h + tst_focus.cpp + DEFINES + QQC2_IMPORT_PATH=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/quickcontrols2\\\" + PUBLIC_LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QuickControls2 + Qt::QuickControls2Private + Qt::QuickPrivate + Qt::QuickTemplates2Private + Qt::QuickTest + Qt::TestPrivate + TESTDATA ${test_data} +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_focus CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=\\\":/data\\\" +) + +qt_internal_extend_target(tst_focus CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" +) diff --git a/tests/auto/quickcontrols2/focus/data/activeFocusOnTab.qml b/tests/auto/quickcontrols2/focus/data/activeFocusOnTab.qml new file mode 100644 index 0000000000..6b57f39b1e --- /dev/null +++ b/tests/auto/quickcontrols2/focus/data/activeFocusOnTab.qml @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls + +Item { + id: main + objectName: "main" + width: 400 + height: 800 + focus: true + Component.onCompleted: button1.focus = true + Column { + anchors.fill: parent + id: column + objectName: "column" + BusyIndicator { + id: busyindicator + objectName: "busyindicator" + } + Button { + id: button1 + objectName: "button1" + text: "button1" + } + Button { + id: button2 + objectName: "button2" + text: "button2" + } + CheckBox { + id: checkbox + objectName: "checkbox" + text: "checkbox" + } + GroupBox { + id: groupbox1 + objectName: "groupbox1" + title: "grouppox1" + Column { + anchors.fill: parent + CheckBox { + id: checkbox1 + objectName: "checkbox1" + text: "checkbox1" + } + CheckBox { + id: checkbox2 + objectName: "checkbox2" + text: "checkbox2" + } + } + } + Label { + id: label + objectName: "label" + text: "label" + } + PageIndicator { + id: pageindicator + objectName: "pageindicator" + } + ProgressBar { + id: progressbar + objectName: "progressbar" + indeterminate: true + } + RadioButton { + id: radiobutton + objectName: "radiobutton" + text: "radiobutton" + } + GroupBox { + id: groupbox2 + objectName: "groupbox2" + title: "groupbox2" + Column { + anchors.fill: parent + RadioButton { + id: radiobutton1 + objectName: "radiobutton1" + text: "radiobutton1" + } + RadioButton { + id: radiobutton2 + objectName: "radiobutton2" + text: "radiobutton2" + } + } + } + RangeSlider { + id: rangeslider + objectName: "rangeslider" + first.handle.objectName: "rangeslider.first" + second.handle.objectName: "rangeslider.second" + } + // ScrollBar + ScrollIndicator { + id: scrollindicator + objectName: "scrollindicator" + } + Slider { + id: slider + objectName: "slider" + value: 0.5 + } + SpinBox { + id: spinbox + objectName: "spinbox" + editable: true + value: 50 + } + // StackView + Switch { + id: swtich // switch + objectName: "switch" + text: "switch" + } + TabBar { + width: parent.width + id: tabbar + objectName: "tabbar" + TabButton { + id: tabbutton1 + objectName: "tabbutton1" + text: "tabbutton1" + } + TabButton { + id: tabbutton2 + objectName: "tabbutton2" + text: "tabbutton2" + } + } + TextField { + id: textfield + objectName: "textfield" + text: "abc" + } + ToolBar { + width: parent.width + id: toolbar + objectName: "toolbar" + ToolButton { + id: toolbutton + objectName: "toolbutton" + text: "toolbutton" + } + } + TextArea { + id: textarea + objectName: "textarea" + text: "abc" + } + } +} diff --git a/tests/auto/quickcontrols2/focus/data/keyNavigation.qml b/tests/auto/quickcontrols2/focus/data/keyNavigation.qml new file mode 100644 index 0000000000..7ac8e014cd --- /dev/null +++ b/tests/auto/quickcontrols2/focus/data/keyNavigation.qml @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls + +Item { + id: main + objectName: "main" + width: 400 + height: 800 + focus: true + Component.onCompleted: button1.focus = true + Column { + anchors.fill: parent + id: column + objectName: "column" + BusyIndicator { + id: busyindicator + objectName: "busyindicator" + } + Button { + id: button1 + objectName: "button1" + text: "button1" + KeyNavigation.up: textarea + KeyNavigation.down: button2 + KeyNavigation.left: toolbutton + KeyNavigation.right: button2 + } + Button { + id: button2 + objectName: "button2" + text: "button2" + KeyNavigation.up: button1 + KeyNavigation.down: checkbox + KeyNavigation.left: button1 + KeyNavigation.right: checkbox + } + CheckBox { + id: checkbox + objectName: "checkbox" + text: "checkbox" + KeyNavigation.up: button2 + KeyNavigation.down: checkbox1 + KeyNavigation.left: button2 + KeyNavigation.right: checkbox1 + } + GroupBox { + id: groupbox1 + objectName: "groupbox1" + title: "grouppox1" + Column { + anchors.fill: parent + CheckBox { + id: checkbox1 + objectName: "checkbox1" + text: "checkbox1" + KeyNavigation.up: checkbox + KeyNavigation.down: checkbox2 + KeyNavigation.left: checkbox + KeyNavigation.right: checkbox2 + } + CheckBox { + id: checkbox2 + objectName: "checkbox2" + text: "checkbox2" + KeyNavigation.up: checkbox1 + KeyNavigation.down: radiobutton + KeyNavigation.left: checkbox1 + KeyNavigation.right: radiobutton + } + } + } + Label { + id: label + objectName: "label" + text: "label" + } + PageIndicator { + id: pageindicator + objectName: "pageindicator" + } + ProgressBar { + id: progressbar + objectName: "progressbar" + indeterminate: true + } + RadioButton { + id: radiobutton + objectName: "radiobutton" + text: "radiobutton" + KeyNavigation.up: checkbox2 + KeyNavigation.down: radiobutton1 + KeyNavigation.left: checkbox2 + KeyNavigation.right: radiobutton1 + } + GroupBox { + id: groupbox2 + objectName: "groupbox2" + title: "groupbox2" + Column { + anchors.fill: parent + RadioButton { + id: radiobutton1 + objectName: "radiobutton1" + text: "radiobutton1" + KeyNavigation.up: radiobutton + KeyNavigation.down: radiobutton2 + KeyNavigation.left: radiobutton + KeyNavigation.right: radiobutton2 + } + RadioButton { + id: radiobutton2 + objectName: "radiobutton2" + text: "radiobutton2" + KeyNavigation.up: radiobutton1 + KeyNavigation.down: rangeslider + KeyNavigation.left: radiobutton1 + KeyNavigation.right: spinbox + } + } + } + RangeSlider { + id: rangeslider + objectName: "rangeslider" + first.handle.objectName: "rangeslider.first" + second.handle.objectName: "rangeslider.second" + KeyNavigation.up: radiobutton2 + KeyNavigation.down: slider + } + // ScrollBar + ScrollIndicator { + id: scrollindicator + objectName: "scrollindicator" + } + Slider { + id: slider + objectName: "slider" + value: 0.5 + KeyNavigation.up: rangeslider + KeyNavigation.down: swtich + } + SpinBox { + id: spinbox + objectName: "spinbox" + value: 50 + KeyNavigation.left: radiobutton2 + KeyNavigation.right: swtich + } + // StackView + Switch { + id: swtich // switch + objectName: "switch" + text: "switch" + KeyNavigation.up: slider + KeyNavigation.down: tabbutton1 + KeyNavigation.left: spinbox + KeyNavigation.right: tabbutton1 + } + TabBar { + width: parent.width + id: tabbar + objectName: "tabbar" + TabButton { + id: tabbutton1 + objectName: "tabbutton1" + text: "tabbutton1" + KeyNavigation.up: swtich + KeyNavigation.down: tabbutton2 + KeyNavigation.left: swtich + KeyNavigation.right: tabbutton2 + } + TabButton { + id: tabbutton2 + objectName: "tabbutton2" + text: "tabbutton2" + KeyNavigation.up: tabbutton1 + KeyNavigation.down: textfield + KeyNavigation.left: tabbutton1 + KeyNavigation.right: toolbutton + } + } + TextField { + id: textfield + objectName: "textfield" + text: "abc" + KeyNavigation.up: tabbutton2 + KeyNavigation.down: toolbutton + } + ToolBar { + width: parent.width + id: toolbar + objectName: "toolbar" + ToolButton { + id: toolbutton + objectName: "toolbutton" + text: "toolbutton" + KeyNavigation.up: textfield + KeyNavigation.down: textarea + KeyNavigation.left: tabbutton2 + KeyNavigation.right: button1 + } + } + TextArea { + id: textarea + objectName: "textarea" + text: "abc" + KeyNavigation.up: toolbutton + KeyNavigation.down: button1 + } + } +} diff --git a/tests/auto/quickcontrols2/focus/data/visualFocus.qml b/tests/auto/quickcontrols2/focus/data/visualFocus.qml new file mode 100644 index 0000000000..2f516be767 --- /dev/null +++ b/tests/auto/quickcontrols2/focus/data/visualFocus.qml @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls + +Column { + width: 400 + height: 400 + Button { + text: "Button" + property bool showFocus: visualFocus + } + TextField { + text: "TextField" + } +} diff --git a/tests/auto/quickcontrols2/focus/tst_focus.cpp b/tests/auto/quickcontrols2/focus/tst_focus.cpp new file mode 100644 index 0000000000..92df4665be --- /dev/null +++ b/tests/auto/quickcontrols2/focus/tst_focus.cpp @@ -0,0 +1,396 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite 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 <qtest.h> +#include <QtTest/QSignalSpy> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlcontext.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuickTemplates2/private/qquickcontrol_p.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpointingdevice.h> +#include <QtGui/qstylehints.h> +#include "../shared/util.h" +#include "../shared/visualtestutil.h" + +using namespace QQuickVisualTestUtil; + +class tst_focus : public QQmlDataTest +{ + Q_OBJECT + +private slots: + void initTestCase() override; + + void navigation_data(); + void navigation(); + + void policy_data(); + void policy(); + + void reason_data(); + void reason(); + + void visualFocus(); + + void scope_data(); + void scope(); +}; + +void tst_focus::initTestCase() +{ + QQuickStyle::setStyle("Basic"); + QQmlDataTest::initTestCase(); +} + +void tst_focus::navigation_data() +{ + QTest::addColumn<Qt::Key>("key"); + QTest::addColumn<QString>("testFile"); + QTest::addColumn<Qt::TabFocusBehavior>("behavior"); + QTest::addColumn<QStringList>("order"); + + QTest::newRow("tab-all-controls") << Qt::Key_Tab << QString("activeFocusOnTab.qml") << Qt::TabFocusAllControls << (QStringList() << "button2" << "checkbox" << "checkbox1" << "checkbox2" << "radiobutton" << "radiobutton1" << "radiobutton2" << "rangeslider.first" << "rangeslider.second" << "slider" << "spinbox" << "switch" << "tabbutton1" << "tabbutton2" << "textfield" << "toolbutton" << "textarea" << "button1"); + QTest::newRow("backtab-all-controls") << Qt::Key_Backtab << QString("activeFocusOnTab.qml") << Qt::TabFocusAllControls << (QStringList() << "textarea" << "toolbutton" << "textfield" << "tabbutton2" << "tabbutton1" << "switch" << "spinbox" << "slider" << "rangeslider.second" << "rangeslider.first" << "radiobutton2" << "radiobutton1" << "radiobutton" << "checkbox2" << "checkbox1" << "checkbox" << "button2" << "button1"); + + QTest::newRow("tab-text-controls") << Qt::Key_Tab << QString("activeFocusOnTab.qml") << Qt::TabFocusTextControls << (QStringList() << "spinbox" << "textfield" << "textarea"); + QTest::newRow("backtab-text-controls") << Qt::Key_Backtab << QString("activeFocusOnTab.qml") << Qt::TabFocusTextControls << (QStringList() << "textarea" << "textfield" << "spinbox"); + + QTest::newRow("key-up") << Qt::Key_Up << QString("keyNavigation.qml") << Qt::TabFocusAllControls << (QStringList() << "textarea" << "toolbutton" << "textfield" << "tabbutton2" << "tabbutton1" << "switch" << "slider" << "rangeslider.first" << "radiobutton2" << "radiobutton1" << "radiobutton" << "checkbox2" << "checkbox1" << "checkbox" << "button2" << "button1"); + QTest::newRow("key-down") << Qt::Key_Down << QString("keyNavigation.qml") << Qt::TabFocusAllControls << (QStringList() << "button2" << "checkbox" << "checkbox1" << "checkbox2" << "radiobutton" << "radiobutton1" << "radiobutton2" << "rangeslider.first" << "slider" << "switch" << "tabbutton1" << "tabbutton2" << "textfield" << "toolbutton" << "textarea" << "button1"); + QTest::newRow("key-left") << Qt::Key_Left << QString("keyNavigation.qml") << Qt::TabFocusAllControls << (QStringList() << "toolbutton" << "tabbutton2" << "tabbutton1" << "switch" << "spinbox" << "radiobutton2" << "radiobutton1" << "radiobutton" << "checkbox2" << "checkbox1" << "checkbox" << "button2" << "button1"); + QTest::newRow("key-right") << Qt::Key_Right << QString("keyNavigation.qml") << Qt::TabFocusAllControls << (QStringList() << "button2" << "checkbox" << "checkbox1" << "checkbox2" << "radiobutton" << "radiobutton1" << "radiobutton2" << "spinbox" << "switch" << "tabbutton1" << "tabbutton2" << "toolbutton" << "button1"); +} + +void tst_focus::navigation() +{ + QFETCH(Qt::Key, key); + QFETCH(QString, testFile); + QFETCH(Qt::TabFocusBehavior, behavior); + QFETCH(QStringList, order); + + QGuiApplication::styleHints()->setTabFocusBehavior(behavior); + + QQuickView view; + view.contentItem()->setObjectName("contentItem"); + + view.setSource(testFileUrl(testFile)); + view.show(); + view.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(QGuiApplication::focusWindow() == &view); + + for (const QString &name : qAsConst(order)) { + QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier); + QGuiApplication::sendEvent(&view, &event); + QVERIFY(event.isAccepted()); + + QQuickItem *item = findItem<QQuickItem>(view.rootObject(), name); + QVERIFY2(item, qPrintable(name)); + QVERIFY2(item->hasActiveFocus(), qPrintable(QString("expected: '%1', actual: '%2'").arg(name).arg(view.activeFocusItem() ? view.activeFocusItem()->objectName() : "null"))); + } + + QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusBehavior(-1)); +} + +void tst_focus::policy_data() +{ + QTest::addColumn<QString>("name"); + + QTest::newRow("Control") << "Control"; + QTest::newRow("ComboBox") << "ComboBox"; + QTest::newRow("Button") << "Button"; + QTest::newRow("Slider") << "Slider"; + QTest::newRow("ScrollBar") << "ScrollBar"; +} + +void tst_focus::policy() +{ + QFETCH(QString, name); + + QQmlEngine engine; + QScopedPointer<QPointingDevice> device(QTest::createTouchDevice()); + QQmlComponent component(&engine); + component.setData(QString("import QtQuick.Controls; ApplicationWindow { width: 100; height: 100; %1 { anchors.fill: parent } }").arg(name).toUtf8(), QUrl()); + + QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(component.create())); + QVERIFY(window); + + QQuickControl *control = qobject_cast<QQuickControl *>(window->contentItem()->childItems().first()); + QVERIFY(control); + + QVERIFY(!control->hasActiveFocus()); + QVERIFY(!control->hasVisualFocus()); + + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + control->setFocusPolicy(Qt::NoFocus); + QCOMPARE(control->focusPolicy(), Qt::NoFocus); + + // Qt::TabFocus vs. QQuickItem::activeFocusOnTab + control->setActiveFocusOnTab(true); + QCOMPARE(control->focusPolicy(), Qt::TabFocus); + control->setActiveFocusOnTab(false); + QCOMPARE(control->focusPolicy(), Qt::NoFocus); + + control->setFocusPolicy(Qt::TabFocus); + QCOMPARE(control->focusPolicy(), Qt::TabFocus); + QCOMPARE(control->activeFocusOnTab(), true); + + // Qt::TabFocus + QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls); + QTest::keyClick(window.data(), Qt::Key_Tab); + QVERIFY(control->hasActiveFocus()); + QVERIFY(control->hasVisualFocus()); + QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusBehavior(-1)); + + // reset + control->setFocus(false); + QVERIFY(!control->hasActiveFocus()); + + // Qt::ClickFocus (mouse) + control->setFocusPolicy(Qt::NoFocus); + control->setAcceptedMouseButtons(Qt::LeftButton); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(control->width() / 2, control->height() / 2)); + QVERIFY(!control->hasActiveFocus()); + QVERIFY(!control->hasVisualFocus()); + + control->setFocusPolicy(Qt::ClickFocus); + QCOMPARE(control->focusPolicy(), Qt::ClickFocus); + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(control->width() / 2, control->height() / 2)); + QVERIFY(control->hasActiveFocus()); + QVERIFY(!control->hasVisualFocus()); + + // reset + control->setFocus(false); + QVERIFY(!control->hasActiveFocus()); + + // Qt::ClickFocus (touch) + control->setFocusPolicy(Qt::NoFocus); + QTest::touchEvent(window.data(), device.data()).press(0, QPoint(control->width() / 2, control->height() / 2)); + QTest::touchEvent(window.data(), device.data()).release(0, QPoint(control->width() / 2, control->height() / 2)); + QVERIFY(!control->hasActiveFocus()); + QVERIFY(!control->hasVisualFocus()); + + control->setFocusPolicy(Qt::ClickFocus); + QCOMPARE(control->focusPolicy(), Qt::ClickFocus); + QTest::touchEvent(window.data(), device.data()).press(0, QPoint(control->width() / 2, control->height() / 2)); + QTest::touchEvent(window.data(), device.data()).release(0, QPoint(control->width() / 2, control->height() / 2)); + QVERIFY(control->hasActiveFocus()); + QVERIFY(!control->hasVisualFocus()); + + // reset + control->setFocus(false); + QVERIFY(!control->hasActiveFocus()); + + // Qt::WheelFocus + QWheelEvent wheelEvent(QPointF(control->width() / 2, control->height() / 2), QPointF(), + QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier, + Qt::NoScrollPhase, false); + QGuiApplication::sendEvent(control, &wheelEvent); + QVERIFY(!control->hasActiveFocus()); + QVERIFY(!control->hasVisualFocus()); + + control->setFocusPolicy(Qt::WheelFocus); + QCOMPARE(control->focusPolicy(), Qt::WheelFocus); + + QGuiApplication::sendEvent(control, &wheelEvent); + QVERIFY(control->hasActiveFocus()); + QVERIFY(!control->hasVisualFocus()); +} + +void tst_focus::reason_data() +{ + QTest::addColumn<QString>("name"); + + QTest::newRow("Control") << "Control"; + QTest::newRow("TextField") << "TextField"; + QTest::newRow("TextArea") << "TextArea"; + QTest::newRow("SpinBox") << "SpinBox"; + QTest::newRow("ComboBox") << "ComboBox"; +} + +void tst_focus::reason() +{ + QFETCH(QString, name); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(QString("import QtQuick.Controls; ApplicationWindow { width: 100; height: 100; %1 { anchors.fill: parent } }").arg(name).toUtf8(), QUrl()); + + QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(component.create())); + QVERIFY(window.data()); + + QQuickItem *control = window->contentItem()->childItems().first(); + QVERIFY(control); + + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + QCOMPARE(control->property("focusReason").toInt(), int(Qt::OtherFocusReason)); + control->forceActiveFocus(Qt::MouseFocusReason); + QVERIFY(control->hasActiveFocus()); + QCOMPARE(control->property("focusReason").toInt(), int(Qt::MouseFocusReason)); + + QEXPECT_FAIL("TextArea", "TODO: TextArea::visualFocus?", Continue); + QEXPECT_FAIL("TextField", "TODO: TextField::visualFocus?", Continue); + QCOMPARE(control->property("visualFocus"), QVariant(false)); + + window->contentItem()->setFocus(false, Qt::TabFocusReason); + QVERIFY(!control->hasActiveFocus()); + QCOMPARE(control->property("focusReason").toInt(), int(Qt::TabFocusReason)); + + QEXPECT_FAIL("TextArea", "", Continue); + QEXPECT_FAIL("TextField", "", Continue); + QCOMPARE(control->property("visualFocus"), QVariant(false)); + + control->forceActiveFocus(Qt::TabFocusReason); + QVERIFY(control->hasActiveFocus()); + QCOMPARE(control->property("focusReason").toInt(), int(Qt::TabFocusReason)); + + QEXPECT_FAIL("TextArea", "", Continue); + QEXPECT_FAIL("TextField", "", Continue); + QCOMPARE(control->property("visualFocus"), QVariant(true)); +} + +void tst_focus::visualFocus() +{ + QQuickView view; + view.setSource(testFileUrl("visualFocus.qml")); + view.show(); + view.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QQuickItem *column = view.rootObject(); + QVERIFY(column); + QCOMPARE(column->childItems().count(), 2); + + QQuickControl *button = qobject_cast<QQuickControl *>(column->childItems().first()); + QVERIFY(button); + + QQuickItem *textfield = column->childItems().last(); + QVERIFY(textfield); + + button->forceActiveFocus(Qt::TabFocusReason); + QVERIFY(button->hasActiveFocus()); + QVERIFY(button->hasVisualFocus()); + QVERIFY(button->property("showFocus").toBool()); + + QTest::mouseClick(&view, Qt::LeftButton, Qt::NoModifier, QPoint(textfield->x() + textfield->width() / 2, textfield->y() + textfield->height() / 2)); + QVERIFY(!button->hasActiveFocus()); + QVERIFY(!button->hasVisualFocus()); + QVERIFY(!button->property("showFocus").toBool()); +} + +void tst_focus::scope_data() +{ + QTest::addColumn<QString>("name"); + + QTest::newRow("Frame") << "Frame"; + QTest::newRow("GroupBox") << "Frame"; + QTest::newRow("Page") << "Page"; + QTest::newRow("Pane") << "Pane"; + QTest::newRow("StackView") << "StackView"; +} + +void tst_focus::scope() +{ + QFETCH(QString, name); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(QString("import QtQuick; import QtQuick.Controls; ApplicationWindow { property alias child: child; width: 100; height: 100; %1 { anchors.fill: parent; Item { id: child; width: 10; height: 10 } } }").arg(name).toUtf8(), QUrl()); + + QScopedPointer<QPointingDevice> device(QTest::createTouchDevice()); + QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(component.create())); + QVERIFY2(window, qPrintable(component.errorString())); + + QQuickControl *control = qobject_cast<QQuickControl *>(window->contentItem()->childItems().first()); + QVERIFY(control); + + control->setFocusPolicy(Qt::WheelFocus); + control->setAcceptedMouseButtons(Qt::LeftButton); + + QQuickItem *child = window->property("child").value<QQuickItem *>(); + QVERIFY(child); + + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + + + child->forceActiveFocus(); + QVERIFY(child->hasActiveFocus()); + QVERIFY(control->hasActiveFocus()); + + // Qt::ClickFocus (mouse) + QTest::mouseClick(window.data(), Qt::LeftButton, Qt::NoModifier, QPoint(control->width() / 2, control->height() / 2)); + QVERIFY(!child->hasActiveFocus()); + QVERIFY(control->hasActiveFocus()); + + // reset + child->forceActiveFocus(); + QVERIFY(child->hasActiveFocus()); + QVERIFY(control->hasActiveFocus()); + + // Qt::ClickFocus (touch) + QTest::touchEvent(window.data(), device.data()).press(0, QPoint(control->width() / 2, control->height() / 2)); + QTest::touchEvent(window.data(), device.data()).release(0, QPoint(control->width() / 2, control->height() / 2)); + QVERIFY(!child->hasActiveFocus()); + QVERIFY(control->hasActiveFocus()); + + // reset + child->forceActiveFocus(); + QVERIFY(child->hasActiveFocus()); + QVERIFY(control->hasActiveFocus()); + + // Qt::WheelFocus + QWheelEvent wheelEvent(QPointF(control->width() / 2, control->height() / 2), QPointF(), + QPoint(), QPoint(0, 10), Qt::NoButton, Qt::NoModifier, + Qt::NoScrollPhase, false); + QGuiApplication::sendEvent(control, &wheelEvent); + QVERIFY(!child->hasActiveFocus()); + QVERIFY(control->hasActiveFocus()); +} + +QTEST_MAIN(tst_focus) + +#include "tst_focus.moc" |