diff options
author | Mitch Curtis <mitch.curtis@theqtcompany.com> | 2015-02-12 13:55:22 +0100 |
---|---|---|
committer | J-P Nurmi <jpnurmi@theqtcompany.com> | 2015-02-13 12:01:33 +0000 |
commit | 6f15c206b069ed0fcf48a285bfcc4ad636927df0 (patch) | |
tree | 6150e262bc2d049219981a4cb1e1deeeeed5d636 /tests/auto/extras/data | |
parent | d28a02aec9a1632f2263d9276099454b33fb6741 (diff) | |
download | qtquickcontrols-6f15c206b069ed0fcf48a285bfcc4ad636927df0.tar.gz |
Import Qt Quick Extras (the former Qt Quick Enterprise Controls)
Change-Id: I59c5c97c564f707da4ce617e25e13ff8124f7d4b
Reviewed-by: J-P Nurmi <jpnurmi@theqtcompany.com>
Diffstat (limited to 'tests/auto/extras/data')
21 files changed, 3391 insertions, 0 deletions
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 Binary files differnew file mode 100644 index 00000000..e408aca2 --- /dev/null +++ b/tests/auto/extras/data/picture.dat 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); + } + } + } + } +} |