From 023f4ec1a135d1a3bd9ce659b095bd4ed2e8b806 Mon Sep 17 00:00:00 2001 From: Liang Qi <liang.qi@digia.com> Date: Thu, 21 Feb 2013 11:38:10 +0100 Subject: [PATCH] Support QQuickItem.activeFocusOnTab in QtQuick.Controls Change-Id: Ie23b504f590e6c4e7f2a1a9090c2dd8671389937 Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com> --- src/controls/Button.qml | 2 + src/controls/CheckBox.qml | 2 + src/controls/ComboBox.qml | 2 + src/controls/GroupBox.qml | 2 + src/controls/Label.qml | 1 + src/controls/ProgressBar.qml | 2 + src/controls/RadioButton.qml | 2 + src/controls/ScrollView.qml | 2 + src/controls/Slider.qml | 2 + src/controls/SpinBox.qml | 2 + src/controls/StatusBar.qml | 1 + src/controls/Tab.qml | 2 + src/controls/TabView.qml | 2 + src/controls/TextArea.qml | 2 + src/controls/TextField.qml | 2 + src/controls/ToolBar.qml | 1 + src/controls/ToolButton.qml | 2 + src/private/AbstractCheckable.qml | 2 + src/private/BasicButton.qml | 2 + src/private/Control.qml | 2 + src/private/ScrollBar.qml | 2 + src/private/TabBar.qml | 2 + .../activeFocusOnTab/activeFocusOnTab.pro | 12 + .../data/activeFocusOnTab.qml | 206 +++++++++ .../activeFocusOnTab/tst_activeFocusOnTab.cpp | 427 ++++++++++++++++++ tests/auto/auto.pro | 2 +- tests/auto/shared/util.cpp | 138 ++++++ tests/auto/shared/util.h | 111 +++++ tests/auto/shared/util.pri | 8 + tests/auto/shared/visualtestutil.cpp | 76 ++++ tests/auto/shared/visualtestutil.h | 117 +++++ 31 files changed, 1137 insertions(+), 1 deletion(-) create mode 100644 tests/auto/activeFocusOnTab/activeFocusOnTab.pro create mode 100644 tests/auto/activeFocusOnTab/data/activeFocusOnTab.qml create mode 100644 tests/auto/activeFocusOnTab/tst_activeFocusOnTab.cpp create mode 100644 tests/auto/shared/util.cpp create mode 100644 tests/auto/shared/util.h create mode 100644 tests/auto/shared/util.pri create mode 100644 tests/auto/shared/visualtestutil.cpp create mode 100644 tests/auto/shared/visualtestutil.h diff --git a/src/controls/Button.qml b/src/controls/Button.qml index 5317e6147..6872a143a 100644 --- a/src/controls/Button.qml +++ b/src/controls/Button.qml @@ -83,6 +83,8 @@ BasicButton { */ property url iconSource + activeFocusOnTab: true + Accessible.name: text style: Qt.createComponent(Settings.THEME_PATH + "/ButtonStyle.qml", button) diff --git a/src/controls/CheckBox.qml b/src/controls/CheckBox.qml index c459e812b..163575331 100644 --- a/src/controls/CheckBox.qml +++ b/src/controls/CheckBox.qml @@ -132,6 +132,8 @@ AbstractCheckable { style: Qt.createComponent(Settings.THEME_PATH + "/CheckBoxStyle.qml", checkBox) + activeFocusOnTab: true + Accessible.role: Accessible.CheckBox Accessible.name: text diff --git a/src/controls/ComboBox.qml b/src/controls/ComboBox.qml index 8942683e3..47efb700e 100644 --- a/src/controls/ComboBox.qml +++ b/src/controls/ComboBox.qml @@ -93,6 +93,8 @@ Control { style: Qt.createComponent(Settings.THEME_PATH + "/ComboBoxStyle.qml", comboBox) + activeFocusOnTab: true + Accessible.role: Accessible.ComboBox MouseArea { diff --git a/src/controls/GroupBox.qml b/src/controls/GroupBox.qml index 0571d1fb7..16c40d922 100644 --- a/src/controls/GroupBox.qml +++ b/src/controls/GroupBox.qml @@ -151,6 +151,8 @@ Item { Accessible.role: Accessible.Grouping Accessible.name: title + activeFocusOnTab: false + Loader { id: loader property alias control: groupbox diff --git a/src/controls/Label.qml b/src/controls/Label.qml index e983ecc79..db6de97f4 100644 --- a/src/controls/Label.qml +++ b/src/controls/Label.qml @@ -81,6 +81,7 @@ Text { id: label color: pal.text + activeFocusOnTab: false renderType: Text.NativeRendering SystemPalette { id: pal diff --git a/src/controls/ProgressBar.qml b/src/controls/ProgressBar.qml index e49ae8be8..381936544 100644 --- a/src/controls/ProgressBar.qml +++ b/src/controls/ProgressBar.qml @@ -112,6 +112,8 @@ Control { setValue(value) } + activeFocusOnTab: false + Accessible.role: Accessible.ProgressBar Accessible.name: value diff --git a/src/controls/RadioButton.qml b/src/controls/RadioButton.qml index bfb37a606..87f572578 100644 --- a/src/controls/RadioButton.qml +++ b/src/controls/RadioButton.qml @@ -77,6 +77,8 @@ import "Styles/Settings.js" as Settings AbstractCheckable { id: radioButton + activeFocusOnTab: true + Accessible.role: Accessible.RadioButton /*! diff --git a/src/controls/ScrollView.qml b/src/controls/ScrollView.qml index 8cf5b48ae..6bdc7c2a5 100644 --- a/src/controls/ScrollView.qml +++ b/src/controls/ScrollView.qml @@ -138,6 +138,8 @@ FocusScope { /*! \internal */ property alias verticalScrollBar: scroller.verticalScrollBar + activeFocusOnTab: true + /*! \internal */ onContentItemChanged: { diff --git a/src/controls/Slider.qml b/src/controls/Slider.qml index 7f7231acf..bb2102ff2 100644 --- a/src/controls/Slider.qml +++ b/src/controls/Slider.qml @@ -167,6 +167,8 @@ Control { /*! \internal */ property bool __horizontal: orientation === Qt.Horizontal + activeFocusOnTab: true + Accessible.role: Accessible.Slider Accessible.name: value diff --git a/src/controls/SpinBox.qml b/src/controls/SpinBox.qml index 576a13c10..de836b24b 100644 --- a/src/controls/SpinBox.qml +++ b/src/controls/SpinBox.qml @@ -206,6 +206,8 @@ Control { /*! \internal */ onValueChanged: if (__initialized) input.setValue(value) + activeFocusOnTab: true + Accessible.name: input.text Accessible.role: Accessible.SpinBox diff --git a/src/controls/StatusBar.qml b/src/controls/StatusBar.qml index f0a6011f8..b3987cb4a 100644 --- a/src/controls/StatusBar.qml +++ b/src/controls/StatusBar.qml @@ -69,6 +69,7 @@ Item { id: statusbar implicitHeight: 20 implicitWidth: parent ? parent.width : style.implicitWidth + activeFocusOnTab: false StyleItem { id: style anchors.fill: parent diff --git a/src/controls/Tab.qml b/src/controls/Tab.qml index b7d4fb7f5..3c86cc3c4 100644 --- a/src/controls/Tab.qml +++ b/src/controls/Tab.qml @@ -60,6 +60,8 @@ Loader { active: false visible: false + activeFocusOnTab: false + /*! \internal */ onVisibleChanged: if (visible) active = true diff --git a/src/controls/TabView.qml b/src/controls/TabView.qml index 2248c8569..d24e677c4 100644 --- a/src/controls/TabView.qml +++ b/src/controls/TabView.qml @@ -141,6 +141,8 @@ FocusScope { count = __tabs.length } + activeFocusOnTab: false + Component { id: tabcomp Tab {} diff --git a/src/controls/TextArea.qml b/src/controls/TextArea.qml index 474011479..c34315c35 100644 --- a/src/controls/TextArea.qml +++ b/src/controls/TextArea.qml @@ -616,6 +616,8 @@ ScrollView { flickableItem.contentWidth: edit.paintedWidth + (2 * documentMargins) frameVisible: true + activeFocusOnTab: true + Accessible.role: Accessible.EditableText /*! diff --git a/src/controls/TextField.qml b/src/controls/TextField.qml index 8a9173a93..8fd9353bf 100644 --- a/src/controls/TextField.qml +++ b/src/controls/TextField.qml @@ -539,6 +539,8 @@ Control { textInput.forceActiveFocus(); } + activeFocusOnTab: true + Accessible.name: text Accessible.role: Accessible.EditableText Accessible.description: placeholderText diff --git a/src/controls/ToolBar.qml b/src/controls/ToolBar.qml index fd2924ac4..0c555cb55 100644 --- a/src/controls/ToolBar.qml +++ b/src/controls/ToolBar.qml @@ -71,6 +71,7 @@ import QtQuick.Controls.Private 1.0 Item { implicitHeight: toolbar.implicitHeight implicitWidth: parent ? parent.width : toolbar.implicitWidth + activeFocusOnTab: false Accessible.role: Accessible.ToolBar StyleItem { id: toolbar diff --git a/src/controls/ToolButton.qml b/src/controls/ToolButton.qml index 007bbec78..445ab96b2 100644 --- a/src/controls/ToolButton.qml +++ b/src/controls/ToolButton.qml @@ -74,6 +74,8 @@ BasicButton { /*! The label text. */ property string text + activeFocusOnTab: true + Accessible.name: text style: Qt.createComponent(Settings.THEME_PATH + "/ToolButtonStyle.qml", button) diff --git a/src/private/AbstractCheckable.qml b/src/private/AbstractCheckable.qml index f0acf48fb..905d4b653 100644 --- a/src/private/AbstractCheckable.qml +++ b/src/private/AbstractCheckable.qml @@ -106,6 +106,8 @@ Control { */ readonly property alias __containsMouse: mouseArea.containsMouse + activeFocusOnTab: true + MouseArea { id: mouseArea focus: true diff --git a/src/private/BasicButton.qml b/src/private/BasicButton.qml index 412028a08..f0d2a9ece 100644 --- a/src/private/BasicButton.qml +++ b/src/private/BasicButton.qml @@ -122,6 +122,8 @@ Control { onTriggered: button.clicked() } + activeFocusOnTab: true + Keys.onPressed: { if (event.key === Qt.Key_Space && !event.isAutoRepeat && !behavior.pressed) behavior.keyPressed = true; diff --git a/src/private/Control.qml b/src/private/Control.qml index e18e83f30..06bcdce64 100644 --- a/src/private/Control.qml +++ b/src/private/Control.qml @@ -65,6 +65,8 @@ FocusScope { /* \internal */ implicitHeight: __panel ? __panel.implicitHeight: 0 + activeFocusOnTab: false + Loader { id: panelLoader anchors.fill: parent diff --git a/src/private/ScrollBar.qml b/src/private/ScrollBar.qml index a81414e2c..d11fcef6b 100644 --- a/src/private/ScrollBar.qml +++ b/src/private/ScrollBar.qml @@ -59,6 +59,8 @@ Item { property Component style: Qt.createComponent("../" + Settings.THEME_PATH + "/ScrollBarStyle.qml", scrollbar) property alias styleItem: loader.item + activeFocusOnTab: false + Accessible.role: Accessible.ScrollBar implicitWidth: loader.implicitWidth implicitHeight: loader.implicitHeight diff --git a/src/private/TabBar.qml b/src/private/TabBar.qml index a96ee1005..266d20e1a 100644 --- a/src/private/TabBar.qml +++ b/src/private/TabBar.qml @@ -51,6 +51,8 @@ FocusScope { height: tabrow.height width: tabrow.width + activeFocusOnTab: true + Keys.onRightPressed: { if (tabView && tabView.currentIndex < tabView.count - 1) tabView.currentIndex = tabView.currentIndex + 1 diff --git a/tests/auto/activeFocusOnTab/activeFocusOnTab.pro b/tests/auto/activeFocusOnTab/activeFocusOnTab.pro new file mode 100644 index 000000000..1a32e0e6d --- /dev/null +++ b/tests/auto/activeFocusOnTab/activeFocusOnTab.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_activeFocusOnTab +macx:CONFIG -= app_bundle + +SOURCES += tst_activeFocusOnTab.cpp + +include (../shared/util.pri) + +TESTDATA = data/* + +QT += widgets core-private gui-private v8-private qml-private quick-private testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/activeFocusOnTab/data/activeFocusOnTab.qml b/tests/auto/activeFocusOnTab/data/activeFocusOnTab.qml new file mode 100644 index 000000000..1c95dc50e --- /dev/null +++ b/tests/auto/activeFocusOnTab/data/activeFocusOnTab.qml @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 Digia Plc and its Subsidiary(-ies) 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 2.0 +import QtQuick.Controls 1.0 + +Item { + id: main + objectName: "main" + width: 800 + height: 600 + focus: true + Component.onCompleted: button1.focus = true + Column { + anchors.fill: parent + id: column + objectName: "column" + Button { + id: button1 + objectName: "button1" + text: "button 1" + } + Button { + id: button2 + objectName: "button2" + text: "button 2" + } + Label { + id: label + objectName: "label" + text: "label" + } + ToolButton { + id: toolbutton + objectName: "toolbutton" + iconSource: "images/testIcon.png" + tooltip: "Test Icon" + } + ListModel { + id: choices + ListElement { text: "Banana" } + ListElement { text: "Orange" } + ListElement { text: "Apple" } + ListElement { text: "Coconut" } + } + ComboBox { + id: combobox; + objectName: "combobox" + model: choices; + } + GroupBox { + id: group1 + objectName: "group1" + title: "GroupBox 1" + checkable: true + __checkbox.objectName: "group1_checkbox" + Row { + CheckBox { + id: checkbox1 + objectName: "checkbox1" + text: "Text frame" + checked: true + } + CheckBox { + id: checkbox2 + objectName: "checkbox2" + text: "Tickmarks" + checked: false + } + } + } + GroupBox { + id: group2 + objectName: "group2" + title: "GroupBox 2" + Row { + RadioButton { + id: radiobutton1 + objectName: "radiobutton1" + text: "North" + checked: true + } + RadioButton { + id: radiobutton2 + objectName: "radiobutton2" + text: "South" + } + } + } + //Page + //ProgressBar maybe not need + ProgressBar { + id: progressbar + objectName: "progressbar" + indeterminate: true + } + //ScrollArea + Slider { + id: slider + objectName: "slider" + value: 0.5 + } + SpinBox { + id: spinbox + objectName: "spinbox" + width: 70 + minimumValue: 0 + maximumValue: 100 + value: 50 + } + //SplitterColumn and SplitterRow false + //StatusBar false + TabView { + id: tabview + objectName: "tabview" + width: 200 + Tab { + id: tab1 + objectName: "tab1" + title: "Tab1" + Column { + id: column_in_tab1 + objectName: "column_in_tab1" + anchors.fill: parent + Button { + id: tab1_btn1 + objectName: "tab1_btn1" + text: "button 1 in Tab1" + } + Button { + id: tab1_btn2 + objectName: "tab1_btn2" + text: "button 2 in Tab1" + } + } + } + Tab { + id: tab2 + objectName: "tab2" + title: "Tab2" + Column { + id: column_in_tab2 + objectName: "column_in_tab2" + anchors.fill: parent + Button { + id: tab2_btn1 + objectName: "tab2_btn1" + text: "button 1 in Tab2" + } + Button { + id: tab2_btn2 + objectName: "tab2_btn2" + text: "button 2 in Tab2" + } + } + } + } + TextField { + id: textfield + objectName: "textfield" + text: "abc" + } + TextArea { + id: textarea + objectName: "textarea" + text: "abc" + } + } +} diff --git a/tests/auto/activeFocusOnTab/tst_activeFocusOnTab.cpp b/tests/auto/activeFocusOnTab/tst_activeFocusOnTab.cpp new file mode 100644 index 000000000..19d0d194c --- /dev/null +++ b/tests/auto/activeFocusOnTab/tst_activeFocusOnTab.cpp @@ -0,0 +1,427 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.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 "../shared/util.h" +#include "../shared/visualtestutil.h" + +using namespace QQuickVisualTestUtil; + +class tst_activeFocusOnTab : public QQmlDataTest +{ + Q_OBJECT +public: + tst_activeFocusOnTab(); + +private slots: + void initTestCase(); + void cleanup(); + + void activeFocusOnTab(); +private: + QQmlEngine engine; +}; + +tst_activeFocusOnTab::tst_activeFocusOnTab() +{ +} + +void tst_activeFocusOnTab::initTestCase() +{ + QQmlDataTest::initTestCase(); +} + +void tst_activeFocusOnTab::cleanup() +{ +} + +void tst_activeFocusOnTab::activeFocusOnTab() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("activeFocusOnTab.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + // original: button1 + QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "button1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: button1->button2 + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "button2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: button2->toolbutton + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "toolbutton"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: toolbutton->combobox + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "combobox"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: combobox->group1_checkbox + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "group1"); + QVERIFY(item); + QQuickItem *subitem = findItem<QQuickItem>(item, "group1_checkbox"); + QVERIFY(subitem); + QVERIFY(subitem->hasActiveFocus()); + + // Tab: group1->checkbox1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "checkbox1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: checkbox1->checkbox2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "checkbox2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: checkbox2->radiobutton1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "radiobutton1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: radiobutton1->radiobutton2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "radiobutton2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: radiobutton2->slider + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "slider"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: slider->spinbox + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "spinbox"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: spinbox->tab1_btn1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + for (int i = 0; i < 2; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem<QQuickItem>(window->rootObject(), "tab1_btn1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: tab1_btn1->tab1_btn2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "tab1_btn2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: tab1_btn2->textfield + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "textfield"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: textfield->textarea + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "textarea"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: textarea->textfield + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "textfield"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: textfield->tab1_btn2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "tab1_btn2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: tab1_btn2->tab1_btn1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "tab1_btn1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: tab1_btn1->tab1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); +/* + // Right: tab1->tab2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + // Tab: tab2->tab2_btn1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "tab2_btn1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: tab2_btn1->tab2_btn2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "tab2_btn2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: tab2_btn2->textfield + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "textfield"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: textfield->tab2_btn2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "tab2_btn2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: tab2_btn2->tab2_btn1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "tab2_btn1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: tab2_btn1->tab2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); +*/ + // BackTab: tab2->spinbox + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "spinbox"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: spinbox->slider + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "slider"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: slider->radiobutton2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "radiobutton2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: radiobutton2->radiobutton1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "radiobutton1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: radiobutton1->checkbox2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "checkbox2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: checkbox2->checkbox1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "checkbox1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: checkbox1->group1_checkbox + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "group1_checkbox"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: group1_checkbox->combobox + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "combobox"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: combobox->toolbutton + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "toolbutton"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: toolbutton->button2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "button2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button2->button1 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "button1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button1->textarea + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QQuickItem>(window->rootObject(), "textarea"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + delete window; +} + +QTEST_MAIN(tst_activeFocusOnTab) + +#include "tst_activeFocusOnTab.moc" diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index dcbf1f8b5..927b7ccac 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs -SUBDIRS += testplugin controls +SUBDIRS += testplugin controls activeFocusOnTab CONFIG += ordered diff --git a/tests/auto/shared/util.cpp b/tests/auto/shared/util.cpp new file mode 100644 index 000000000..e0b14d60e --- /dev/null +++ b/tests/auto/shared/util.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "util.h" + +#include <QtQml/QQmlComponent> +#include <QtQml/QQmlError> +#include <QtQml/QQmlContext> +#include <QtQml/QQmlEngine> +#include <QtCore/QTextStream> +#include <QtCore/QDebug> +#include <QtCore/QMutexLocker> + +QQmlDataTest *QQmlDataTest::m_instance = 0; + +QQmlDataTest::QQmlDataTest() : +#ifdef QT_TESTCASE_BUILDDIR + m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0, QT_TESTCASE_BUILDDIR)), +#else + m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0)), +#endif + + m_dataDirectoryUrl(QUrl::fromLocalFile(m_dataDirectory + QLatin1Char('/'))) +{ + m_instance = this; +} + +QQmlDataTest::~QQmlDataTest() +{ + m_instance = 0; +} + +void QQmlDataTest::initTestCase() +{ + QVERIFY2(!m_dataDirectory.isEmpty(), "'data' directory not found"); + m_directory = QFileInfo(m_dataDirectory).absolutePath(); + QVERIFY2(QDir::setCurrent(m_directory), qPrintable(QLatin1String("Could not chdir to ") + m_directory)); +} + +QString QQmlDataTest::testFile(const QString &fileName) const +{ + if (m_directory.isEmpty()) + qFatal("QQmlDataTest::initTestCase() not called."); + QString result = m_dataDirectory; + result += QLatin1Char('/'); + result += fileName; + return result; +} + +QByteArray QQmlDataTest::msgComponentError(const QQmlComponent &c, + const QQmlEngine *engine /* = 0 */) +{ + QString result; + const QList<QQmlError> errors = c.errors(); + QTextStream str(&result); + str << "Component '" << c.url().toString() << "' has " << errors.size() + << " errors: '"; + for (int i = 0; i < errors.size(); ++i) { + if (i) + str << ", '"; + str << errors.at(i).toString() << '\''; + + } + if (!engine) + if (QQmlContext *context = c.creationContext()) + engine = context->engine(); + if (engine) { + str << " Import paths: (" << engine->importPathList().join(QStringLiteral(", ")) + << ") Plugin paths: (" << engine->pluginPathList().join(QStringLiteral(", ")) + << ')'; + } + return result.toLocal8Bit(); +} + +Q_GLOBAL_STATIC(QMutex, qQmlTestMessageHandlerMutex) + +QQmlTestMessageHandler *QQmlTestMessageHandler::m_instance = 0; + +void QQmlTestMessageHandler::messageHandler(QtMsgType, const QMessageLogContext &, const QString &message) +{ + QMutexLocker locker(qQmlTestMessageHandlerMutex()); + if (QQmlTestMessageHandler::m_instance) + QQmlTestMessageHandler::m_instance->m_messages.push_back(message); +} + +QQmlTestMessageHandler::QQmlTestMessageHandler() +{ + QMutexLocker locker(qQmlTestMessageHandlerMutex()); + Q_ASSERT(!QQmlTestMessageHandler::m_instance); + QQmlTestMessageHandler::m_instance = this; + m_oldHandler = qInstallMessageHandler(messageHandler); +} + +QQmlTestMessageHandler::~QQmlTestMessageHandler() +{ + QMutexLocker locker(qQmlTestMessageHandlerMutex()); + Q_ASSERT(QQmlTestMessageHandler::m_instance); + qInstallMessageHandler(m_oldHandler); + QQmlTestMessageHandler::m_instance = 0; +} diff --git a/tests/auto/shared/util.h b/tests/auto/shared/util.h new file mode 100644 index 000000000..dc9303825 --- /dev/null +++ b/tests/auto/shared/util.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTESTUTILS_H +#define QQMLTESTUTILS_H + +#include <QtCore/QDir> +#include <QtCore/QUrl> +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtTest/QTest> + +QT_FORWARD_DECLARE_CLASS(QQmlComponent) +QT_FORWARD_DECLARE_CLASS(QQmlEngine) + +/* Base class for tests with data that are located in a "data" subfolder. */ + +class QQmlDataTest : public QObject +{ + Q_OBJECT +public: + QQmlDataTest(); + virtual ~QQmlDataTest(); + + QString testFile(const QString &fileName) const; + inline QString testFile(const char *fileName) const + { return testFile(QLatin1String(fileName)); } + inline QUrl testFileUrl(const QString &fileName) const + { return QUrl::fromLocalFile(testFile(fileName)); } + inline QUrl testFileUrl(const char *fileName) const + { return testFileUrl(QLatin1String(fileName)); } + + inline QString dataDirectory() const { return m_dataDirectory; } + inline QUrl dataDirectoryUrl() const { return m_dataDirectoryUrl; } + inline QString directory() const { return m_directory; } + + static inline QQmlDataTest *instance() { return m_instance; } + + static QByteArray msgComponentError(const QQmlComponent &, + const QQmlEngine *engine = 0); + +public slots: + virtual void initTestCase(); + +private: + static QQmlDataTest *m_instance; + + const QString m_dataDirectory; + const QUrl m_dataDirectoryUrl; + QString m_directory; +}; + +class QQmlTestMessageHandler +{ + Q_DISABLE_COPY(QQmlTestMessageHandler) +public: + QQmlTestMessageHandler(); + ~QQmlTestMessageHandler(); + + const QStringList &messages() const { return m_messages; } + const QString messageString() const { return m_messages.join(QLatin1Char('\n')); } + + void clear() { m_messages.clear(); } + +private: + static void messageHandler(QtMsgType, const QMessageLogContext &, const QString &message); + + static QQmlTestMessageHandler *m_instance; + QStringList m_messages; + QtMessageHandler m_oldHandler; +}; + +#endif // QQMLTESTUTILS_H diff --git a/tests/auto/shared/util.pri b/tests/auto/shared/util.pri new file mode 100644 index 000000000..38c2e6a1a --- /dev/null +++ b/tests/auto/shared/util.pri @@ -0,0 +1,8 @@ +QT += core-private gui-private v8-private qml-private quick-private + +HEADERS += $$PWD/visualtestutil.h \ + $$PWD/util.h +SOURCES += $$PWD/visualtestutil.cpp \ + $$PWD/util.cpp + +DEFINES += QT_QMLTEST_DATADIR=\\\"$${_PRO_FILE_PWD_}/data\\\" diff --git a/tests/auto/shared/visualtestutil.cpp b/tests/auto/shared/visualtestutil.cpp new file mode 100644 index 000000000..4b5c201a3 --- /dev/null +++ b/tests/auto/shared/visualtestutil.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "visualtestutil.h" + +#include <QtQuick/QQuickItem> +#include <QtCore/QDebug> + +bool QQuickVisualTestUtil::delegateVisible(QQuickItem *item) +{ + return item->isVisible() && !QQuickItemPrivate::get(item)->culled; +} + +QQuickItem *QQuickVisualTestUtil::findVisibleChild(QQuickItem *parent, const QString &objectName) +{ + QQuickItem *item = 0; + QList<QQuickItem*> items = parent->findChildren<QQuickItem*>(objectName); + for (int i = 0; i < items.count(); ++i) { + if (items.at(i)->isVisible() && !QQuickItemPrivate::get(items.at(i))->culled) { + item = items.at(i); + break; + } + } + return item; +} + +void QQuickVisualTestUtil::dumpTree(QQuickItem *parent, int depth) +{ + static QString padding(" "); + for (int i = 0; i < parent->childItems().count(); ++i) { + QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i)); + if (!item) + continue; + qDebug() << padding.left(depth*2) << item; + dumpTree(item, depth+1); + } +} + diff --git a/tests/auto/shared/visualtestutil.h b/tests/auto/shared/visualtestutil.h new file mode 100644 index 000000000..5edb5d08c --- /dev/null +++ b/tests/auto/shared/visualtestutil.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKVISUALTESTUTIL_H +#define QQUICKVISUALTESTUTIL_H + +#include <QtQuick/QQuickItem> +#include <QtQml/QQmlExpression> + +#include <QtQuick/private/qquickitem_p.h> + +namespace QQuickVisualTestUtil +{ + QQuickItem *findVisibleChild(QQuickItem *parent, const QString &objectName); + + void dumpTree(QQuickItem *parent, int depth = 0); + + bool delegateVisible(QQuickItem *item); + + /* + Find an item with the specified objectName. If index is supplied then the + item must also evaluate the {index} expression equal to index + */ + template<typename T> + T *findItem(QQuickItem *parent, const QString &objectName, int index = -1) + { + const QMetaObject &mo = T::staticMetaObject; + for (int i = 0; i < parent->childItems().count(); ++i) { + QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i)); + if (!item) + continue; + if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) { + if (index != -1) { + QQmlExpression e(qmlContext(item), item, "index"); + if (e.evaluate().toInt() == index) + return static_cast<T*>(item); + } else { + return static_cast<T*>(item); + } + } + item = findItem<T>(item, objectName, index); + if (item) + return static_cast<T*>(item); + } + + return 0; + } + + template<typename T> + QList<T*> findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly = true) + { + QList<T*> items; + const QMetaObject &mo = T::staticMetaObject; + for (int i = 0; i < parent->childItems().count(); ++i) { + QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i)); + if (!item || (visibleOnly && (!item->isVisible() || QQuickItemPrivate::get(item)->culled))) + continue; + if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) + items.append(static_cast<T*>(item)); + items += findItems<T>(item, objectName); + } + + return items; + } + + template<typename T> + QList<T*> findItems(QQuickItem *parent, const QString &objectName, const QList<int> &indexes) + { + QList<T*> items; + for (int i=0; i<indexes.count(); i++) + items << qobject_cast<QQuickItem*>(findItem<T>(parent, objectName, indexes[i])); + return items; + } +} + +#define QQUICK_VERIFY_POLISH(item) \ + QTRY_COMPARE(QQuickItemPrivate::get(item)->polishScheduled, false) + +#endif // QQUICKVISUALTESTUTIL_H -- GitLab