summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/auto.pro2
-rw-r--r--tests/auto/extras/data/PieMenu3Items.qml53
-rw-r--r--tests/auto/extras/data/PieMenu3ItemsKeepOpen.qml54
-rw-r--r--tests/auto/extras/data/PieMenu3ItemsLongPress.qml44
-rw-r--r--tests/auto/extras/data/PieMenuBoundingItem.qml51
-rw-r--r--tests/auto/extras/data/PieMenuRotatedBoundingItem.qml49
-rw-r--r--tests/auto/extras/data/PieMenuVisibleButNoParent.qml12
-rw-r--r--tests/auto/extras/data/PieMenuVisibleOnCompleted.qml14
-rw-r--r--tests/auto/extras/data/TestUtils.js55
-rw-r--r--tests/auto/extras/data/TumblerDatePicker.qml38
-rw-r--r--tests/auto/extras/data/picture.datbin0 -> 186 bytes
-rw-r--r--tests/auto/extras/data/tst_circulargauge.qml93
-rw-r--r--tests/auto/extras/data/tst_circulartickmarklabel.qml318
-rw-r--r--tests/auto/extras/data/tst_common.qml82
-rw-r--r--tests/auto/extras/data/tst_delaybutton.qml194
-rw-r--r--tests/auto/extras/data/tst_dial.qml367
-rw-r--r--tests/auto/extras/data/tst_gauge.qml287
-rw-r--r--tests/auto/extras/data/tst_picture.qml149
-rw-r--r--tests/auto/extras/data/tst_piemenu.qml836
-rw-r--r--tests/auto/extras/data/tst_statusindicator.qml127
-rw-r--r--tests/auto/extras/data/tst_togglebutton.qml65
-rw-r--r--tests/auto/extras/data/tst_tumbler.qml503
-rw-r--r--tests/auto/extras/extras.pro32
-rw-r--r--tests/auto/extras/tst_extras.cpp39
-rw-r--r--tests/auto/paint/paint.pro6
-rw-r--r--tests/auto/paint/tst_paint.cpp107
-rw-r--r--tests/benchmarks/benchmarks.pro3
-rw-r--r--tests/benchmarks/statusindicator/LotsOfIndicatorsActive.qml58
-rw-r--r--tests/benchmarks/statusindicator/LotsOfIndicatorsInactive.qml58
-rw-r--r--tests/benchmarks/statusindicator/statusindicator.pro13
-rw-r--r--tests/benchmarks/statusindicator/tst_statusindicator.cpp82
-rw-r--r--tests/tests.pro2
32 files changed, 3791 insertions, 2 deletions
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 810b4678..de1b45b6 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -1,3 +1,3 @@
TEMPLATE = subdirs
-SUBDIRS += testplugin controls activeFocusOnTab applicationwindow dialogs
+SUBDIRS += testplugin controls activeFocusOnTab applicationwindow dialogs extras paint
controls.depends = testplugin
diff --git a/tests/auto/extras/data/PieMenu3Items.qml b/tests/auto/extras/data/PieMenu3Items.qml
new file mode 100644
index 00000000..f9349e63
--- /dev/null
+++ b/tests/auto/extras/data/PieMenu3Items.qml
@@ -0,0 +1,53 @@
+import QtQuick 2.2
+import QtQuick.Controls 1.1
+import QtQuick.Extras 1.3
+
+Rectangle {
+ id: rect
+ anchors.fill: parent
+
+ signal actionTriggered(int index)
+
+ property alias mouseArea: area
+
+ MouseArea {
+ id: area
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onPressed: {
+ if (menu.triggerMode === TriggerMode.TriggerOnRelease)
+ pieMenu.popup(mouseX, mouseY);
+ }
+
+ onClicked: {
+ if (menu.triggerMode !== TriggerMode.TriggerOnRelease)
+ pieMenu.popup(mouseX, mouseY);
+ }
+ }
+
+ property alias pieMenu: menu
+
+ PieMenu {
+ id: menu
+ width: 200
+ height: 200
+
+ MenuItem {
+ text: "Action 1"
+ onTriggered: actionTriggered(0)
+ }
+ MenuItem {
+ text: "Action 2"
+ onTriggered: actionTriggered(1)
+ }
+ MenuItem {
+ text: "Action 3"
+ onTriggered: actionTriggered(2)
+ }
+ }
+ Rectangle {
+ anchors.fill: menu
+ color: "transparent"
+ border.color: "black"
+ }
+}
diff --git a/tests/auto/extras/data/PieMenu3ItemsKeepOpen.qml b/tests/auto/extras/data/PieMenu3ItemsKeepOpen.qml
new file mode 100644
index 00000000..ffbfa20a
--- /dev/null
+++ b/tests/auto/extras/data/PieMenu3ItemsKeepOpen.qml
@@ -0,0 +1,54 @@
+import QtQuick 2.2
+import QtQuick.Controls 1.1
+import QtQuick.Extras 1.3
+
+Rectangle {
+ id: rect
+ anchors.fill: parent
+
+ signal actionTriggered(int index)
+
+ property alias mouseArea: area
+
+ MouseArea {
+ id: area
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ onClicked: pieMenu.popup(mouseX, mouseY);
+ }
+
+ property alias pieMenu: menu
+
+ PieMenu {
+ id: menu
+ width: 200
+ height: 200
+
+ MenuItem {
+ text: "Action 1"
+ onTriggered: {
+ menu.visible = true;
+ actionTriggered(0);
+ }
+ }
+ MenuItem {
+ text: "Action 2"
+ onTriggered: {
+ menu.visible = true;
+ actionTriggered(1);
+ }
+ }
+ MenuItem {
+ text: "Action 3"
+ onTriggered: {
+ menu.visible = true;
+ actionTriggered(2);
+ }
+ }
+ }
+ Rectangle {
+ anchors.fill: menu
+ color: "transparent"
+ border.color: "black"
+ }
+}
diff --git a/tests/auto/extras/data/PieMenu3ItemsLongPress.qml b/tests/auto/extras/data/PieMenu3ItemsLongPress.qml
new file mode 100644
index 00000000..fddc5460
--- /dev/null
+++ b/tests/auto/extras/data/PieMenu3ItemsLongPress.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.2
+import QtQuick.Controls 1.1
+import QtQuick.Extras 1.3
+
+Rectangle {
+ id: rect
+ anchors.fill: parent
+
+ signal actionTriggered(int index)
+
+ property alias mouseArea: area
+
+ MouseArea {
+ id: area
+ anchors.fill: parent
+ onPressAndHold: pieMenu.popup(mouseX, mouseY)
+ }
+
+ property alias pieMenu: menu
+
+ PieMenu {
+ id: menu
+ width: 200
+ height: 200
+
+ MenuItem {
+ text: "Action 1"
+ onTriggered: actionTriggered(0)
+ }
+ MenuItem {
+ text: "Action 2"
+ onTriggered: actionTriggered(1)
+ }
+ MenuItem {
+ text: "Action 3"
+ onTriggered: actionTriggered(2)
+ }
+ }
+ Rectangle {
+ anchors.fill: menu
+ color: "transparent"
+ border.color: "black"
+ }
+}
diff --git a/tests/auto/extras/data/PieMenuBoundingItem.qml b/tests/auto/extras/data/PieMenuBoundingItem.qml
new file mode 100644
index 00000000..3daf0f19
--- /dev/null
+++ b/tests/auto/extras/data/PieMenuBoundingItem.qml
@@ -0,0 +1,51 @@
+import QtQuick 2.2
+import QtQuick.Controls 1.1
+import QtQuick.Extras 1.3
+
+Rectangle {
+ id: root
+ anchors.fill: parent
+ color: "black"
+
+ property alias mouseArea: area
+
+ property alias pieMenu: menu
+
+ readonly property int margins: 50
+ readonly property int boundingItemTopMargin: 100
+
+ Rectangle {
+ id: canvasContainer
+ anchors.fill: parent
+ anchors.topMargin: boundingItemTopMargin
+
+ Rectangle {
+ id: rect
+ anchors.fill: parent
+ anchors.margins: margins
+ border.color: "black"
+
+ MouseArea {
+ id: area
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton
+ onClicked: menu.popup(mouseX, mouseY)
+ }
+
+ PieMenu {
+ id: menu
+ boundingItem: canvasContainer
+
+ MenuItem {
+ text: "Action 1"
+ }
+ MenuItem {
+ text: "Action 2"
+ }
+ MenuItem {
+ text: "Action 3"
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/extras/data/PieMenuRotatedBoundingItem.qml b/tests/auto/extras/data/PieMenuRotatedBoundingItem.qml
new file mode 100644
index 00000000..2df592ab
--- /dev/null
+++ b/tests/auto/extras/data/PieMenuRotatedBoundingItem.qml
@@ -0,0 +1,49 @@
+import QtQuick 2.2
+import QtQuick.Controls 1.1
+import QtQuick.Extras 1.3
+
+Rectangle {
+ id: root
+ anchors.fill: parent
+
+ signal actionTriggered(int index)
+
+ property alias mouseArea: area
+
+ property alias pieMenu: menu
+
+ readonly property int margins: 50
+
+ Rectangle {
+ id: rect
+ rotation: 90
+ anchors.fill: parent
+ anchors.margins: root.margins
+ border.color: "black"
+
+ MouseArea {
+ id: area
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton
+ onClicked: menu.popup(mouseX, mouseY)
+ }
+
+ PieMenu {
+ id: menu
+ boundingItem: root
+
+ MenuItem {
+ text: "Action 1"
+ onTriggered: actionTriggered(0)
+ }
+ MenuItem {
+ text: "Action 2"
+ onTriggered: actionTriggered(1)
+ }
+ MenuItem {
+ text: "Action 3"
+ onTriggered: actionTriggered(2)
+ }
+ }
+ }
+}
diff --git a/tests/auto/extras/data/PieMenuVisibleButNoParent.qml b/tests/auto/extras/data/PieMenuVisibleButNoParent.qml
new file mode 100644
index 00000000..582e168a
--- /dev/null
+++ b/tests/auto/extras/data/PieMenuVisibleButNoParent.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.2
+import QtQuick.Extras 1.3
+
+Rectangle {
+ width: 400
+ height: 400
+
+ property PieMenu pieMenu: PieMenu {
+ id: pieMenu
+ visible: true
+ }
+}
diff --git a/tests/auto/extras/data/PieMenuVisibleOnCompleted.qml b/tests/auto/extras/data/PieMenuVisibleOnCompleted.qml
new file mode 100644
index 00000000..aa9cc18c
--- /dev/null
+++ b/tests/auto/extras/data/PieMenuVisibleOnCompleted.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.2
+import QtQuick.Extras 1.3
+
+Rectangle {
+ width: 400
+ height: 400
+
+ PieMenu {
+ Component.onCompleted: {
+ visible = true;
+ visible = false;
+ }
+ }
+}
diff --git a/tests/auto/extras/data/TestUtils.js b/tests/auto/extras/data/TestUtils.js
new file mode 100644
index 00000000..3cf2ff1b
--- /dev/null
+++ b/tests/auto/extras/data/TestUtils.js
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+.pragma library
+
+function findChild(parent, childObjectName) {
+ if (!parent)
+ return null;
+
+ for (var i = 0; i < parent.children.length; ++i) {
+ // Is this direct child of ours the child we're after?
+ var child = parent.children[i];
+ if (child.objectName === childObjectName)
+ return child;
+
+ // Try the direct child's children.
+ child = findChild(parent.children[i], childObjectName);
+ if (child)
+ return child;
+ }
+ return null;
+}
diff --git a/tests/auto/extras/data/TumblerDatePicker.qml b/tests/auto/extras/data/TumblerDatePicker.qml
new file mode 100644
index 00000000..9872fc00
--- /dev/null
+++ b/tests/auto/extras/data/TumblerDatePicker.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.3
+import QtQuick.Controls 1.2
+import QtQuick.Extras 1.3
+
+Tumbler {
+ id: tumbler
+
+ readonly property var days: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+
+ TumblerColumn {
+ id: tumblerDayColumn
+
+ function updateModel() {
+ var previousIndex = tumblerDayColumn.currentIndex;
+ var array = [];
+ var newDays = tumbler.days[monthColumn.currentIndex];
+ for (var i = 0; i < newDays; ++i) {
+ array.push(i + 1);
+ }
+ model = array;
+ tumbler.setCurrentIndexAt(0, Math.min(newDays - 1, previousIndex));
+ }
+ }
+ TumblerColumn {
+ id: monthColumn
+ model: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+ onCurrentIndexChanged: tumblerDayColumn.updateModel()
+ }
+ TumblerColumn {
+ model: ListModel {
+ Component.onCompleted: {
+ for (var i = 2000; i < 2100; ++i) {
+ append({value: i.toString()});
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/extras/data/picture.dat b/tests/auto/extras/data/picture.dat
new file mode 100644
index 00000000..e408aca2
--- /dev/null
+++ b/tests/auto/extras/data/picture.dat
Binary files differ
diff --git a/tests/auto/extras/data/tst_circulargauge.qml b/tests/auto/extras/data/tst_circulargauge.qml
new file mode 100644
index 00000000..16c0815f
--- /dev/null
+++ b/tests/auto/extras/data/tst_circulargauge.qml
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.1
+import "TestUtils.js" as TestUtils
+
+TestCase {
+ id: testcase
+ name: "Tests_CircularGauge"
+ when: windowShown
+ width: 400
+ height: 400
+
+ function test_instance() {
+ var gauge = Qt.createQmlObject('import QtQuick.Extras 1.3; CircularGauge { }', testcase, '');
+ verify (gauge, "CircularGauge: failed to create an instance")
+ verify(gauge.__style)
+ gauge.destroy()
+ }
+
+ property Component tickmark: Rectangle {
+ objectName: "tickmark" + styleData.index
+ implicitWidth: 2
+ implicitHeight: 6
+ color: "#c8c8c8"
+ }
+
+ property Component minorTickmark: Rectangle {
+ objectName: "minorTickmark" + styleData.index
+ implicitWidth: 1
+ implicitHeight: 3
+ color: "#c8c8c8"
+ }
+
+ property Component tickmarkLabel: Text {
+ objectName: "tickmarkLabel" + styleData.index
+ text: styleData.value
+ color: "#c8c8c8"
+ }
+
+ function test_tickmarksVisible() {
+ var gauge = Qt.createQmlObject("import QtQuick.Extras 1.3; CircularGauge { }", testcase, "");
+ verify(gauge, "CircularGauge: failed to create an instance");
+
+ gauge.__style.tickmark = tickmark;
+ gauge.__style.minorTickmark = minorTickmark;
+ gauge.__style.tickmarkLabel = tickmarkLabel;
+ verify(TestUtils.findChild(gauge, "tickmark0"));
+ verify(TestUtils.findChild(gauge, "minorTickmark0"));
+ verify(TestUtils.findChild(gauge, "tickmarkLabel0"));
+
+ gauge.tickmarksVisible = false;
+ verify(!TestUtils.findChild(gauge, "tickmark0"));
+ verify(!TestUtils.findChild(gauge, "minorTickmark0"));
+ verify(!TestUtils.findChild(gauge, "tickmarkLabel0"));
+
+ gauge.destroy();
+ }
+}
diff --git a/tests/auto/extras/data/tst_circulartickmarklabel.qml b/tests/auto/extras/data/tst_circulartickmarklabel.qml
new file mode 100644
index 00000000..2521cbe9
--- /dev/null
+++ b/tests/auto/extras/data/tst_circulartickmarklabel.qml
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.1
+import "TestUtils.js" as TestUtils
+
+TestCase {
+ id: testcase
+ name: "Tests_CircularTickmarkLabel"
+ visible: windowShown
+ when: windowShown
+ width: 400
+ height: 400
+
+ property var label: null
+
+ function init() {
+ label = Qt.createQmlObject("import QtQuick.Extras 1.3; import QtQuick.Extras.Private 1.0; CircularTickmarkLabel {}", testcase, "");
+ verify(label, "CircularTickmarkLabel: failed to create an instance");
+ verify(label.__style);
+ }
+
+ function cleanup() {
+ label.destroy();
+ }
+
+ function test_angleRange() {
+ label.minimumValueAngle = -180;
+ label.maximumValueAngle = 180;
+ compare(label.angleRange, 360);
+
+ label.minimumValueAngle = -140;
+ label.maximumValueAngle = 160;
+ compare(label.angleRange, 300);
+
+ label.minimumValueAngle = -40;
+ label.maximumValueAngle = -10;
+ compare(label.angleRange, 30);
+
+ label.minimumValueAngle = -40;
+ label.maximumValueAngle = 10;
+ compare(label.angleRange, 50);
+ }
+
+ function test_tickmarksAndLabels() {
+ label.minimumValueAngle = 0;
+ label.maximumValueAngle = 180;
+
+ // When the label step size is 40 and the maximumValue 220, the following labels should be displayed:
+ // 0, 40, 80, 120, 160, 200
+ // In addition, the labels should be positioned according to how much of the angle range they actually use;
+ // since 240 is unable to be displayed, 200 should not be displayed on the last tickmark, but two tickmarks
+ // before it (the tickmarkStepSize is 10).
+ label.maximumValue = 220;
+ label.tickmarkStepSize = 10;
+ label.labelStepSize = 40;
+ compare(label.angleRange, 180);
+ compare(label.valueToAngle(0), 0);
+ compare(label.valueToAngle(220), 180);
+ compare(label.__panel.labelAngleFromIndex(1), 32.7272727);
+ compare(label.__panel.labelAngleFromIndex(0), 0);
+ compare(label.__panel.totalMinorTickmarkCount, 88);
+ compare(label.__panel.tickmarkValueFromIndex(0), 0);
+ compare(label.__panel.tickmarkValueFromIndex(label.tickmarkCount - 1), 220);
+ compare(label.__panel.tickmarkValueFromMinorIndex(0), 2);
+ compare(label.__panel.tickmarkValueFromMinorIndex(label.minorTickmarkCount - 1), 8);
+ compare(label.__panel.tickmarkValueFromMinorIndex(((label.tickmarkCount - 1) * label.minorTickmarkCount) - 1), 218);
+
+ var rotations = [
+ -0.5729577951308232,
+ 7.6088603866873585,
+ 15.79067856850554,
+ 23.972496750323725,
+ 32.1543149321419,
+ 40.33613311396008,
+ 48.51795129577827,
+ 56.69976947759645,
+ 64.88158765941463,
+ 73.06340584123282,
+ 81.245224023051,
+ 89.42704220486918,
+ 97.60886038668737,
+ 105.79067856850554,
+ 113.97249675032373,
+ 122.1543149321419,
+ 130.33613311396007,
+ 138.51795129577826,
+ 146.69976947759645,
+ 154.8815876594146,
+ 163.0634058412328,
+ 171.24522402305098,
+ 179.42704220486917
+ ];
+
+ // Check that the tickmarks have the correct transforms and hence are actually in the correct position.
+ for (var tickmarkIndex = 0; tickmarkIndex < label.__tickmarkCount; ++tickmarkIndex) {
+ var tickmark = TestUtils.findChild(label.__panel, "tickmark" + tickmarkIndex);
+ verify(tickmark);
+ compare(tickmark.transform[1].angle, rotations[tickmarkIndex]);
+ }
+
+ var minorRotations = [
+ 1.3498847387982247, 2.986248375161861,
+ 4.622612011525496, 6.258975647889133, 9.531702920616407,
+ 11.168066556980042, 12.804430193343679, 14.440793829707316,
+ 17.713521102434587, 19.349884738798224, 20.98624837516186,
+ 22.622612011525494, 25.89533928425277, 27.531702920616407,
+ 29.16806655698004, 30.80443019334368, 34.07715746607095,
+ 35.71352110243459, 37.349884738798224, 38.986248375161864,
+ 42.25897564788913, 43.89533928425277, 45.531702920616404,
+ 47.168066556980044, 50.44079382970732, 52.07715746607096,
+ 53.71352110243459, 55.34988473879823, 58.6226120115255,
+ 60.25897564788914, 61.89533928425277, 63.53170292061641,
+ 66.80443019334368, 68.4407938297073, 70.07715746607094,
+ 71.71352110243458, 74.98624837516186, 76.62261201152549,
+ 78.25897564788913, 79.89533928425277, 83.16806655698004,
+ 84.80443019334366, 86.4407938297073, 88.07715746607094,
+ 91.34988473879822, 92.98624837516185, 94.62261201152549,
+ 96.25897564788913, 99.53170292061641, 101.16806655698004,
+ 102.80443019334368, 104.44079382970732, 107.71352110243458,
+ 109.34988473879821, 110.98624837516185, 112.62261201152549,
+ 115.89533928425277, 117.5317029206164, 119.16806655698004,
+ 120.80443019334368, 124.07715746607094, 125.71352110243457,
+ 127.34988473879821, 128.98624837516184, 132.25897564788912,
+ 133.89533928425277, 135.5317029206164, 137.16806655698002,
+ 140.4407938297073, 142.07715746607096, 143.71352110243458,
+ 145.3498847387982, 148.6226120115255, 150.25897564788914,
+ 151.89533928425277, 153.5317029206164, 156.80443019334365,
+ 158.4407938297073, 160.07715746607093, 161.71352110243456,
+ 164.98624837516184, 166.6226120115255, 168.25897564788912,
+ 169.89533928425274, 173.16806655698002, 174.80443019334368,
+ 176.4407938297073, 178.07715746607093,
+ ];
+
+ for (var minorTickmarkIndex = 0; minorTickmarkIndex < label.__panel.totalMinorTickmarkCount; ++minorTickmarkIndex) {
+ var minorTickmark = TestUtils.findChild(label.__panel, "minorTickmark" + minorTickmarkIndex);
+ verify(minorTickmark);
+ compare(minorTickmark.transform[1].angle, minorRotations[minorTickmarkIndex]);
+ }
+
+ // 0, 10, 20... 100
+ label.labelStepSize = 10;
+ label.minimumValue = 0;
+ label.maximumValue = 100;
+ compare(label.tickmarkCount, 11);
+ compare(label.minorTickmarkCount, 4);
+ compare(label.labelCount, 11);
+ compare(label.angleRange, 180);
+ compare(label.valueToAngle(0), 0);
+ compare(label.valueToAngle(100), 180);
+ compare(label.__panel.labelAngleFromIndex(0), 0);
+ compare(label.__panel.labelAngleFromIndex(label.labelCount - 1), 180);
+ compare(label.__panel.totalMinorTickmarkCount, 40);
+ compare(label.__panel.tickmarkValueFromIndex(0), 0);
+ compare(label.__panel.tickmarkValueFromIndex(label.tickmarkCount - 1), 100);
+ compare(label.__panel.tickmarkValueFromMinorIndex(0), 2);
+ compare(label.__panel.tickmarkValueFromMinorIndex(label.minorTickmarkCount - 1), 8);
+ compare(label.__panel.tickmarkValueFromMinorIndex(((label.tickmarkCount - 1) * label.minorTickmarkCount) - 1), 98);
+
+ // 10, 20... 100
+ label.labelStepSize = 10;
+ label.minimumValue = 10;
+ label.maximumValue = 100;
+
+ compare(label.tickmarkCount, 10);
+ compare(label.minorTickmarkCount, 4);
+ compare(label.labelCount, 10);
+ compare(label.angleRange, 180);
+ compare(label.valueToAngle(10), 0);
+ compare(label.valueToAngle(100), 180);
+ compare(label.__panel.labelAngleFromIndex(0), 0);
+ compare(label.__panel.labelAngleFromIndex(label.labelCount - 1), 180);
+ compare(label.__panel.totalMinorTickmarkCount, 36);
+ compare(label.__panel.tickmarkValueFromIndex(0), 10);
+ compare(label.__panel.tickmarkValueFromIndex(label.tickmarkCount - 1), 100);
+ compare(label.__panel.tickmarkValueFromMinorIndex(0), 12);
+ compare(label.__panel.tickmarkValueFromMinorIndex(label.minorTickmarkCount - 1), 18);
+ compare(label.__panel.tickmarkValueFromMinorIndex(((label.tickmarkCount - 1) * label.minorTickmarkCount) - 1), 98);
+
+ // -10, 0, 10, 20... 100
+ label.labelStepSize = 10;
+ label.minimumValue = -10;
+ label.maximumValue = 100;
+ compare(label.tickmarkCount, 12);
+ compare(label.minorTickmarkCount, 4);
+ compare(label.labelCount, 12);
+ compare(label.angleRange, 180);
+ compare(label.valueToAngle(-10), 0);
+ compare(label.valueToAngle(100), 180);
+ compare(label.__panel.labelAngleFromIndex(0), 0);
+ compare(label.__panel.labelAngleFromIndex(label.labelCount - 1), 180);
+ compare(label.__panel.totalMinorTickmarkCount, 44);
+ compare(label.__panel.tickmarkValueFromIndex(0), -10);
+ compare(label.__panel.tickmarkValueFromIndex(label.tickmarkCount - 1), 100);
+ compare(label.__panel.tickmarkValueFromMinorIndex(0), -8);
+ compare(label.__panel.tickmarkValueFromMinorIndex(label.minorTickmarkCount - 1), -2);
+ compare(label.__panel.tickmarkValueFromMinorIndex(((label.tickmarkCount - 1) * label.minorTickmarkCount) - 1), 98);
+
+ // 0, 10, 20... 105
+ label.labelStepSize = 10;
+ label.minimumValue = 0;
+ label.maximumValue = 105;
+ label.minorTickmarkCount = 1;
+ compare(label.tickmarkCount, 11);
+ compare(label.labelCount, 11);
+ compare(label.angleRange, 180);
+ compare(label.valueToAngle(0), 0);
+ compare(label.valueToAngle(105), 180);
+ compare(label.__panel.labelAngleFromIndex(0), 0);
+ compare(label.__panel.labelAngleFromIndex(label.labelCount - 1), 171.42857142857142);
+ compare(label.__panel.tickmarkAngleFromIndex(label.tickmarkCount - 1), 171.42857142857142);
+ compare(label.__panel.minorTickmarkAngleFromIndex(label.minorTickmarkCount * (label.tickmarkCount - 1)), 180);
+ compare(label.__panel.totalMinorTickmarkCount, 11);
+ compare(label.__panel.tickmarkValueFromIndex(0), 0);
+ compare(label.__panel.tickmarkValueFromIndex(label.tickmarkCount - 1), 105);
+ compare(label.__panel.tickmarkValueFromMinorIndex(0), 5.25);
+ compare(label.__panel.tickmarkValueFromMinorIndex(((label.tickmarkCount - 1) * label.minorTickmarkCount) - 1), 99.75);
+
+ // 0, 10, 20... 101. Shouldn't show an extra minor tickmark, because each minor tickmark
+ // is 2 "units", and 1 (taken from 101) < 2.
+ label.labelStepSize = 10;
+ label.minimumValue = 0;
+ label.maximumValue = 101;
+ label.minorTickmarkCount = 4;
+ compare(label.valueToAngle(0), 0);
+ compare(label.valueToAngle(101), 180);
+ compare(label.tickmarkCount, 11);
+ compare(label.labelCount, 11);
+ compare(label.angleRange, 180);
+ compare(label.__panel.labelAngleFromIndex(0), 0);
+ compare(label.__panel.labelAngleFromIndex(label.labelCount - 1), 178.21782178217825);
+ compare(label.__panel.tickmarkAngleFromIndex(label.tickmarkCount - 1), 178.21782178217825);
+ compare(label.__panel.totalMinorTickmarkCount, 40);
+ compare(label.__panel.tickmarkValueFromIndex(0), 0);
+ compare(label.__panel.tickmarkValueFromIndex(label.tickmarkCount - 1), 101);
+ compare(label.__panel.tickmarkValueFromMinorIndex(0), 2.02);
+ compare(label.__panel.tickmarkValueFromMinorIndex(label.minorTickmarkCount - 1), 8.08);
+ compare(label.__panel.tickmarkValueFromMinorIndex(((label.tickmarkCount - 1) * label.minorTickmarkCount) - 1), 98.97999999999999);
+
+ // Test reversed labels.
+ label.minimumValueAngle = 270;
+ label.maximumValueAngle = 90;
+ label.minimumValue = 0;
+ label.maximumValue = 100;
+ compare(label.valueToAngle(0), 270);
+ compare(label.valueToAngle(50), 180);
+ compare(label.valueToAngle(100), 90);
+
+ label.minimumValueAngle = 270;
+ label.maximumValueAngle = 0;
+ compare(label.valueToAngle(0), 270);
+ compare(label.valueToAngle(100), 0);
+
+ label.minimumValueAngle = -90;
+ label.maximumValueAngle = 0;
+ compare(label.valueToAngle(0), -90);
+ compare(label.valueToAngle(100), 0);
+
+ label.minimumValueAngle = -180;
+ label.maximumValueAngle = 90;
+ compare(label.valueToAngle(0), -180);
+ compare(label.valueToAngle(100), 90);
+
+ label.minimumValueAngle = 215;
+ label.maximumValueAngle = 145;
+ label.minimumValue = 0;
+ label.maximumValue = 100;
+ compare(label.tickmarkCount, 11);
+ compare(label.labelCount, 11);
+ compare(label.angleRange, -70);
+ compare(label.__panel.totalMinorTickmarkCount, 40);
+ compare(label.__panel.tickmarkValueFromIndex(0), 0);
+ compare(label.__panel.tickmarkValueFromIndex(label.tickmarkCount - 1), 100);
+ compare(label.__panel.tickmarkValueFromMinorIndex(0), 2);
+ compare(label.__panel.tickmarkValueFromMinorIndex(label.minorTickmarkCount - 1), 8);
+ compare(label.__panel.tickmarkValueFromMinorIndex(((label.tickmarkCount - 1) * label.minorTickmarkCount) - 1), 98);
+ }
+
+ function test_invalidValues() {
+ // Shouldn't produce warnings.
+ label.labelStepSize = 0;
+ label.tickmarkStepSize = 0;
+ }
+}
diff --git a/tests/auto/extras/data/tst_common.qml b/tests/auto/extras/data/tst_common.qml
new file mode 100644
index 00000000..1a7fe990
--- /dev/null
+++ b/tests/auto/extras/data/tst_common.qml
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.1
+
+TestCase {
+ id: testCase
+ name: "Tests_Common"
+ visible: windowShown
+ when: windowShown
+ width: 400
+ height: 400
+
+ property var control
+
+ function init_data() {
+ return [
+ { tag: "CircularGauge" },
+ { tag: "DelayButton" },
+ { tag: "Dial" },
+ { tag: "Gauge" },
+ { tag: "PieMenu", qml: "import QtQuick.Controls 1.1; import QtQuick.Extras 1.3;"
+ + "PieMenu { visible: true; MenuItem { text: 'foo' } }"},
+ { tag: "StatusIndicator" },
+ { tag: "ToggleButton" },
+ { tag: "Tumbler", qml: "import QtQuick.Extras 1.3; Tumbler { TumblerColumn { model: 10 } }" }
+ ];
+ }
+
+ function cleanup() {
+ control.destroy();
+ }
+
+ function test_resize(data) {
+ var qml = data.qml ? data.qml : "import QtQuick.Extras 1.3; " + data.tag + " { }";
+ control = Qt.createQmlObject(qml, testCase, "");
+
+ var resizeAnimation = Qt.createQmlObject("import QtQuick 2.2; NumberAnimation {}", testCase, "");
+ resizeAnimation.target = control;
+ resizeAnimation.properties = "width,height";
+ resizeAnimation.duration = 100;
+ resizeAnimation.to = 0;
+ resizeAnimation.start();
+ // Shouldn't get any warnings.
+ tryCompare(resizeAnimation, "running", false);
+ resizeAnimation.destroy();
+ }
+}
diff --git a/tests/auto/extras/data/tst_delaybutton.qml b/tests/auto/extras/data/tst_delaybutton.qml
new file mode 100644
index 00000000..59e602a1
--- /dev/null
+++ b/tests/auto/extras/data/tst_delaybutton.qml
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.1
+
+TestCase {
+ id: testcase
+ name: "Tests_DelayButton"
+ visible: windowShown
+ when: windowShown
+ width: 400
+ height: 400
+
+ function test_instance() {
+ var button = Qt.createQmlObject('import QtQuick.Extras 1.3; DelayButton { }', testcase, '')
+ verify (button, "DelayButton: failed to create an instance")
+ verify(button.__style)
+ verify(!button.checked)
+ verify(!button.pressed)
+ button.destroy()
+ }
+
+ SignalSpy {
+ id: activationSpy
+ signalName: "activated"
+ }
+
+ function test_activation_data() {
+ return [
+ { tag: "delayed", delay: 1 },
+ { tag: "immediate", delay: 0 },
+ { tag: "negative", delay: -1 }
+ ]
+ }
+
+ function test_activation(data) {
+ var button = Qt.createQmlObject('import QtQuick.Extras 1.3; DelayButton { }', testcase, '')
+ verify (button, "DelayButton: failed to create an instance")
+ button.delay = data.delay
+
+ activationSpy.clear()
+ activationSpy.target = button
+ verify(activationSpy.valid)
+
+ // press and hold to activate
+ mousePress(button, button.width / 2, button.height / 2)
+ verify(button.pressed)
+ var immediate = data.delay <= 0
+ if (!immediate)
+ activationSpy.wait()
+ compare(activationSpy.count, 1)
+
+ // release
+ mouseRelease(button, button.width / 2, button.height / 2)
+ verify(!button.pressed)
+ compare(activationSpy.count, 1)
+
+ button.destroy()
+ }
+
+ SignalSpy {
+ id: progressSpy
+ signalName: "progressChanged"
+ }
+
+ function test_progress() {
+ var button = Qt.createQmlObject('import QtQuick.Extras 1.3; DelayButton { delay: 1 }', testcase, '')
+ verify (button, "DelayButton: failed to create an instance")
+
+ progressSpy.target = button
+ verify(progressSpy.valid)
+
+ compare(button.progress, 0.0)
+ mousePress(button, button.width / 2, button.height / 2)
+ tryCompare(button, "progress", 1.0)
+ verify(progressSpy.count > 0)
+
+ button.destroy()
+ }
+
+ SignalSpy {
+ id: checkSpy
+ signalName: "checkedChanged"
+ }
+
+ function test_checked_data() {
+ return [
+ { tag: "delayed", delay: 1 },
+ { tag: "immediate", delay: 0 },
+ { tag: "negative", delay: -1 }
+ ]
+ }
+
+ function test_checked(data) {
+ var button = Qt.createQmlObject('import QtQuick.Extras 1.3; DelayButton { }', testcase, '')
+ verify (button, "DelayButton: failed to create an instance")
+ button.delay = data.delay
+
+ var checkCount = 0
+
+ checkSpy.clear()
+ checkSpy.target = button
+ verify(checkSpy.valid)
+ verify(!button.checked)
+
+ // press and hold to check
+ mousePress(button, button.width / 2, button.height / 2)
+ verify(button.pressed)
+ var immediate = data.delay <= 0
+ compare(button.checked, immediate)
+ if (!immediate)
+ tryCompare(button, "checked", true)
+ compare(checkSpy.count, ++checkCount)
+
+ // release
+ mouseRelease(button, button.width / 2, button.height / 2)
+ verify(!button.pressed)
+ verify(button.checked)
+ compare(checkSpy.count, checkCount)
+
+ // press to uncheck immediately
+ mousePress(button, button.width / 2, button.height / 2)
+ verify(button.pressed)
+ verify(!button.checked)
+ compare(checkSpy.count, ++checkCount)
+
+ // release
+ mouseRelease(button, button.width / 2, button.height / 2)
+ verify(!button.pressed)
+ verify(!button.checked)
+ compare(checkSpy.count, checkCount)
+
+ button.destroy()
+ }
+
+ function test_programmaticCheck() {
+ var button = Qt.createQmlObject("import QtQuick.Extras 1.3; DelayButton {}", testcase, "");
+ verify(button, "DelayButton: failed to create an instance");
+
+ checkSpy.clear();
+ checkSpy.target = button;
+ verify(!button.checked);
+
+ button.checked = true;
+ compare(button.progress, 1);
+
+ button.checked = false;
+ compare(button.progress, 0);
+
+ button.destroy();
+ }
+
+ function test_largeText() {
+ // Should be no binding loop warnings.
+ var button = Qt.createQmlObject("import QtQuick.Extras 1.3; DelayButton { "
+ + "anchors.centerIn: parent; text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }", testcase, "");
+ verify(button, "DelayButton: failed to create an instance");
+ button.destroy();
+ }
+}
diff --git a/tests/auto/extras/data/tst_dial.qml b/tests/auto/extras/data/tst_dial.qml
new file mode 100644
index 00000000..02e6eaa3
--- /dev/null
+++ b/tests/auto/extras/data/tst_dial.qml
@@ -0,0 +1,367 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.1
+import "TestUtils.js" as TestUtils
+
+TestCase {
+ id: testcase
+ name: "Tests_Dial"
+ visible: windowShown
+ when: windowShown
+ width: 400
+ height: 400
+
+ function test_instance() {
+ var dial = Qt.createQmlObject('import QtQuick.Extras 1.3; Dial { }', testcase, '');
+ verify (dial, "Dial: failed to create an instance")
+ verify(dial.__style)
+ compare(dial.value, 0.0)
+ compare(dial.minimumValue, 0.0)
+ compare(dial.maximumValue, 1.0)
+ compare(dial.stepSize, 0.0)
+ verify(!dial.wrap)
+ verify(!dial.activeFocusOnPress)
+ verify(!dial.containsMouse)
+ verify(!dial.pressed)
+ dial.destroy()
+ }
+
+ function test_minimumValue() {
+ var dial = Qt.createQmlObject('import QtQuick.Extras 1.3; Dial { }', testcase, '');
+ verify (dial, "Dial: failed to create an instance")
+ dial.minimumValue = 5
+ dial.maximumValue = 10
+ dial.value = 2
+ compare(dial.minimumValue, 5)
+ compare(dial.value, 5)
+ dial.destroy()
+ }
+
+ function test_maximumValue() {
+ var dial = Qt.createQmlObject('import QtQuick.Extras 1.3; Dial { }', testcase, '');
+ verify (dial, "Dial: failed to create an instance")
+ dial.minimumValue = 5
+ dial.maximumValue = 10
+ dial.value = 15
+ compare(dial.maximumValue, 10)
+ compare(dial.value, 10)
+ dial.destroy()
+ }
+
+ function test_activeFocusOnPress(){
+ var scope = Qt.createQmlObject('import QtQuick 2.2; FocusScope { focus: false }', testcase, '')
+ verify(!scope.activeFocus)
+
+ var dial = Qt.createQmlObject('import QtQuick.Extras 1.3; Dial { }', scope, '')
+ verify (dial, "Dial: failed to create an instance")
+ verify(!dial.activeFocusOnPress)
+ verify(!dial.activeFocus)
+ verify(!scope.activeFocus)
+ mousePress(dial, dial.width / 2, dial.height / 2)
+ verify(!dial.activeFocus)
+ verify(!scope.activeFocus)
+ mouseRelease(dial, dial.width / 2, dial.height / 2)
+ verify(!dial.activeFocus)
+ verify(!scope.activeFocus)
+
+ dial.activeFocusOnPress = true
+ verify(dial.activeFocusOnPress)
+ verify(!dial.activeFocus)
+ verify(!scope.activeFocus)
+ mousePress(dial, dial.width / 2, dial.height / 2)
+ verify(dial.activeFocus)
+ verify(scope.activeFocus)
+ mouseRelease(dial, dial.width / 2, dial.height / 2)
+ verify(dial.activeFocus)
+ verify(scope.activeFocus)
+
+ dial.destroy()
+ }
+
+ SignalSpy {
+ id: pressSpy
+ signalName: "pressedChanged"
+ }
+
+ function test_pressed() {
+ var dial = Qt.createQmlObject('import QtQuick.Extras 1.3; Dial { }', testcase, '')
+ verify (dial, "Dial: failed to create an instance")
+
+ pressSpy.target = dial
+ verify(pressSpy.valid)
+ verify(!dial.pressed)
+
+ mousePress(dial, dial.width / 2, dial.height / 2)
+ verify(dial.pressed)
+ compare(pressSpy.count, 1)
+
+ mouseRelease(dial, dial.width / 2, dial.height / 2)
+ verify(!dial.pressed)
+ compare(pressSpy.count, 2)
+
+ dial.destroy()
+ }
+
+ SignalSpy {
+ id: hoverSpy
+ signalName: "hoveredChanged"
+ }
+
+ function test_hovered() {
+ var dial = Qt.createQmlObject('import QtQuick.Extras 1.3; Dial { }', testcase, '')
+ verify (dial, "Dial: failed to create an instance")
+
+ hoverSpy.target = dial
+ verify(hoverSpy.valid)
+ verify(!dial.hovered)
+
+ mouseMove(dial, dial.width / 2, dial.height / 2)
+ verify(dial.hovered)
+ compare(hoverSpy.count, 1)
+
+ mouseMove(dial, dial.width * 2, dial.height * 2)
+ verify(!dial.hovered)
+ compare(hoverSpy.count, 2)
+
+ dial.destroy()
+ }
+
+ SignalSpy {
+ id: valueSpy
+ signalName: "valueChanged"
+ }
+
+ function test_dragging_data() {
+ return [
+ { tag: "default", min: 0, max: 1, leftValue: 0.20, topValue: 0.5, rightValue: 0.8, bottomValue: 1.0 },
+ { tag: "scaled2", min: 0, max: 2, leftValue: 0.4, topValue: 1.0, rightValue: 1.6, bottomValue: 2.0 },
+ { tag: "scaled1", min: -1, max: 0, leftValue: -0.8, topValue: -0.5, rightValue: -0.2, bottomValue: 0.0 }
+ ]
+ }
+
+ function test_dragging(data) {
+ var dial = Qt.createQmlObject('import QtQuick.Extras 1.3; Dial { }', testcase, '')
+ verify(dial, "Dial: failed to create an instance")
+ dial.minimumValue = data.min
+ dial.maximumValue = data.max
+
+ valueSpy.target = dial
+ verify(valueSpy.valid)
+
+ // drag to the left
+ mouseDrag(dial, dial.width / 2, dial.height / 2, -dial.width / 2, 0, Qt.LeftButton)
+ fuzzyCompare(dial.value, data.leftValue, 0.1)
+ verify(valueSpy.count > 0)
+ valueSpy.clear()
+
+ // drag to the top
+ mouseDrag(dial, dial.width / 2, dial.height / 2, 0, -dial.height / 2, Qt.LeftButton)
+ fuzzyCompare(dial.value, data.topValue, 0.1)
+ verify(valueSpy.count > 0)
+ valueSpy.clear()
+
+ // drag to the right
+ mouseDrag(dial, dial.width / 2, dial.height / 2, dial.width / 2, 0, Qt.LeftButton)
+ fuzzyCompare(dial.value, data.rightValue, 0.1)
+ verify(valueSpy.count > 0)
+ valueSpy.clear()
+
+ // drag to the bottom
+ mouseDrag(dial, dial.width / 2, dial.height / 2, 0, dial.height / 2, Qt.LeftButton)
+ fuzzyCompare(dial.value, data.bottomValue, 0.1)
+ verify(valueSpy.count > 0)
+ valueSpy.clear()
+
+ dial.destroy()
+ }
+
+ function test_outerRadius() {
+ var dial = Qt.createQmlObject("import QtQuick.Extras 1.3; Dial { }", testcase, "");
+ verify(dial, "Dial: failed to create an instance");
+ // Implicit width and height are identical.
+ compare(dial.__style.outerRadius, dial.width / 2);
+
+ dial.width = 100;
+ dial.height = 250;
+ compare(dial.__style.outerRadius, dial.width / 2);
+
+ dial.width = 250;
+ dial.height = 100;
+ compare(dial.__style.outerRadius, dial.height / 2);
+
+ dial.destroy();
+ }
+
+ property Component tickmark: Rectangle {
+ objectName: "tickmark" + styleData.index
+ implicitWidth: 3
+ implicitHeight: 8
+ color: "#c8c8c8"
+ }
+
+ property Component minorTickmark: Rectangle {
+ objectName: "minorTickmark" + styleData.index
+ implicitWidth: 2
+ implicitHeight: 6
+ color: "#c8c8c8"
+ }
+
+ property Component tickmarkLabel: Text {
+ objectName: "tickmarkLabel" + styleData.index
+ text: styleData.value
+ color: "#c8c8c8"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ function test_tickmarksVisible() {
+ var dial = Qt.createQmlObject("import QtQuick.Extras 1.3; Dial { }", testcase, "");
+ verify(dial, "Dial: failed to create an instance");
+
+ dial.__style.minorTickmarkCount = 4;
+ dial.__style.tickmark = tickmark;
+ dial.__style.minorTickmark = minorTickmark;
+ dial.__style.tickmarkLabel = tickmarkLabel;
+ verify(TestUtils.findChild(dial, "tickmark0"));
+ verify(TestUtils.findChild(dial, "minorTickmark0"));
+ verify(TestUtils.findChild(dial, "tickmarkLabel0"));
+
+ dial.tickmarksVisible = false;
+ verify(!TestUtils.findChild(dial, "tickmark0"));
+ verify(!TestUtils.findChild(dial, "minorTickmark0"));
+ verify(!TestUtils.findChild(dial, "tickmarkLabel0"));
+
+ dial.destroy();
+ }
+
+ property Component focusTest: Component {
+ FocusScope {
+ signal receivedKeyPress
+
+ Component.onCompleted: forceActiveFocus()
+ anchors.fill: parent
+ Keys.onPressed: receivedKeyPress()
+ }
+ }
+
+ SignalSpy {
+ id: parentEventSpy
+ }
+
+ function test_keyboardNavigation() {
+ var focusScope = focusTest.createObject(testcase);
+ verify(focusScope);
+
+ // Tests that we've accepted events that we're interested in.
+ parentEventSpy.target = focusScope;
+ parentEventSpy.signalName = "receivedKeyPress";
+
+ var dial = Qt.createQmlObject("import QtQuick.Extras 1.3; Dial { }", focusScope, "");
+ verify(dial, "Dial: failed to create an instance");
+ compare(dial.activeFocusOnTab, true);
+ compare(dial.value, 0);
+
+ dial.focus = true;
+ compare(dial.activeFocus, true);
+ dial.stepSize = 0.1;
+
+ keyClick(Qt.Key_Left);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, 0);
+
+ var keyPairs = [[Qt.Key_Left, Qt.Key_Right], [Qt.Key_Down, Qt.Key_Up]];
+ for (var keyPairIndex = 0; keyPairIndex < 2; ++keyPairIndex) {
+ for (var i = 1; i <= 10; ++i) {
+ keyClick(keyPairs[keyPairIndex][1]);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.stepSize * i);
+ }
+
+ compare(dial.value, dial.maximumValue);
+
+ for (i = 10; i > 0; --i) {
+ keyClick(keyPairs[keyPairIndex][0]);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.stepSize * (i - 1));
+ }
+ }
+
+ compare(dial.value, dial.minimumValue);
+
+ keyClick(Qt.Key_Home);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.minimumValue);
+
+ keyClick(Qt.Key_End);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.maximumValue);
+
+ keyClick(Qt.Key_End);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.maximumValue);
+
+ keyClick(Qt.Key_Home);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.minimumValue);
+
+ focusScope.destroy();
+ }
+
+ function test_dragToSet() {
+ var dial = Qt.createQmlObject("import QtQuick.Extras 1.3; Dial { }", testcase, "");
+ verify(dial, "Dial: failed to create an instance");
+
+ dial.__style.__dragToSet = false;
+
+ compare(dial.value, 0);
+ var lastValue = dial.value;
+
+ mousePress(dial, dial.width, dial.height / 2, Qt.LeftButton);
+ verify(dial.value !== lastValue);
+ lastValue = dial.value;
+ mouseRelease(dial, dial.width, dial.height / 2, Qt.LeftButton);
+
+ dial.__style.__dragToSet = true;
+
+ mousePress(dial, dial.width / 4, dial.height / 2, Qt.LeftButton);
+ verify(dial.value === lastValue);
+ mouseRelease(dial, dial.width / 4, dial.height / 2, Qt.LeftButton);
+
+ dial.destroy();
+ }
+}
diff --git a/tests/auto/extras/data/tst_gauge.qml b/tests/auto/extras/data/tst_gauge.qml
new file mode 100644
index 00000000..08fdf53f
--- /dev/null
+++ b/tests/auto/extras/data/tst_gauge.qml
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.2
+import QtQuick.Extras.Styles 1.3
+import "TestUtils.js" as TestUtils
+
+TestCase {
+ id: testcase
+ name: "Tests_Gauge"
+ visible: windowShown
+ when: windowShown
+ width: 60
+ height: 200
+
+ property var gauge
+
+ Rectangle {
+ anchors.fill: parent
+ color: "black"
+ }
+
+ property Component zeroTickmarkWidthGaugeStyle: GaugeStyle {
+ tickmark: Item {
+ implicitWidth: 0
+ implicitHeight: 1
+ }
+ }
+
+ property Component largeGaugeStyle: GaugeStyle {
+ tickmark: Item {
+ implicitWidth: 8
+ implicitHeight: 4
+ }
+ }
+
+ function init() {
+ gauge = Qt.createQmlObject("import QtQuick.Extras 1.3; Gauge { }", testcase, "");
+ verify(gauge, "Gauge: failed to create an instance");
+ verify(gauge.__style);
+ }
+
+ function cleanup() {
+ if (gauge)
+ gauge.destroy();
+ }
+
+ function test_assignStyle() {
+ gauge.style = zeroTickmarkWidthGaugeStyle;
+ compare(gauge.__panel.tickmarkLabelBoundsWidth, gauge.__hiddenText.width);
+ compare(gauge.__panel.tickmarkLength, 0);
+ compare(gauge.__panel.tickmarkWidth, 1);
+
+ // Ensure that our internals are correct after changing to smaller tickmarks.
+ gauge.style = largeGaugeStyle;
+ compare(gauge.__panel.tickmarkLabelBoundsWidth, gauge.__hiddenText.width + 8);
+ compare(gauge.__panel.tickmarkLength, 8);
+ compare(gauge.__panel.tickmarkWidth, 4);
+
+ gauge.style = zeroTickmarkWidthGaugeStyle;
+ compare(gauge.__panel.tickmarkLabelBoundsWidth, gauge.__hiddenText.width);
+ compare(gauge.__panel.tickmarkLength, 0);
+ compare(gauge.__panel.tickmarkWidth, 1);
+ }
+
+ property Component centeredTickmarkStyle: GaugeStyle {
+ tickmark: Item {
+ implicitWidth: 10
+ implicitHeight: 4
+
+ Rectangle {
+ // We must use this as the actual tickmark, because the parent is just spacing.
+ objectName: "tickmark" + styleData.index
+ x: control.tickmarkAlignment === Qt.AlignLeft
+ || control.tickmarkAlignment === Qt.AlignTop ? parent.implicitWidth : -28
+ width: 28
+ height: parent.height
+ }
+ }
+
+ valueBar: Rectangle {
+ objectName: "valueBar"
+ implicitWidth: 28
+
+ Text {
+ text: parent.width
+ }
+ }
+ }
+
+ function test_valueBarPosition_data() {
+ return [
+ { tag: "AlignLeft", orientation: Qt.Vertical, tickmarkAlignment: Qt.AlignLeft },
+ { tag: "AlignRight", orientation: Qt.Vertical, tickmarkAlignment: Qt.AlignRight },
+ // TODO: these combinations work, but the tests need to account for them (perhaps due to rotation)
+// { tag: "AlignTop", orientation: Qt.Horizontal, tickmarkAlignment: Qt.AlignTop },
+// { tag: "AlignBottom", orientation: Qt.Horizontal, tickmarkAlignment: Qt.AlignBottom },
+ ];
+ }
+
+ function test_valueBarPosition(data) {
+ gauge.orientation = data.orientation;
+ gauge.tickmarkAlignment = data.tickmarkAlignment;
+ gauge.value = 1.8;
+ gauge.maximumValue = 4;
+ gauge.tickmarkStepSize = 1;
+ gauge.minorTickmarkCount = 1;
+ gauge.width = 50;
+
+ gauge.style = centeredTickmarkStyle;
+
+ var valueBar = TestUtils.findChild(gauge, "valueBar");
+ var firstTickmark = TestUtils.findChild(gauge, "tickmark0");
+ compare(testcase.mapFromItem(valueBar, 0, 0).x, testcase.mapFromItem(firstTickmark, 0, 0).x);
+ }
+
+ property Component tickmarkValueStyle: GaugeStyle {
+ tickmark: Rectangle {
+ implicitWidth: 10
+ implicitHeight: 4
+ objectName: "tickmark" + styleData.index
+
+ readonly property real value: styleData.value
+ readonly property real valuePosition: styleData.valuePosition
+ }
+
+ minorTickmark: Rectangle {
+ implicitWidth: 5
+ implicitHeight: 2
+ objectName: "minorTickmark" + styleData.index
+
+ readonly property real value: styleData.value
+ readonly property real valuePosition: styleData.valuePosition
+ }
+ }
+
+ function test_gaugeTickmarkValues_data() {
+ var data = [
+ {
+ tickmarkStepSize: 1, minorTickmarkCount: 4, minimumValue: 0, maximumValue: 5,
+ expectedTickmarkValues: [0, 1, 2, 3, 4, 5],
+ expectedMinorTickmarkValues: [0.2, 0.4, 0.6, 0.8, 1.2, 1.4, 1.6, 1.8, 2.2, 2.4,
+ 2.6, 2.8, 3.2, 3.4, 3.6, 3.8, 4.2, 4.4, 4.6, 4.8],
+ expectedTickmarkValuePositions: [0, 19, 38, 58, 77, 96],
+ expectedMinorTickmarkValuePositions: [6, 9, 12, 15, 25, 28, 31, 34, 44, 47, 51, 54, 64, 67, 70, 73, 83, 86, 89, 92]
+ },
+ {
+ tickmarkStepSize: 1, minorTickmarkCount: 1, minimumValue: 0, maximumValue: 5,
+ expectedTickmarkValues: [0, 1, 2, 3, 4, 5],
+ expectedMinorTickmarkValues: [0.5, 1.5, 2.5, 3.5, 4.5],
+ expectedTickmarkValuePositions: [0, 19, 38, 58, 77, 96],
+ expectedMinorTickmarkValuePositions: [11, 30, 49, 68, 87]
+ },
+ {
+ tickmarkStepSize: 1, minorTickmarkCount: 1, minimumValue: -1, maximumValue: 1,
+ expectedTickmarkValues: [-1, 0, 1],
+ expectedMinorTickmarkValues: [-0.5, 0.5],
+ expectedTickmarkValuePositions: [0, 48, 96],
+ expectedMinorTickmarkValuePositions: [25, 73]
+ },
+ {
+ tickmarkStepSize: 1, minorTickmarkCount: 1, minimumValue: -2, maximumValue: -1,
+ expectedTickmarkValues: [-2, -1],
+ expectedMinorTickmarkValues: [-1.5],
+ expectedTickmarkValuePositions: [0, 96],
+ expectedMinorTickmarkValuePositions: [49]
+ },
+ {
+ tickmarkStepSize: 0.5, minorTickmarkCount: 1, minimumValue: 1, maximumValue: 2,
+ expectedTickmarkValues: [1, 1.5, 2],
+ expectedMinorTickmarkValues: [1.25, 1.75],
+ expectedTickmarkValuePositions: [0, 48, 96],
+ expectedMinorTickmarkValuePositions: [25, 73]
+ },
+ {
+ tickmarkStepSize: 0.5, minorTickmarkCount: 1, minimumValue: -0.5, maximumValue: 0.5,
+ expectedTickmarkValues: [-0.5, 0, 0.5],
+ expectedMinorTickmarkValues: [-0.25, 0.25],
+ expectedTickmarkValuePositions: [0, 48, 96],
+ expectedMinorTickmarkValuePositions: [25, 73]
+ }
+ ];
+
+ for (var i = 0; i < data.length; ++i) {
+ data[i].tag = "tickmarkStepSize=" + data[i].tickmarkStepSize
+ + " minorTickmarkCount=" + data[i].minorTickmarkCount
+ + " minimumValue=" + data[i].minimumValue
+ + " maximumValue=" + data[i].maximumValue;
+ }
+
+ return data;
+ }
+
+ function gaugeHeightFor100PixelHighValueBar(gauge) {
+ return 100 + gauge.__panel.tickmarkOffset * 2 - 2;
+ }
+
+ function test_gaugeTickmarkValues(data) {
+ gauge.minimumValue = data.minimumValue;
+ gauge.maximumValue = data.maximumValue;
+ gauge.minorTickmarkCount = data.minorTickmarkCount;
+ gauge.tickmarkStepSize = data.tickmarkStepSize;
+ gauge.style = tickmarkValueStyle;
+
+ // To make it easier on us, we expect the max length of the value bar to be 100.
+ gauge.height = gaugeHeightFor100PixelHighValueBar(gauge);
+ compare(gauge.__panel.barLength, 100);
+
+ // Give stuff time to re-layout after the new control height, etc.,
+ // otherwise we'll be comparing against incorrect pixel positions.
+ wait(0);
+
+ for (var tickmarkIndex = 0; tickmarkIndex < data.expectedTickmarkValues.length; ++tickmarkIndex) {
+ var tickmark = TestUtils.findChild(gauge, "tickmark" + tickmarkIndex);
+ compare(tickmark.value, data.expectedTickmarkValues[tickmarkIndex],
+ "Value of tickmark at index " + tickmarkIndex + " is " + data.expectedTickmarkValues[tickmarkIndex]);
+
+ var expectedValuePos = data.expectedTickmarkValuePositions[tickmarkIndex];
+ compare(tickmark.valuePosition, expectedValuePos,
+ "Value position of tickmark at index " + tickmarkIndex + " is " + expectedValuePos);
+ }
+
+ for (var minorTickmarkIndex = 0; minorTickmarkIndex < data.expectedMinorTickmarkValues.length; ++minorTickmarkIndex) {
+ var minorTickmark = TestUtils.findChild(gauge, "minorTickmark" + minorTickmarkIndex);
+ compare(minorTickmark.value, data.expectedMinorTickmarkValues[minorTickmarkIndex],
+ "Value of minor tickmark at index " + minorTickmarkIndex + " is " + data.expectedMinorTickmarkValues[minorTickmarkIndex]);
+
+ expectedValuePos = data.expectedMinorTickmarkValuePositions[minorTickmarkIndex];
+ compare(minorTickmark.valuePosition, expectedValuePos,
+ "Value position of minor tickmark at index " + minorTickmarkIndex + " is " + expectedValuePos);
+ }
+ }
+
+ function test_formatValue() {
+ var formatValueCalled = false;
+ gauge.formatValue = function(value) { formatValueCalled = true; return value; }
+ verify(formatValueCalled);
+ }
+
+ function test_valuePosition() {
+ gauge.minimumValue = 0;
+ gauge.maximumValue = 100;
+ gauge.tickmarkStepSize = 10;
+
+ // To make it easier on us, we expect the max length of the value bar to be 100.
+ gauge.height = gaugeHeightFor100PixelHighValueBar(gauge);
+ compare(gauge.__panel.barLength, 100);
+
+ for (var i = gauge.minimumValue; i < gauge.maximumValue; ++i) {
+ compare(gauge.__style.valuePosition, 0);
+ }
+ }
+}
diff --git a/tests/auto/extras/data/tst_picture.qml b/tests/auto/extras/data/tst_picture.qml
new file mode 100644
index 00000000..3c3c4391
--- /dev/null
+++ b/tests/auto/extras/data/tst_picture.qml
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.4
+import QtQuick.Extras 1.3
+import QtQuick.Extras.Private 1.0
+
+TestCase {
+ id: testCase
+ name: "Tests_Picture"
+ visible: windowShown
+ when: windowShown
+ width: 400
+ height: 400
+
+ property var picture
+
+ /*
+ picture.dat was generated with a 10 width pen, and:
+
+ painter.drawEllipse(painter.pen().width() / 2, painter.pen().width() / 2,
+ 100 - painter.pen().width(), 100 - painter.pen().width());
+ */
+ property color pictureDotDatDefaultColor: Qt.rgba(0, 0, 0, 1)
+ property size pictureDotDatImplicitSize: Qt.size(100, 100)
+
+ function cleanup() {
+ if (picture)
+ picture.destroy();
+ }
+
+ function test_instance() {
+ picture = Qt.createQmlObject("import QtQuick.Extras 1.3; Picture { }", testCase, "");
+ verify(picture, "Picture: failed to create an instance");
+ }
+
+ function test_source_data() {
+ return [
+ {
+ tag: "picture.dat",
+ implicitSize: pictureDotDatImplicitSize,
+ pixels: [
+ { x: 0, y: 0, color: Qt.rgba(1, 1, 1, 1) },
+ { x: 18, y: 18, color: pictureDotDatDefaultColor },
+ { x: 50, y: 50, color: Qt.rgba(1, 1, 1, 1) }
+ ]
+ }
+ ];
+ }
+
+ function test_source(data) {
+ picture = Qt.createQmlObject("import QtQuick.Extras 1.3; Picture {}", testCase, "");
+ verify(picture, "Picture: failed to create an instance");
+ picture.source = data.tag;
+ picture.width = data.implicitSize.width;
+ picture.height = data.implicitSize.height;
+ waitForRendering(picture);
+
+ var pictureImage = grabImage(picture);
+
+ for (var i = 0; i < data.pixels.length; ++i) {
+ var pixel = data.pixels[i];
+ // TODO: use compare when QTBUG-34878 is fixed
+ verify(Qt.colorEqual(pictureImage.pixel(pixel.x, pixel.y), pixel.color),
+ "pixel " + pictureImage.pixel(pixel.x, pixel.y) + " at "
+ + pixel.x + "," + pixel.y + " isn't equal to " + pixel.color);
+ }
+ }
+
+ function test_color_data() {
+ return [
+ { tag: "undefined", color: undefined, expectedColor: pictureDotDatDefaultColor },
+ { tag: "not a valid color", color: "not a valid color", expectedColor: pictureDotDatDefaultColor },
+ { tag: "red", color: "red", expectedColor: Qt.rgba(1, 0, 0, 1) },
+ { tag: "black", color: "black", expectedColor: Qt.rgba(0, 0, 0, 1) }
+ ]
+ }
+
+ function test_color(data) {
+ picture = Qt.createQmlObject("import QtQuick.Extras 1.3; Picture {}", testCase, "");
+ verify(picture, "Picture: failed to create an instance");
+
+ picture.width = pictureDotDatImplicitSize.width;
+ picture.height = pictureDotDatImplicitSize.height;
+ picture.source = "picture.dat";
+ picture.color = data.color;
+ waitForRendering(picture);
+
+ var pictureImage = grabImage(picture);
+
+ verify(Qt.colorEqual(pictureImage.pixel(0, 0), Qt.rgba(1, 1, 1, 1)));
+ verify(Qt.colorEqual(pictureImage.pixel(17, 17), data.expectedColor));
+ verify(Qt.colorEqual(pictureImage.pixel(picture.width / 2, picture.height / 2), Qt.rgba(1, 1, 1, 1)));
+ }
+
+ FontMetrics {
+ id: fontMetrics
+ }
+
+ function test_size() {
+ picture = Qt.createQmlObject("import QtQuick.Extras 1.3; Picture {}", testCase, "");
+ verify(picture, "Picture: failed to create an instance");
+
+ compare(picture.implicitWidth, fontMetrics.height * 4);
+ compare(picture.implicitHeight, fontMetrics.height * 4);
+ compare(picture.width, picture.implicitWidth);
+ compare(picture.height, picture.implicitHeight);
+
+ picture.source = "picture.dat";
+ compare(picture.implicitWidth, pictureDotDatImplicitSize.width);
+ compare(picture.implicitHeight, pictureDotDatImplicitSize.height);
+ compare(picture.width, picture.implicitWidth);
+ compare(picture.height, picture.implicitHeight);
+ }
+}
diff --git a/tests/auto/extras/data/tst_piemenu.qml b/tests/auto/extras/data/tst_piemenu.qml
new file mode 100644
index 00000000..fa109a1a
--- /dev/null
+++ b/tests/auto/extras/data/tst_piemenu.qml
@@ -0,0 +1,836 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.1
+import QtQuick.Controls 1.1
+import QtQuick.Extras 1.3
+import QtQuick.Extras.Private 1.0
+import QtQuick.Extras.Private.CppUtils 1.0
+
+// Have to have Item here otherwise mouse clicks don't work.
+Item {
+ id: container
+ width: 400
+ height: 400
+
+ TestCase {
+ id: testcase
+ name: "Tests_PieMenu"
+ when: windowShown
+ anchors.fill: parent
+
+ readonly property real menuWidth: 200
+ readonly property real menuHeight: 200
+
+ // The root item for each test, if one exists.
+ property Item root
+ // The pie menu for each test, if no root is created.
+ property Item pieMenu
+
+ SignalSpy {
+ id: currentIndexSignalSpy
+ }
+
+ SignalSpy {
+ id: actionSignalSpy
+ }
+
+ SignalSpy {
+ id: selectedAngleChangedSpy
+ }
+
+ function cleanup() {
+ currentIndexSignalSpy.clear();
+ actionSignalSpy.clear();
+ selectedAngleChangedSpy.clear();
+
+ if (root)
+ root.destroy();
+ if (pieMenu)
+ pieMenu.destroy();
+ }
+
+ function mouseButtonToString(button) {
+ return button === Qt.LeftButton ? "Qt.LeftButton" : "Qt.RightButton";
+ }
+
+ function triggerModeToString(triggerMode) {
+ return triggerMode === TriggerMode.TriggerOnPress ? "TriggerOnPress"
+ : (triggerMode === TriggerMode.TriggerOnRelease ? "TriggerOnRelease" : "TriggerOnClick");
+ }
+
+ function test_instance() {
+ var pieMenu = Qt.createQmlObject("import QtQuick.Extras 1.3; PieMenu { }", container, "");
+ verify(pieMenu, "PieMenu: failed to create an instance");
+ verify(pieMenu.__style);
+ compare(pieMenu.triggerMode, TriggerMode.TriggerOnClick);
+ pieMenu.destroy();
+
+ // Ensure setting visible = true; visible = false; in onCompleted doesn't cause any problems.
+ var pieMenuComponent = Qt.createComponent("PieMenuVisibleOnCompleted.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ pieMenu = pieMenuComponent.createObject(container);
+ verify(pieMenu, "PieMenu: failed to create an instance");
+ pieMenu.destroy();
+
+ // Ensure constructing a menu as a property (and hence no parent)
+ // with visible = true doesn't cause any problems.
+ pieMenuComponent = Qt.createComponent("PieMenuVisibleButNoParent.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ pieMenu = pieMenuComponent.createObject(container);
+ verify(pieMenu, "PieMenu: failed to create an instance");
+ pieMenu.destroy();
+ }
+
+ function test_triggerMode() {
+ var pieMenuComponent = Qt.createComponent("PieMenu3Items.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var mouseArea = root.mouseArea;
+ var pieMenu = root.pieMenu;
+ currentIndexSignalSpy.signalName = "onCurrentIndexChanged"
+ currentIndexSignalSpy.target = pieMenu;
+ actionSignalSpy.signalName = "onActionTriggered";
+ actionSignalSpy.target = root;
+
+ compare(pieMenu.triggerMode, TriggerMode.TriggerOnClick);
+
+ var triggerModes = [
+ TriggerMode.TriggerOnClick,
+ TriggerMode.TriggerOnPress,
+ TriggerMode.TriggerOnRelease
+ ];
+ // Our root mouse area will accept either left click or right click,
+ // and the menu should accept either to select items or close it.
+ var buttonVariations = [
+ [Qt.LeftButton, Qt.LeftButton],
+ [Qt.RightButton, Qt.RightButton],
+ [Qt.LeftButton, Qt.RightButton],
+ [Qt.RightButton, Qt.LeftButton]
+ ];
+ for (var modeIndex = 0; modeIndex < triggerModes.length; ++modeIndex) {
+ for (var i = 0; i < buttonVariations.length; ++i) {
+ var openButton = buttonVariations[i][0];
+ var closeButton = buttonVariations[i][1];
+
+ pieMenu.triggerMode = triggerModes[modeIndex];
+ compare(pieMenu.triggerMode, triggerModes[modeIndex]);
+
+ // Make the menu visible.
+ if (pieMenu.triggerMode !== TriggerMode.TriggerOnRelease) {
+ mouseClick(root, 0, 0, openButton);
+ } else {
+ mousePress(root, 0, 0, openButton);
+ }
+ tryCompare(pieMenu, "visible", true);
+
+ // Click/press outside the menu to close it.
+ switch (pieMenu.triggerMode) {
+ case TriggerMode.TriggerOnClick:
+ mouseClick(root, 0, 0, closeButton);
+ tryCompare(currentIndexSignalSpy, "count", 0);
+ compare(pieMenu.visible, false, "Menu isn't closed when clicking "
+ + mouseButtonToString(closeButton) + " outside (triggerMode: "
+ + triggerModeToString(pieMenu.triggerMode) + ", openButton: "
+ + mouseButtonToString(openButton) + ")");
+ break;
+ case TriggerMode.TriggerOnPress:
+ mousePress(root, 0, 0, closeButton);
+ tryCompare(currentIndexSignalSpy, "count", 0);
+ compare(pieMenu.visible, false, "Menu isn't closed when pressing "
+ + mouseButtonToString(closeButton) + " outside (triggerMode: "
+ + triggerModeToString(pieMenu.triggerMode) + ", openButton: "
+ + mouseButtonToString(openButton) + ")");
+ mouseRelease(root, 0, 0, closeButton);
+ tryCompare(currentIndexSignalSpy, "count", 0);
+ compare(pieMenu.visible, false, "Menu shouldn't be opened when releasing "
+ + mouseButtonToString(closeButton) + " outside (triggerMode: "
+ + triggerModeToString(pieMenu.triggerMode) + ", openButton: "
+ + mouseButtonToString(openButton) + ")");
+ break;
+ case TriggerMode.TriggerOnRelease:
+ mouseRelease(root, 0, 0, closeButton);
+ tryCompare(currentIndexSignalSpy, "count", 0);
+ compare(pieMenu.visible, false, "Menu is closed when releasing "
+ + mouseButtonToString(closeButton) + " outside");
+ break;
+ }
+
+ // Make the menu visible again.
+ if (pieMenu.triggerMode !== TriggerMode.TriggerOnRelease) {
+ mouseClick(root, 0, 0, openButton);
+ } else {
+ mousePress(root, 0, 0, openButton);
+ }
+ tryCompare(pieMenu, "visible", true);
+
+ switch (pieMenu.triggerMode) {
+ case TriggerMode.TriggerOnClick:
+ // Click on a section of the menu (index 1); it should trigger the item and then close the menu.
+ mouseClick(root, pieMenu.x + pieMenu.width / 2, pieMenu.y + pieMenu.height / 4, closeButton);
+ // The current index changes once when the item is selected (1) and once when the menu closes (-1).
+ tryCompare(currentIndexSignalSpy, "count", 2);
+ compare(actionSignalSpy.count, 1);
+ compare(actionSignalSpy.signalArguments[0][0], 1);
+ compare(pieMenu.currentIndex, -1);
+ compare(pieMenu.visible, false);
+ break;
+ case TriggerMode.TriggerOnPress:
+ // Press on a section of the menu (index 1); it should trigger the item and then close the menu.
+ mousePress(root, pieMenu.x + pieMenu.width / 2, pieMenu.y + pieMenu.height / 4, closeButton);
+ // The current index changes once when the item is selected (1) and once when the menu closes (-1).
+ tryCompare(currentIndexSignalSpy, "count", 2);
+ compare(actionSignalSpy.count, 1);
+ compare(actionSignalSpy.signalArguments[0][0], 1);
+ compare(pieMenu.currentIndex, -1);
+ compare(pieMenu.visible, false);
+
+ mouseRelease(root, pieMenu.x + pieMenu.width / 2, pieMenu.y + pieMenu.height / 4, closeButton);
+ // None of these should change after releasing.
+ tryCompare(currentIndexSignalSpy, "count", 2);
+ compare(actionSignalSpy.count, 1);
+ compare(pieMenu.currentIndex, -1);
+ compare(pieMenu.visible, false);
+ break;
+ case TriggerMode.TriggerOnRelease:
+ // Click/press on a section of the menu (index 1); it should trigger the item and then close the menu.
+ mouseRelease(root, pieMenu.x + pieMenu.width / 2, pieMenu.y + pieMenu.height / 4, closeButton);
+ // The current index changes once when the item is selected (1) and once when the menu closes (-1).
+ tryCompare(currentIndexSignalSpy, "count", 2);
+ compare(actionSignalSpy.count, 1);
+ compare(actionSignalSpy.signalArguments[0][0], 1);
+ compare(pieMenu.currentIndex, -1);
+ compare(pieMenu.visible, false);
+ }
+
+ currentIndexSignalSpy.clear();
+ actionSignalSpy.clear();
+ }
+ }
+ }
+
+ function test_selectionAngle_data() {
+ var data = [];
+
+ var dataRow = {};
+ dataRow.startAngle = -90;
+ dataRow.endAngle = 90;
+ dataRow.mouseX = 1;
+ dataRow.mouseY = 1;
+ dataRow.expectedAngle = -Math.PI / 4;
+ dataRow.expectedCurrentIndex = -1;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", top left";
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = -90;
+ dataRow.endAngle = 90;
+ dataRow.mouseX = menuWidth - 1;
+ dataRow.mouseY = menuHeight - 1;
+ dataRow.expectedAngle = Math.PI * 0.75;
+ dataRow.expectedCurrentIndex = -1;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", bottom right";
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = -90;
+ dataRow.endAngle = -270;
+ dataRow.mouseX = menuWidth / 2 - 1;
+ dataRow.mouseY = menuHeight * 0.75;
+ dataRow.expectedAngle = -3.1215953196166426;
+ dataRow.expectedCurrentIndex = 1;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", bottom left edge";
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = -90;
+ dataRow.endAngle = -270;
+ dataRow.mouseX = menuWidth / 2 + 1;
+ dataRow.mouseY = menuHeight * 0.75;
+ dataRow.expectedAngle = 3.1215953196166426;
+ dataRow.expectedCurrentIndex = 1;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", bottom right edge";
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = 0;
+ dataRow.endAngle = 190;
+ dataRow.mouseX = menuWidth / 2 - 1;
+ dataRow.mouseY = menuHeight * 0.75;
+ dataRow.expectedAngle = -3.1215953196166426;
+ dataRow.expectedCurrentIndex = 2;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", bottom left edge";
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = 0;
+ dataRow.endAngle = -90;
+ dataRow.mouseX = menuWidth / 4;
+ dataRow.mouseY = menuHeight / 2 - 1;
+ dataRow.expectedAngle = -1.550798992821746;
+ dataRow.expectedCurrentIndex = 2;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", section 2";
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = 0;
+ dataRow.endAngle = -90;
+ dataRow.mouseX = menuWidth / 4;
+ dataRow.mouseY = menuHeight / 4;
+ dataRow.expectedAngle = -0.7853981633974483;
+ dataRow.expectedCurrentIndex = 1;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", section 1";
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = 0;
+ dataRow.endAngle = -90;
+ dataRow.mouseX = menuWidth / 2 - 1;
+ dataRow.mouseY = menuHeight / 4;
+ dataRow.expectedAngle = -0.01999733397315053;
+ dataRow.expectedCurrentIndex = 0;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", section 0";
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = -190;
+ dataRow.endAngle = -90;
+ dataRow.mouseX = menuWidth / 2 + 1;
+ dataRow.mouseY = menuHeight * 0.75;
+ dataRow.expectedAngle = 3.1215953196166426;
+ dataRow.expectedCurrentIndex = 0;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", bottom right edge";
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = -90;
+ dataRow.endAngle = -190;
+ dataRow.mouseX = menuWidth / 2 + 1;
+ dataRow.mouseY = menuHeight * 0.75;
+ dataRow.expectedAngle = 3.1215953196166426;
+ dataRow.expectedCurrentIndex = 2;
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", bottom right edge";
+ data.push(dataRow);
+
+ return data;
+ }
+
+ function test_selectionAngle(data) {
+ var pieMenuComponent = Qt.createComponent("PieMenu3Items.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var mouseArea = root.mouseArea;
+ var pieMenu = root.pieMenu;
+
+ compare(pieMenu.selectionAngle, 0);
+
+ pieMenu.__style.startAngle = data.startAngle;
+ pieMenu.__style.endAngle = data.endAngle;
+
+ waitForRendering(root);
+ root.forceActiveFocus();
+ // Don't allow bounds snapping by always opening within bounds.
+ mouseClick(root, menuWidth / 2, menuHeight / 2);
+ tryCompare(pieMenu, "visible", true);
+
+ mouseMove(root, data.mouseX, data.mouseY);
+ compare(pieMenu.selectionAngle, data.expectedAngle);
+ compare(pieMenu.currentIndex, data.expectedCurrentIndex);
+ }
+
+ function test_sectionAngles_data() {
+ var data = [];
+ var angleOrigin = 90;
+
+ var dataRow = {};
+ dataRow.startAngle = -90;
+ dataRow.endAngle = 90;
+ dataRow.section = 0;
+ dataRow.expectedSectionSize = MathUtils.degToRadOffset(60 + angleOrigin);
+ dataRow.expectedSectionStartAngle = MathUtils.degToRadOffset(-90);
+ dataRow.expectedSectionCenterAngle = MathUtils.degToRadOffset(-60);
+ dataRow.expectedSectionEndAngle = MathUtils.degToRadOffset(-30);
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", section=" + dataRow.section;
+ data.push(dataRow);
+
+ dataRow = {};
+ dataRow.startAngle = 270;
+ dataRow.endAngle = 90;
+ dataRow.section = 0;
+ dataRow.expectedSectionSize = MathUtils.degToRadOffset(-60 + angleOrigin);
+ dataRow.expectedSectionStartAngle = MathUtils.degToRadOffset(270);
+ dataRow.expectedSectionCenterAngle = MathUtils.degToRadOffset(240);
+ dataRow.expectedSectionEndAngle = MathUtils.degToRadOffset(210);
+ dataRow.tag = "startAngle=" + dataRow.startAngle + ", endAngle=" + dataRow.endAngle + ", section=" + dataRow.section;
+ data.push(dataRow);
+
+ return data;
+ }
+
+ function test_sectionAngles(data) {
+ var pieMenuComponent = Qt.createComponent("PieMenu3Items.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var pieMenu = root.pieMenu;
+
+ pieMenu.__style.startAngle = data.startAngle;
+ pieMenu.__style.endAngle = data.endAngle;
+ compare(pieMenu.__protectedScope.sectionSize, data.expectedSectionSize);
+ compare(pieMenu.__protectedScope.sectionStartAngle(data.section), data.expectedSectionStartAngle);
+ compare(pieMenu.__protectedScope.sectionCenterAngle(data.section), data.expectedSectionCenterAngle);
+ compare(pieMenu.__protectedScope.sectionEndAngle(data.section), data.expectedSectionEndAngle);
+ }
+
+ function test_bounds_data() {
+ return [
+ { tag: "noSnapCenter", mouseX: container.width / 2, mouseY: container.height / 2,
+ expectedX: container.width / 2 - 100, expectedY: container.height / 2 - 100 },
+ { tag: "noSnapNearLeft", mouseX: 100, mouseY: container.height / 2,
+ expectedX: 0, expectedY: container.height / 2 - 100 },
+ { tag: "noSnapNearRight", mouseX: container.width - 100, mouseY: container.height / 2,
+ expectedX: container.width - menuWidth, expectedY: container.height / 2 - 100 },
+ { tag: "noSnapNearTop", mouseX: container.width / 2, mouseY: 100,
+ expectedX: container.width / 2 - 100, expectedY: 0 },
+ { tag: "noSnapNearBottom", mouseX: container.width / 2, mouseY: container.height - 100,
+ expectedX: container.width / 2 - 100, expectedY: container.height - menuHeight },
+ { tag: "noSnapNearTopLeft", mouseX: 100, mouseY: 100,
+ expectedX: 0, expectedY: 0 },
+ { tag: "noSnapNearTopRight", mouseX: container.width - 100, mouseY: 100,
+ expectedX: container.width - menuWidth, expectedY: 0 },
+ { tag: "noSnapNearBottomRight", mouseX: container.width - 100, mouseY: container.height - 100,
+ expectedX: container.width - menuWidth, expectedY: container.height - menuHeight },
+ { tag: "noSnapNearBottomLeft", mouseX: 100, mouseY: container.height - 100,
+ expectedX: 0, expectedY: container.height - menuHeight },
+ { tag: "leftEdge", mouseX: 10, mouseY: container.height / 2,
+ expectedX: 0, expectedY: container.height / 2 - 100 },
+ { tag: "rightEdge", mouseX: container.width - 10, mouseY: container.height / 2,
+ expectedX: container.width - menuHeight, expectedY: container.height / 2 - 100 },
+ { tag: "topEdge", mouseX: container.width / 2, mouseY: 10,
+ expectedX: container.width / 2 - 100, expectedY: 0 },
+ // The default start and end angles mean that the bottom edge won't snap.
+ { tag: "bottomEdge", mouseX: container.width / 2, mouseY: container.height - 10,
+ expectedX: container.width / 2 - 100, expectedY: container.height - 100 - 10 },
+ { tag: "topLeftCorner", mouseX: 10, mouseY: 10,
+ expectedX: 0, expectedY: 0 },
+ { tag: "topRightCorner", mouseX: container.width - 10, mouseY: 10,
+ expectedX: container.width - menuHeight, expectedY: 0 },
+ { tag: "bottomRightCorner", mouseX: container.width - 10, mouseY: container.height - 10,
+ expectedX: container.width - menuHeight, expectedY: container.height - 100 - 10 },
+ { tag: "bottomLeftCorner", mouseX: 10, mouseY: container.height - 10,
+ expectedX: 0, expectedY: container.height - 100 - 10 }
+ ]
+ }
+
+ function test_bounds(data) {
+ var pieMenuComponent = Qt.createComponent("PieMenu3Items.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var mouseArea = root.mouseArea;
+ var pieMenu = root.pieMenu;
+
+ var rootParent = pieMenu;
+ while (rootParent.parent) {
+ rootParent = rootParent.parent;
+ }
+ // Necessary due to QTBUG-36938
+ rootParent.width = 400;
+ rootParent.height = 400;
+
+ var originalStartAngle = pieMenu.__style.startAngle;
+ var originalEndAngle = pieMenu.__style.endAngle;
+
+ mouseClick(root, data.mouseX, data.mouseY);
+ tryCompare(pieMenu, "visible", true);
+
+ compare(pieMenu.x, data.expectedX);
+ compare(pieMenu.y, data.expectedY);
+
+ // Angles shouldn't change.
+ compare(pieMenu.__style.startAngle, originalStartAngle);
+ compare(pieMenu.__style.endAngle, originalEndAngle);
+
+ // Cancel the menu. Even if it's at the top left, this will land on the cancel area.
+ mouseClick(root, pieMenu.x + pieMenu.width / 2, pieMenu.y + pieMenu.height / 2);
+ compare(pieMenu.visible, false);
+
+ // Angles shouldn't change.
+ compare(pieMenu.__style.startAngle, originalStartAngle);
+ compare(pieMenu.__style.endAngle, originalEndAngle);
+ }
+
+ function test_hideItem_data() {
+ return [
+ { tag: "hideFirst", indexVisibility: [false, true, true], expectedIndexHits: [0, 1, 1], expectedText: ["item1", "item1", "item2"] },
+ { tag: "hideSecond", indexVisibility: [true, false, true], expectedIndexHits: [0, 1, 1], expectedText: ["item0", "item0", "item2"] },
+ { tag: "hideThird", indexVisibility: [true, true, false], expectedIndexHits: [0, 1, 1], expectedText: ["item0", "item0", "item1"] },
+ ];
+ }
+
+ function test_hideItem(data) {
+ var pieMenuComponent = Qt.createComponent("PieMenu3Items.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var mouseArea = root.mouseArea;
+ var pieMenu = root.pieMenu;
+
+ var originalStartAngle = pieMenu.__style.startAngle;
+ var originalEndAngle = pieMenu.__style.endAngle;
+
+ // Store the positions at which we should click before we remove any items.
+ var mousePositions = [Qt.point(40, 70), Qt.point(90, 30), Qt.point(160, 70)];
+
+ while (pieMenu.menuItems.length > 0) {
+ pieMenu.removeItem(pieMenu.menuItems[pieMenu.menuItems.length - 1]);
+ }
+
+ for (var i = 0; i < data.indexVisibility.length; ++i) {
+ var item = pieMenu.addItem("item" + i);
+ item.triggered.connect(function (){ root.actionTriggered(data.expectedIndexHits[i]) })
+ item.visible = data.indexVisibility[i];
+ }
+
+ // Angles shouldn't change.
+ compare(pieMenu.__style.startAngle, originalStartAngle);
+ compare(pieMenu.__style.endAngle, originalEndAngle);
+
+ for (i = 0; i < data.indexVisibility.length; ++i) {
+ actionSignalSpy.signalName = "onActionTriggered";
+ actionSignalSpy.target = root;
+
+ compare(pieMenu.visible, false);
+
+ // Make the menu visible.
+ mouseClick(root, 100, 100, Qt.LeftButton);
+ tryCompare(pieMenu, "visible", true);
+
+ var pos = mousePositions[i];
+
+ mouseMove(root, pos.x, pos.y);
+ // Not all styles have titles.
+ if (pieMenu.__style.title)
+ tryCompare(pieMenu.__panel.titleItem, "text", data.expectedText[i]);
+
+ mouseClick(root, pos.x, pos.y, Qt.LeftButton);
+
+ tryCompare(pieMenu, "visible", false);
+ compare(actionSignalSpy.count, 1);
+ compare(actionSignalSpy.signalArguments[0][0], data.expectedIndexHits[i]);
+
+ actionSignalSpy.clear();
+ }
+ }
+
+ function test_addItem() {
+ pieMenu = Qt.createQmlObject("import QtQuick.Extras 1.1; PieMenu {}", container);
+ compare(pieMenu.menuItems.length, 0);
+
+ pieMenu.addItem("Action 1");
+ compare(pieMenu.menuItems.length, 1);
+ }
+
+ function test_insertItem() {
+ pieMenu = Qt.createQmlObject("import QtQuick.Extras 1.1; PieMenu {}", container);
+ compare(pieMenu.menuItems.length, 0);
+
+ pieMenu.insertItem(0, "Action 1");
+ compare(pieMenu.menuItems.length, 1);
+ compare(pieMenu.menuItems[0].text, "Action 1");
+
+ pieMenu.insertItem(1, "Action 2");
+ compare(pieMenu.menuItems.length, 2);
+ compare(pieMenu.menuItems[0].text, "Action 1");
+ compare(pieMenu.menuItems[1].text, "Action 2");
+ }
+
+ function test_removeItem() {
+ var pieMenuComponent = Qt.createComponent("PieMenu3Items.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var pieMenu = root.pieMenu;
+
+ var originalLength = pieMenu.menuItems.length;
+ for (var i = 0; i < originalLength; ++i) {
+ pieMenu.removeItem(pieMenu.menuItems[pieMenu.menuItems.length - 1]);
+ compare(pieMenu.menuItems.length, originalLength - (i + 1));
+ }
+ }
+
+ function debugMousePosition(pieMenu, mouseX, mouseY, positionText) {
+ var rectItem = Qt.createQmlObject("import QtQuick 2.0; Rectangle { width: 10; height: 10; radius: 5; color: 'red' }", pieMenu);
+ rectItem.x = mouseX - rectItem.width / 2;
+ rectItem.y = mouseY - rectItem.height / 2;
+
+ var textItem = Qt.createQmlObject("import QtQuick 2.0; Text {}", rectItem);
+ textItem.text = positionText;
+ textItem.font.pixelSize = 8;
+ textItem.anchors.centerIn = textItem.parent;
+ }
+
+ function test_selectionItemOnMouseMove_QTRD3024() {
+ // Check when an item is hovered by the mouse, it gets made current
+ // as expected and the current item is cleared when the mouse moves outside the menu
+ var pieMenuComponent = Qt.createComponent("PieMenu3Items.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var mouseArea = root.mouseArea;
+ pieMenu = root.pieMenu;
+
+ // Make the menu visible at (0,0)
+ waitForRendering(root);
+ root.forceActiveFocus();
+ mouseClick(root, 0, 0);
+ tryCompare(pieMenu, "visible", true);
+
+ // Move the mouse over the menu
+ mouseMove(root, 0, 0);
+ compare(pieMenu.currentIndex, -1);
+
+ // We would use a data function for this, but we need access to properties
+ // of the style that change with each style.
+ var data = [];
+ var xCenter = pieMenu.width / 2;
+ var yCenter = pieMenu.height / 2;
+ var startAngleFunction = pieMenu.__protectedScope.sectionStartAngle;
+ var centerAngleFunction = pieMenu.__protectedScope.sectionCenterAngle;
+ var endAngleFunction = pieMenu.__protectedScope.sectionEndAngle;
+
+ var pos = MathUtils.centerAlongCircle(xCenter, yCenter, 0, 0, centerAngleFunction(0), 90)
+ data.push({ name: "inside", mouseX: pos.x, mouseY: pos.y, expectedCurrentIndex: 0 });
+
+ pos = MathUtils.centerAlongCircle(xCenter, yCenter, 0, 0, startAngleFunction(0) + MathUtils.degToRad(1), pieMenu.__style.cancelRadius + 10);
+ data.push({ name: "bottom", mouseX: pos.x, mouseY: pos.y, expectedCurrentIndex: 0 });
+
+ pos = MathUtils.centerAlongCircle(xCenter, yCenter, 0, 0, centerAngleFunction(0), pieMenu.__style.cancelRadius + 10);
+ data.push({ name: "outside", mouseX: pos.x, mouseY: pos.y, expectedCurrentIndex: 0 });
+
+ pos = MathUtils.centerAlongCircle(xCenter, yCenter, 0, 0, endAngleFunction(0) - MathUtils.degToRad(1), pieMenu.__style.radius - 1);
+ data.push({ name: "close to second", mouseX: pos.x, mouseY: pos.y, expectedCurrentIndex: 0 });
+
+ pos = MathUtils.centerAlongCircle(xCenter, yCenter, 0, 0, centerAngleFunction(1), pieMenu.__style.radius - 1);
+ data.push({ name: "outside", mouseX: pos.x, mouseY: pos.y, expectedCurrentIndex: 1 });
+
+ pos = MathUtils.centerAlongCircle(xCenter, yCenter, 0, 0, centerAngleFunction(1) - MathUtils.degToRad(10),
+ pieMenu.__style.cancelRadius + (pieMenu.__style.radius - pieMenu.__style.cancelRadius) / 2);
+ data.push({ name: "inside", mouseX: pos.x, mouseY: pos.y, expectedCurrentIndex: 1 });
+
+ pos = MathUtils.centerAlongCircle(xCenter, yCenter, 0, 0, startAngleFunction(1) + MathUtils.degToRad(1),
+ pieMenu.__style.radius - 1);
+ data.push({ name: "close to first", mouseX: pos.x, mouseY: pos.y, expectedCurrentIndex: 1 });
+
+ pos = MathUtils.centerAlongCircle(xCenter, yCenter, 0, 0, centerAngleFunction(1), pieMenu.__style.cancelRadius + 10);
+ data.push({ name: "low center", mouseX: 100, mouseY: 50, expectedCurrentIndex: 1 });
+
+ for (var i = 0; i < data.length; ++i) {
+ var mouseX = data[i].mouseX;
+ var mouseY = data[i].mouseY;
+ var expectedCurrentIndex = data[i].expectedCurrentIndex;
+
+ // Illustrates the positions.
+// debugMousePosition(pieMenu, mouseX, mouseY, i);
+// wait(1000)
+
+ mouseMove(root, mouseX, mouseY);
+ compare(pieMenu.currentIndex, expectedCurrentIndex, data[i].name + ": current index should be "
+ + expectedCurrentIndex + " when mouse is at " + mouseX + ", " + mouseY);
+ }
+ }
+
+ function test_QTRD3027() {
+ // Check that an item's selection is cleared when the mouse moves outside
+ // its boundaries without changing the selectionAngle
+ var pieMenuComponent = Qt.createComponent("PieMenu3Items.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var mouseArea = root.mouseArea;
+ var pieMenu = root.pieMenu;
+
+ // Make the menu visible at (0,0)
+ root.forceActiveFocus();
+ mouseClick(root, 0, 0);
+ tryCompare(pieMenu, "visible", true);
+
+ // Move the mouse over the menu
+ mouseMove(root, 0, 0)
+ compare(pieMenu.currentIndex, -1)
+ // Move over middle item
+ mouseMove(root, 100, 50)
+ compare(pieMenu.currentIndex, 1)
+ selectedAngleChangedSpy.signalName = "onSelectionAngleChanged"
+ selectedAngleChangedSpy.target = pieMenu;
+ // Move outside the middle item without changing angle
+ mouseMove(root, 100, 98)
+ compare(pieMenu.currentIndex, -1)
+ compare(selectedAngleChangedSpy.count, 0)
+ }
+
+ function test_rotatedBoundingItem() {
+ var pieMenuComponent = Qt.createComponent("PieMenuRotatedBoundingItem.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var mouseArea = root.mouseArea;
+ var pieMenu = root.pieMenu;
+
+ mouseClick(root, root.width / 2, root.height / 2, Qt.RightButton);
+ tryCompare(pieMenu, "visible", true);
+
+ mouseMove(root, 230, 145);
+ // Not all styles have titles.
+ if (pieMenu.__style.title)
+ tryCompare(pieMenu.__panel.titleItem, "text", "Action 1");
+
+ actionSignalSpy.signalName = "onActionTriggered";
+ actionSignalSpy.target = root;
+ mouseClick(root, 230, 145);
+ compare(actionSignalSpy.count, 1);
+ compare(actionSignalSpy.signalArguments[0][0], 0);
+ }
+
+ function test_boundingItem() {
+ var oldContainerWidth = container.width;
+ var oldContainerHeight = container.height;
+ container.width = 560;
+ container.height = 560;
+
+ // Tests boundingItem when there are nested margins.
+ var pieMenuComponent = Qt.createComponent("PieMenuBoundingItem.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var mouseArea = root.mouseArea;
+ var pieMenu = root.pieMenu;
+
+ var mouseAreaAbsolutePos = mouseArea.mapToItem(root, 0, 0);
+ mouseClick(root, mouseAreaAbsolutePos.x + mouseArea.width / 2, mouseAreaAbsolutePos.y, Qt.RightButton);
+ compare(pieMenu.visible, true);
+ compare(pieMenu.y, -root.margins);
+
+ mouseClick(root, 0, 0);
+ compare(pieMenu.visible, false);
+
+ var mouseAreaCenterAbsolutePos = mouseArea.mapToItem(root, mouseArea.width / 2, mouseArea.height / 2);
+ mouseClick(root, mouseAreaCenterAbsolutePos.x, mouseAreaCenterAbsolutePos.y, Qt.RightButton);
+ compare(pieMenu.visible, true);
+ compare(pieMenu.mapToItem(root, 0, 0).x, mouseAreaCenterAbsolutePos.x - pieMenu.width / 2);
+ compare(pieMenu.mapToItem(root, 0, 0).y, mouseAreaCenterAbsolutePos.y - pieMenu.height / 2);
+
+ container.width = oldContainerWidth;
+ container.height = oldContainerHeight;
+ }
+
+ function test_longPressTriggerOnClick() {
+ // Tests that a menu that is opened on press or long press does not
+ // get closed by a release event when the triggerMode requires a
+ // press before the release (TriggerOnClick).
+ var pieMenuComponent = Qt.createComponent("PieMenu3ItemsLongPress.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var pieMenu = root.pieMenu;
+
+ mousePress(root, 0, 0, Qt.LeftButton);
+ tryCompare(pieMenu, "visible", true);
+ compare(pieMenu.__mouseThief.receivedPressEvent, false);
+ compare(pieMenu.__protectedScope.pressedIndex, -1);
+
+ mouseRelease(root, 0, 0, Qt.LeftButton);
+ compare(pieMenu.visible, true);
+ compare(pieMenu.__mouseThief.receivedPressEvent, false);
+ compare(pieMenu.__protectedScope.pressedIndex, -1);
+
+ mousePress(root, 0, 0, Qt.LeftButton);
+ compare(pieMenu.visible, true);
+ compare(pieMenu.__mouseThief.receivedPressEvent, true);
+ compare(pieMenu.__protectedScope.pressedIndex, -1);
+
+ mouseRelease(root, 0, 0, Qt.LeftButton);
+ compare(pieMenu.visible, false);
+ compare(pieMenu.__mouseThief.receivedPressEvent, false);
+ compare(pieMenu.__protectedScope.pressedIndex, -1);
+ }
+
+ function test_keepMenuOpenWhenTriggered() {
+ // This functionality is used in the flat example.
+ var pieMenuComponent = Qt.createComponent("PieMenu3ItemsKeepOpen.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ var pieMenu = root.pieMenu;
+ actionSignalSpy.signalName = "onActionTriggered";
+ actionSignalSpy.target = root;
+
+ mouseClick(root, 0, 0, Qt.LeftButton);
+ tryCompare(pieMenu, "visible", true);
+
+ mouseClick(root, pieMenu.x + pieMenu.width / 4, pieMenu.y + pieMenu.height / 4, Qt.LeftButton);
+ tryCompare(pieMenu, "visible", true);
+ compare(actionSignalSpy.count, 1);
+ compare(actionSignalSpy.signalArguments[0][0], 0);
+
+ actionSignalSpy.clear();
+ mouseClick(root, pieMenu.x + pieMenu.width / 2, pieMenu.y + pieMenu.height / 4, Qt.LeftButton);
+ tryCompare(pieMenu, "visible", true);
+ compare(actionSignalSpy.count, 1);
+ compare(actionSignalSpy.signalArguments[0][0], 1);
+
+ actionSignalSpy.clear();
+ mouseClick(root, pieMenu.x + pieMenu.width * 0.75, pieMenu.y + pieMenu.height / 4, Qt.LeftButton);
+ tryCompare(pieMenu, "visible", true);
+ compare(actionSignalSpy.count, 1);
+ compare(actionSignalSpy.signalArguments[0][0], 2);
+ }
+
+ function test_pressedIndex() {
+ var pieMenuComponent = Qt.createComponent("PieMenu3Items.qml");
+ tryCompare(pieMenuComponent, "status", Component.Ready);
+ root = pieMenuComponent.createObject(container);
+ pieMenu = root.pieMenu;
+ actionSignalSpy.signalName = "onActionTriggered";
+ actionSignalSpy.target = root;
+
+ mouseClick(root, 0, 0, Qt.LeftButton);
+ tryCompare(pieMenu, "visible", true);
+ compare(pieMenu.__protectedScope.pressedIndex, -1);
+
+ mousePress(root, pieMenu.x + pieMenu.width / 4, pieMenu.y + pieMenu.height / 4, Qt.LeftButton);
+ tryCompare(pieMenu, "visible", true);
+ compare(pieMenu.__protectedScope.pressedIndex, 0);
+
+ mouseRelease(root, pieMenu.x + pieMenu.width / 4, pieMenu.y + pieMenu.height / 4, Qt.LeftButton);
+ tryCompare(pieMenu, "visible", false);
+ compare(actionSignalSpy.count, 1);
+ compare(actionSignalSpy.signalArguments[0][0], 0);
+ compare(pieMenu.__protectedScope.pressedIndex, -1);
+ }
+ }
+}
diff --git a/tests/auto/extras/data/tst_statusindicator.qml b/tests/auto/extras/data/tst_statusindicator.qml
new file mode 100644
index 00000000..e9fa6ac9
--- /dev/null
+++ b/tests/auto/extras/data/tst_statusindicator.qml
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.1
+import QtQuick.Extras 1.3
+import QtQuick.Extras.Private 1.0
+
+TestCase {
+ id: testCase
+ name: "Tests_StatusIndicator"
+ visible: windowShown
+ when: windowShown
+ width: 400
+ height: 400
+
+ property var indicator
+
+ function cleanup() {
+ if (indicator)
+ indicator.destroy();
+ }
+
+ function test_instance() {
+ indicator = Qt.createQmlObject("import QtQuick.Extras 1.3; StatusIndicator { }", testCase, "");
+ verify(indicator, "StatusIndicator: failed to create an instance")
+ verify(indicator.__style);
+ }
+
+ function test_active_data() {
+ if (StyleSettings.styleName === "Flat") {
+ return [
+ { tag: "active", active: true, expectedColor: { r: 18, g: 136, b: 203 } },
+ { tag: "inactive", active: false, expectedColor: { r: 179, g: 179, b: 179 } }
+ ];
+ }
+
+ return [
+ { tag: "active", active: true, expectedColor: { r: 255, g: 99, b: 99 } },
+ { tag: "inactive", active: false, expectedColor: { r: 52, g: 52, b: 52 } }
+ ];
+ }
+
+ function test_active(data) {
+ indicator = Qt.createQmlObject("import QtQuick.Extras 1.3; StatusIndicator { }", testCase, "");
+ verify(indicator);
+ compare(indicator.active, false);
+
+ indicator.active = data.active;
+ // Color is slightly different on some platforms/machines, like Windows.
+ var lenience = StyleSettings.styleName === "Flat" ? 0 : 2;
+
+ waitForRendering(indicator);
+ var image = grabImage(indicator);
+ fuzzyCompare(image.red(indicator.width / 2, indicator.height / 2), data.expectedColor.r, lenience);
+ fuzzyCompare(image.green(indicator.width / 2, indicator.height / 2), data.expectedColor.g, lenience);
+ fuzzyCompare(image.blue(indicator.width / 2, indicator.height / 2), data.expectedColor.b, lenience);
+ }
+
+ function test_color() {
+ var flatStyle = StyleSettings.styleName === "Flat";
+
+ indicator = Qt.createQmlObject("import QtQuick.Extras 1.3; StatusIndicator { }", testCase, "");
+ verify(indicator);
+ compare(indicator.color, flatStyle ? "#1288cb" : "#ff0000");
+
+ // The base style uses a gradient for its color, so pure blue will not be pure blue.
+ var expectedR = flatStyle ? 0 : 99;
+ var expectedG = flatStyle ? 0 : 99;
+ var expectedB = flatStyle ? 255 : 255;
+ var lenience = flatStyle ? 0 : 2;
+
+ indicator.active = true;
+ indicator.color = "#0000ff";
+ waitForRendering(indicator);
+ var image = grabImage(indicator);
+ fuzzyCompare(image.red(indicator.width / 2, indicator.height / 2), expectedR, lenience);
+ fuzzyCompare(image.green(indicator.width / 2, indicator.height / 2), expectedG, lenience);
+ fuzzyCompare(image.blue(indicator.width / 2, indicator.height / 2), expectedB, lenience);
+ }
+
+ function test_baseStyleHasOuterShadow() {
+ if (StyleSettings.styleName !== "Base")
+ return;
+
+ indicator = Qt.createQmlObject("import QtQuick.Extras 1.3; StatusIndicator { }", testCase, "");
+ verify(indicator);
+
+ // There should be a "shadow" here...
+ waitForRendering(indicator);
+ var image = grabImage(indicator);
+ verify(image.pixel(indicator.width / 2, 1) !== Qt.rgba(1, 1, 1, 1));
+ }
+}
diff --git a/tests/auto/extras/data/tst_togglebutton.qml b/tests/auto/extras/data/tst_togglebutton.qml
new file mode 100644
index 00000000..46b87f93
--- /dev/null
+++ b/tests/auto/extras/data/tst_togglebutton.qml
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.1
+
+TestCase {
+ id: testcase
+ name: "Tests_ToggleButton"
+ visible: windowShown
+ when: windowShown
+ width: 400
+ height: 400
+
+ function test_instance() {
+ var button = Qt.createQmlObject('import QtQuick.Extras 1.3; ToggleButton { }', testcase, '');
+ verify (button, "ToggleButton: failed to create an instance")
+ verify(button.__style)
+ verify(button.checkable)
+ verify(!button.checked)
+ verify(!button.pressed)
+ button.destroy()
+ }
+
+ function test_largeText() {
+ // Should be no binding loop warnings.
+ var button = Qt.createQmlObject("import QtQuick.Extras 1.3; ToggleButton { "
+ + "anchors.centerIn: parent; text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }", testcase, "");
+ verify(button, "ToggleButton: failed to create an instance");
+ waitForRendering(button);
+ }
+}
diff --git a/tests/auto/extras/data/tst_tumbler.qml b/tests/auto/extras/data/tst_tumbler.qml
new file mode 100644
index 00000000..936269c8
--- /dev/null
+++ b/tests/auto/extras/data/tst_tumbler.qml
@@ -0,0 +1,503 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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$
+**
+****************************************************************************/
+
+import QtTest 1.0
+import QtQuick 2.1
+import QtQuick.Extras 1.3
+import QtQuick.Extras.Styles 1.3
+import QtQuick.Extras.Private 1.0
+import "TestUtils.js" as TestUtils
+
+Item {
+ id: container
+ width: 200
+ height: 200
+
+ TestCase {
+ id: testCase
+ name: "Tests_Tumbler"
+ when: windowShown
+ anchors.fill: parent
+
+ property var tumbler: null
+
+ property Component simpleColumn: TumblerColumn {
+ model: ListModel {
+ Component.onCompleted: {
+ for (var i = 0; i < 5; ++i) {
+ append({value: i.toString()});
+ }
+ }
+ }
+ }
+
+ property Component simpleColumn6Items: TumblerColumn {
+ model: ListModel {
+ Component.onCompleted: {
+ for (var i = 0; i < 6; ++i) {
+ append({value: i.toString()});
+ }
+ }
+ }
+ }
+
+ function init() {
+ tumbler = Qt.createQmlObject("import QtQuick.Extras 1.3; Tumbler { }", container, "");
+ verify(tumbler, "Tumbler: failed to create an instance");
+ }
+
+ function cleanup() {
+ tumbler.destroy();
+ }
+
+ function test_instance() {
+ // Tests instance creation via init() => cleanup().
+ }
+
+ function columnXCenter(columnIndex) {
+ var columnWidth = tumbler.width / tumbler.columnCount;
+ var halfColumnWidth = (columnWidth) / 2;
+ return tumbler.__style.padding.left + halfColumnWidth + (columnWidth * columnIndex);
+ }
+
+ // visualItemIndex is from 0 to the amount of visible items.
+ function itemCenterPos(columnIndex, visualItemIndex) {
+ var halfDelegateHeight = tumbler.__style.__delegateHeight / 2;
+ var yCenter = tumbler.y + tumbler.__style.padding.top + halfDelegateHeight
+ + (tumbler.__style.__delegateHeight * visualItemIndex);
+ return Qt.point(columnXCenter(columnIndex), yCenter);
+ }
+
+ function test_currentIndex() {
+ var column = simpleColumn.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+ compare(tumbler.currentIndexAt(0), 0);
+ compare(column.currentIndex, 0);
+ waitForRendering(tumbler);
+
+ var pos = Qt.point(columnXCenter(0), tumbler.height / 2);
+ mouseDrag(tumbler, pos.x, pos.y, 0, -tumbler.__style.__delegateHeight / 2, Qt.LeftButton, Qt.NoModifier, 200);
+ compare(tumbler.currentIndexAt(0), 1);
+ compare(column.currentIndex, 1);
+ }
+
+ function test_setCurrentIndexAt() {
+ var column = simpleColumn.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+ compare(tumbler.currentIndexAt(0), 0);
+ waitForRendering(tumbler);
+
+ tumbler.setCurrentIndexAt(0, -1);
+ compare(tumbler.currentIndexAt(0), 0);
+
+ tumbler.setCurrentIndexAt(0, -2);
+ compare(tumbler.currentIndexAt(0), 0);
+
+ tumbler.setCurrentIndexAt(0, tumbler.getColumn(0).model.count);
+ compare(tumbler.currentIndexAt(0), 0);
+
+ tumbler.setCurrentIndexAt(0, tumbler.getColumn(0).model.count + 1);
+ compare(tumbler.currentIndexAt(0), 0);
+
+ tumbler.setCurrentIndexAt(-1, 0);
+ for (var i = 0; i < tumbler.columnCount; ++i) {
+ compare(tumbler.currentIndexAt(i), 0);
+ }
+
+ tumbler.setCurrentIndexAt(-1, 1);
+ for (i = 0; i < tumbler.columnCount; ++i) {
+ compare(tumbler.currentIndexAt(i), 0);
+ }
+
+ tumbler.setCurrentIndexAt(0, 1);
+ tryCompare(tumbler.__viewAt(0), "offset", 4);
+ compare(tumbler.currentIndexAt(0), 1);
+ }
+
+ function test_visible() {
+ var column = simpleColumn.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+ column = simpleColumn.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+ compare(tumbler.currentIndexAt(0), 0);
+ waitForRendering(tumbler);
+
+ tumbler.getColumn(1).visible = false;
+ verify(!tumbler.__viewAt(1).visible);
+ // Right-most column never has a separator.
+ compare(tumbler.__viewAt(1).parent.separator, null);
+
+ tumbler.getColumn(1).visible = true;
+ verify(tumbler.__viewAt(1).visible);
+
+ tumbler.getColumn(0).visible = false;
+ verify(!tumbler.__viewAt(0).visible);
+ if (StyleSettings.styleName === "Base")
+ verify(!tumbler.__viewAt(0).parent.separator.visible);
+ }
+
+ function test_keyboardNavigation() {
+ var column = simpleColumn.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+ column = simpleColumn.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+ compare(tumbler.currentIndexAt(0), 0);
+ waitForRendering(tumbler);
+
+ // Tab through each column twice.
+ for (var i = 0; i < 4; ++i) {
+ var columnIndex = i % 2;
+ // Speed it up.
+ tumbler.__highlightMoveDuration = 50;
+
+ keyClick(Qt.Key_Tab);
+ verify(tumbler.__viewAt(columnIndex).activeFocus);
+
+ // Navigate upwards through entire column.
+ for (var j = 0; j < column.model.count - 1; ++j) {
+ tryCompare(tumbler.__movementDelayTimer, "running", false);
+ keyClick(Qt.Key_Up);
+ tryCompare(tumbler.__viewAt(columnIndex), "offset", j + 1);
+ compare(tumbler.currentIndexAt(columnIndex), column.model.count - 1 - j);
+ }
+
+ tryCompare(tumbler.__movementDelayTimer, "running", false);
+ keyClick(Qt.Key_Up);
+ tryCompare(tumbler.__viewAt(columnIndex), "offset", 0);
+ compare(tumbler.currentIndexAt(columnIndex), 0);
+
+ // Navigate downwards through entire column.
+ for (j = 0; j < column.model.count - 1; ++j) {
+ tryCompare(tumbler.__movementDelayTimer, "running", false);
+ keyClick(Qt.Key_Down);
+ tryCompare(tumbler.__viewAt(columnIndex), "offset", column.model.count - 1 - j);
+ compare(tumbler.currentIndexAt(columnIndex), j + 1);
+ }
+
+ tryCompare(tumbler.__movementDelayTimer, "running", false);
+ keyClick(Qt.Key_Down);
+ tryCompare(tumbler.__viewAt(columnIndex), "offset", 0);
+ compare(tumbler.currentIndexAt(columnIndex), 0);
+ }
+
+ // Shift-tab through columns. Focus is on the last column.
+ for (i = 0; i < 4; ++i) {
+ keyClick(Qt.Key_Tab, Qt.ShiftModifier);
+ verify(tumbler.__viewAt(i % 2).activeFocus);
+ }
+
+ // Go back to the first column.
+ keyClick(Qt.Key_Tab, Qt.ShiftModifier);
+ verify(tumbler.__viewAt(0).activeFocus);
+ compare(tumbler.__viewAt(0).offset, 0);
+ }
+
+ property Component fourItemColumn: TumblerColumn {
+ model: 4
+ }
+
+ property Component fourItemDelegate: Item {
+ implicitHeight: 40
+
+ Text {
+ text: styleData.value
+ anchors.centerIn: parent
+ }
+ }
+
+ function test_itemsCorrectlyPositioned() {
+ // TODO: rewrite this test so that it tests supported usecases.
+ // Somehow it works with the Base style. It could be rewritten to use an
+ // equal amount of items for the model and visibleItemCount, judging from
+ // the snippet in QTBUG-40298.
+ if (StyleSettings.styleName === "Flat")
+ skip("Not a valid test case as the model count is less than the visibleItemCount");
+
+ tumbler.height = 120;
+ // By default, the delegate height is based on the height of the tumbler,
+ // but it starts off at 0.
+ compare(tumbler.__style.__delegateHeight, 0);
+
+ var column = fourItemColumn.createObject(tumbler);
+ column.delegate = fourItemDelegate;
+ compare(tumbler.addColumn(column), column);
+ // Now that the delegate has changed, the binding is reevaluated and we get 120 / 3.
+ compare(tumbler.__style.__delegateHeight, 40);
+ waitForRendering(tumbler);
+
+ keyClick(Qt.Key_Tab)
+ verify(tumbler.__viewAt(0).activeFocus);
+ var firstItemCenterPos = itemCenterPos(0, 1);
+ var firstItem = tumbler.__viewAt(0).itemAt(firstItemCenterPos.x, firstItemCenterPos.y);
+ var actualPos = container.mapFromItem(firstItem, 0, 0);
+ compare(actualPos.x, tumbler.__style.padding.left);
+ compare(actualPos.y, tumbler.__style.padding.top + 40);
+
+ keyClick(Qt.Key_Down);
+ tryCompare(tumbler.__viewAt(0), "offset", 3.0);
+ firstItemCenterPos = itemCenterPos(0, 0);
+ firstItem = tumbler.__viewAt(0).itemAt(firstItemCenterPos.x, firstItemCenterPos.y);
+ verify(firstItem);
+ // Test QTBUG-40298.
+ actualPos = container.mapFromItem(firstItem, 0, 0);
+ compare(actualPos.x, tumbler.__style.padding.left);
+ compare(actualPos.y, tumbler.__style.padding.top);
+
+ var secondItemCenterPos = itemCenterPos(0, 1);
+ var secondItem = tumbler.__viewAt(0).itemAt(secondItemCenterPos.x, secondItemCenterPos.y);
+ verify(secondItem);
+ verify(firstItem.y < secondItem.y);
+
+ var thirdItemCenterPos = itemCenterPos(0, 2);
+ var thirdItem = tumbler.__viewAt(0).itemAt(thirdItemCenterPos.x, thirdItemCenterPos.y);
+ verify(thirdItem);
+ verify(firstItem.y < thirdItem.y);
+ verify(secondItem.y < thirdItem.y);
+ }
+
+ property Component oneHundredItemColumn: TumblerColumn {
+ model: ListModel {
+ Component.onCompleted: {
+ for (var i = 0; i < 100; ++i) {
+ append({value: i.toString()});
+ }
+ }
+ }
+ }
+
+ function test_resizeAfterFlicking() {
+ // Test QTBUG-40367 (which is actually invalid because it was my fault :)).
+ var column = oneHundredItemColumn.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+ waitForRendering(tumbler);
+
+ // Flick in some direction.
+ var pos = Qt.point(columnXCenter(0), tumbler.__style.padding.top);
+ mouseDrag(tumbler, pos.x, pos.y, 0, tumbler.height - tumbler.__style.padding.bottom,
+ Qt.LeftButton, Qt.NoModifier, 300);
+ tryCompare(tumbler.__viewAt(0), "offset", StyleSettings.styleName === "Flat" ? 6.0 : 4.0);
+
+ tumbler.height += 100;
+ var padding = tumbler.__style.padding;
+ compare(tumbler.__style.__delegateHeight,
+ (tumbler.height - padding.top - padding.bottom) / tumbler.__style.visibleItemCount);
+ waitForRendering(tumbler);
+ pos = itemCenterPos(0, 1);
+ var ninetyEighthItem = tumbler.__viewAt(0).itemAt(pos.x, pos.y);
+ verify(ninetyEighthItem);
+ }
+
+ property Component dayOfMonthColumn: TumblerColumn {
+ model: ListModel {
+ Component.onCompleted: {
+ for (var i = 0; i < 31; ++i) {
+ append({value: i.toString()});
+ }
+ }
+ }
+ }
+
+ property Component yearColumn: TumblerColumn {
+ model: ListModel {
+ Component.onCompleted: {
+ for (var i = 2000; i < 2100; ++i) {
+ append({value: i.toString()});
+ }
+ }
+ }
+ }
+
+ function test_focusPastLastColumn() {
+ var column = dayOfMonthColumn.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+ column = yearColumn.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+
+ var mouseArea = Qt.createQmlObject(
+ "import QtQuick 2.2; MouseArea { activeFocusOnTab: true; width: 50; height: 50 }", container, "");
+
+ keyClick(Qt.Key_Tab);
+ verify(tumbler.__viewAt(0).activeFocus);
+ verify(tumbler.getColumn(0).activeFocus);
+ verify(!tumbler.__viewAt(1).activeFocus);
+ verify(!tumbler.getColumn(1).activeFocus);
+
+ keyClick(Qt.Key_Tab);
+ verify(!tumbler.__viewAt(0).activeFocus);
+ verify(!tumbler.getColumn(0).activeFocus);
+ verify(tumbler.__viewAt(1).activeFocus);
+ verify(tumbler.getColumn(1).activeFocus);
+
+ keyClick(Qt.Key_Tab);
+ verify(!tumbler.__viewAt(0).activeFocus);
+ verify(!tumbler.getColumn(0).activeFocus);
+ verify(!tumbler.__viewAt(1).activeFocus);
+ verify(!tumbler.getColumn(1).activeFocus);
+ verify(mouseArea.activeFocus);
+
+ mouseArea.destroy();
+ }
+
+ function test_datePicker() {
+ tumbler.destroy();
+
+ var component = Qt.createComponent("TumblerDatePicker.qml");
+ compare(component.status, Component.Ready);
+ tumbler = component.createObject(container);
+ // Should not be any warnings.
+
+ // March.
+ tumbler.setCurrentIndexAt(1, 2);
+ compare(tumbler.currentIndexAt(1), 2);
+ compare(tumbler.getColumn(1).currentIndex, 2);
+
+ // 30th of March.
+ tumbler.setCurrentIndexAt(0, 29);
+ compare(tumbler.currentIndexAt(0), 29);
+ compare(tumbler.getColumn(0).currentIndex, 29);
+
+ // February.
+ tumbler.setCurrentIndexAt(1, 1);
+ compare(tumbler.currentIndexAt(1), 1);
+ compare(tumbler.getColumn(1).currentIndex, 1);
+ compare(tumbler.getColumn(0).currentIndex, 27);
+ }
+
+ property Component displacementStyle: TumblerStyle {
+ visibleItemCount: 5
+
+ delegate: Item {
+ objectName: "delegate" + styleData.index
+ implicitHeight: (control.height - padding.top - padding.bottom) / visibleItemCount
+
+ property real displacement: styleData.displacement
+
+ Text {
+ text: styleData.value
+ anchors.centerIn: parent
+ }
+
+ Text {
+ anchors.right: parent.right
+ text: styleData.displacement.toFixed(1)
+ }
+ }
+ }
+
+ function test_displacement_data() {
+ var data = [
+ // At 0 offset, the first item is current.
+ { index: 0, offset: 0, expectedDisplacement: 0 },
+ { index: 1, offset: 0, expectedDisplacement: -1 },
+ { index: 5, offset: 0, expectedDisplacement: 1 },
+ // When we start to move the first item down, the second item above it starts to become current.
+ { index: 0, offset: 0.25, expectedDisplacement: -0.25 },
+ { index: 1, offset: 0.25, expectedDisplacement: -1.25 },
+ { index: 5, offset: 0.25, expectedDisplacement: 0.75 },
+ { index: 0, offset: 0.5, expectedDisplacement: -0.5 },
+ { index: 1, offset: 0.5, expectedDisplacement: -1.5 },
+ { index: 5, offset: 0.5, expectedDisplacement: 0.5 },
+ // By this stage, the delegate at index 1 is destroyed, so we can't test its displacement.
+ { index: 0, offset: 0.75, expectedDisplacement: -0.75 },
+ { index: 5, offset: 0.75, expectedDisplacement: 0.25 },
+ { index: 0, offset: 4.75, expectedDisplacement: 1.25 },
+ { index: 1, offset: 4.75, expectedDisplacement: 0.25 },
+ { index: 0, offset: 4.5, expectedDisplacement: 1.5 },
+ { index: 1, offset: 4.5, expectedDisplacement: 0.5 },
+ { index: 0, offset: 4.25, expectedDisplacement: 1.75 },
+ { index: 1, offset: 4.25, expectedDisplacement: 0.75 }
+ ];
+ for (var i = 0; i < data.length; ++i) {
+ var row = data[i];
+ row.tag = "delegate" + row.index + " offset=" + row.offset + " expectedDisplacement=" + row.expectedDisplacement;
+ }
+ return data;
+ }
+
+ function test_displacement(data) {
+ tumbler.style = displacementStyle;
+
+ var column = simpleColumn6Items.createObject(tumbler);
+ compare(tumbler.addColumn(column), column);
+ waitForRendering(tumbler);
+ compare(tumbler.columnCount, 1);
+ compare(tumbler.__viewAt(0).count, 6);
+
+ var delegate = TestUtils.findChild(tumbler, "delegate" + data.index);
+ verify(delegate);
+
+ tumbler.__viewAt(0).offset = data.offset;
+ compare(delegate.displacement, data.expectedDisplacement);
+ }
+
+ function test_visibleItemCount_data() {
+ var data = [
+ // e.g. {0: 2} = {delegate index: y pos / delegate height}
+ // Skip item at index 3, because it's out of view.
+ { model: 6, visibleItemCount: 5, expectedYPositions: {0: 2, 1: 3, 2: 4, 4: 0} },
+ { model: 5, visibleItemCount: 3, expectedYPositions: {0: 1, 1: 2, 4: 0} },
+ // Takes up the whole view.
+ { model: 2, visibleItemCount: 1, expectedYPositions: {0: 0} },
+ ];
+
+ for (var i = 0; i < data.length; ++i) {
+ data[i].tag = "items=" + data[i].model + ", visibleItemCount=" + data[i].visibleItemCount;
+ }
+ return data;
+ }
+
+ function test_visibleItemCount(data) {
+ tumbler.style = displacementStyle;
+ tumbler.__style.visibleItemCount = data.visibleItemCount;
+
+ var column = simpleColumn.createObject(tumbler);
+ column.model = data.model;
+ compare(tumbler.addColumn(column), column);
+ waitForRendering(tumbler);
+ compare(tumbler.columnCount, 1);
+ compare(tumbler.__viewAt(0).count, data.model);
+
+ for (var delegateIndex = 0; delegateIndex < data.visibleItemCount; ++delegateIndex) {
+ if (data.expectedYPositions.hasOwnProperty(delegateIndex)) {
+ var delegate = TestUtils.findChild(tumbler, "delegate" + delegateIndex);
+ verify(delegate, "Delegate found at index " + delegateIndex);
+ var expectedYPos = data.expectedYPositions[delegateIndex] * tumbler.__style.__delegateHeight;
+ compare(delegate.mapToItem(tumbler.__viewAt(0), 0, 0).y, expectedYPos);
+ }
+ }
+ }
+ }
+}
diff --git a/tests/auto/extras/extras.pro b/tests/auto/extras/extras.pro
new file mode 100644
index 00000000..66ca9573
--- /dev/null
+++ b/tests/auto/extras/extras.pro
@@ -0,0 +1,32 @@
+TEMPLATE = app
+TARGET = tst_extras
+
+CONFIG += qmltestcase console
+CONFIG += insignificant_test # requires qtgraphicaleffects (QTBUG-35112)
+
+SOURCES += $$PWD/tst_extras.cpp
+
+TESTDATA = $$PWD/data/*
+
+OTHER_FILES += \
+ $$PWD/data/tst_circulargauge.qml \
+ $$PWD/data/tst_circulartickmarklabel.qml \
+ $$PWD/data/tst_common.qml \
+ $$PWD/data/tst_dial.qml \
+ $$PWD/data/tst_piemenu.qml \
+ $$PWD/data/tst_delaybutton.qml \
+ $$PWD/data/tst_statusindicator.qml \
+ $$PWD/data/tst_thermometergauge.qml \
+ $$PWD/data/tst_togglebutton.qml \
+ $$PWD/data/tst_tumbler.qml \
+ $$PWD/data/PieMenu3Items.qml \
+ $$PWD/data/PieMenu3ItemsLongPress.qml \
+ $$PWD/data/PieMenu3ItemsKeepOpen.qml \
+ $$PWD/data/PieMenuVisibleOnCompleted.qml \
+ $$PWD/data/PieMenuVisibleButNoParent.qml \
+ $$PWD/data/tst_gauge.qml \
+ $$PWD/data/tst_picture.qml \
+ $$PWD/data/TestUtils.js \
+ $$PWD/data/TumblerDatePicker.qml \
+ $$PWD/data/PieMenuRotatedBoundingItem.qml \
+ $$PWD/data/PieMenuBoundingItem.qml
diff --git a/tests/auto/extras/tst_extras.cpp b/tests/auto/extras/tst_extras.cpp
new file mode 100644
index 00000000..92abacbb
--- /dev/null
+++ b/tests/auto/extras/tst_extras.cpp
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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 <QtQuickTest/quicktest.h>
+
+QUICK_TEST_MAIN(extras)
diff --git a/tests/auto/paint/paint.pro b/tests/auto/paint/paint.pro
new file mode 100644
index 00000000..48ec8c02
--- /dev/null
+++ b/tests/auto/paint/paint.pro
@@ -0,0 +1,6 @@
+TEMPLATE = app
+TARGET = tst_paint
+QT += qml quick testlib
+CONFIG += testcase insignificant_test
+
+SOURCES += $$PWD/tst_paint.cpp
diff --git a/tests/auto/paint/tst_paint.cpp b/tests/auto/paint/tst_paint.cpp
new file mode 100644
index 00000000..e7a99b09
--- /dev/null
+++ b/tests/auto/paint/tst_paint.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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 <QtTest>
+#include <QtQml>
+#include <QtQuick>
+
+class tst_Paint : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void bounds_data();
+ void bounds();
+};
+
+void tst_Paint::bounds_data()
+{
+ QTest::addColumn<QString>("name");
+
+ QTest::newRow("CircularGauge") << "CircularGauge";
+ QTest::newRow("Dial") << "Dial";
+ QTest::newRow("Gauge") << "Gauge";
+ QTest::newRow("PieMenu") << "PieMenu";
+ QTest::newRow("DelayButton") << "DelayButton";
+ QTest::newRow("ToggleButton") << "ToggleButton";
+ QTest::newRow("Tumbler") << "Tumbler";
+}
+
+void tst_Paint::bounds()
+{
+ QFETCH(QString, name);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(QStringLiteral("import QtQuick.Extras 1.2; %1 { }").arg(name).toUtf8(), QUrl());
+ QQuickItem *control = qobject_cast<QQuickItem*>(component.create());
+ QVERIFY(control);
+
+ const int w = control->width();
+ const int h = control->height();
+ QVERIFY(w > 0);
+ QVERIFY(h > 0);
+
+ static const int margin = 10;
+ static const QColor bg = Qt::yellow;
+
+ QQuickWindow window;
+ window.setColor(bg);
+ window.resize(w + 2 * margin, h + 2 * margin);
+ control->setParentItem(window.contentItem());
+ control->setPosition(QPoint(margin, margin));
+ window.create();
+ window.show();
+
+ QTest::qWaitForWindowExposed(&window);
+
+ const QRect bounds(margin, margin, w, h);
+ const QImage image = window.grabWindow();
+
+ for (int x = 0; x < image.width(); ++x) {
+ for (int y = 0; y < image.height(); ++y) {
+ if (!bounds.contains(x, y)) {
+ const QByteArray msg = QString("painted outside bounds (%1,%2 %3x%4) at (%5,%6)").arg(bounds.x()).arg(bounds.y()).arg(bounds.width()).arg(bounds.height()).arg(x).arg(y).toUtf8();
+ const QColor px = image.pixel(x, y);
+ QVERIFY2(px == bg, msg);
+ }
+ }
+ }
+}
+
+QTEST_MAIN(tst_Paint)
+
+#include "tst_paint.moc"
diff --git a/tests/benchmarks/benchmarks.pro b/tests/benchmarks/benchmarks.pro
new file mode 100644
index 00000000..0453716e
--- /dev/null
+++ b/tests/benchmarks/benchmarks.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS = \
+ statusindicator
diff --git a/tests/benchmarks/statusindicator/LotsOfIndicatorsActive.qml b/tests/benchmarks/statusindicator/LotsOfIndicatorsActive.qml
new file mode 100644
index 00000000..ff8424f2
--- /dev/null
+++ b/tests/benchmarks/statusindicator/LotsOfIndicatorsActive.qml
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Extras 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$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtQuick.Extras 1.3
+
+Rectangle {
+ id: root
+ width: 240
+ height: 240
+ color: "#444"
+
+ Repeater {
+ model: 50
+
+ delegate: StatusIndicator {
+ x: Math.floor(index % 10) * 8
+ y: Math.floor(index / 10) * 8
+ width: 8
+ height: 8
+ color: Qt.rgba(36 / 255, 141 / 255, 254 / 255)
+ active: true
+ }
+ }
+}
diff --git a/tests/benchmarks/statusindicator/LotsOfIndicatorsInactive.qml b/tests/benchmarks/statusindicator/LotsOfIndicatorsInactive.qml
new file mode 100644
index 00000000..dd5a19a2
--- /dev/null
+++ b/tests/benchmarks/statusindicator/LotsOfIndicatorsInactive.qml
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Extras 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$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtQuick.Extras 1.3
+
+Rectangle {
+ id: root
+ width: 240
+ height: 240
+ color: "#444"
+
+ Repeater {
+ model: 50
+
+ delegate: StatusIndicator {
+ x: Math.floor(index % 10) * 8
+ y: Math.floor(index / 10) * 8
+ width: 8
+ height: 8
+ color: Qt.rgba(36 / 255, 141 / 255, 254 / 255)
+ active: false
+ }
+ }
+}
diff --git a/tests/benchmarks/statusindicator/statusindicator.pro b/tests/benchmarks/statusindicator/statusindicator.pro
new file mode 100644
index 00000000..d66b5515
--- /dev/null
+++ b/tests/benchmarks/statusindicator/statusindicator.pro
@@ -0,0 +1,13 @@
+TEMPLATE = app
+TARGET = tst_bench_statusindicator
+QT = core quick testlib
+
+SOURCES += \
+ tst_statusindicator.cpp
+
+OTHER_FILES += \
+ LotsOfIndicatorsOn.qml \
+ LotsOfIndicatorsOff.qml
+
+# Define SRCDIR equal to test's source directory
+DEFINES += SRCDIR=\\\"$$PWD\\\"
diff --git a/tests/benchmarks/statusindicator/tst_statusindicator.cpp b/tests/benchmarks/statusindicator/tst_statusindicator.cpp
new file mode 100644
index 00000000..3aa7678a
--- /dev/null
+++ b/tests/benchmarks/statusindicator/tst_statusindicator.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Extras 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 <QtTest/QtTest>
+#include <QtQml/QQmlComponent>
+#include <QtQml/QQmlEngine>
+#include <QtQuick/QQuickItem>
+#include <QtQuick/QQuickWindow>
+
+class tst_StatusIndicator: public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void benchmarkCreation_data();
+ void benchmarkCreation();
+};
+
+void tst_StatusIndicator::benchmarkCreation_data()
+{
+ QTest::addColumn<bool>("active");
+
+ QTest::newRow("active") << true;
+ QTest::newRow("inactive") << false;
+}
+
+void tst_StatusIndicator::benchmarkCreation()
+{
+ QFETCH(bool, active);
+
+ QQuickWindow window;
+ window.resize(240, 240);
+ window.create();
+
+ QQmlEngine engine;
+ // TODO: fix
+ QString path = QString::fromLatin1(SRCDIR "/LotsOfIndicators%1.qml").arg(active ? "Active" : "Inactive");
+ QQmlComponent component(&engine, QUrl::fromLocalFile(path));
+ QVERIFY2(!component.isError(), qPrintable(component.errorString()));
+ QVERIFY(component.isReady());
+ QBENCHMARK {
+ QQuickItem *root = qobject_cast<QQuickItem*>(component.create());
+ root->setParentItem(window.contentItem());
+ window.grabWindow();
+ }
+}
+
+QTEST_MAIN(tst_StatusIndicator)
+
+#include "tst_statusindicator.moc"
diff --git a/tests/tests.pro b/tests/tests.pro
index 85e4f3a5..86fcc232 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -1,2 +1,2 @@
TEMPLATE = subdirs
-SUBDIRS += auto
+SUBDIRS += auto benchmarks