aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/quickcontrols2/controls/data/tst_tumbler.qml')
-rw-r--r--tests/auto/quickcontrols2/controls/data/tst_tumbler.qml1281
1 files changed, 1281 insertions, 0 deletions
diff --git a/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml b/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml
new file mode 100644
index 0000000000..3a09bd9fe9
--- /dev/null
+++ b/tests/auto/quickcontrols2/controls/data/tst_tumbler.qml
@@ -0,0 +1,1281 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtTest
+import QtQuick.Controls
+
+TestCase {
+ id: testCase
+ width: 300
+ height: 300
+ visible: true
+ when: windowShown
+ name: "Tumbler"
+
+ property var tumbler: null
+ readonly property real implicitTumblerWidth: 60
+ readonly property real implicitTumblerHeight: 200
+ readonly property real defaultImplicitDelegateHeight: implicitTumblerHeight / 3
+ readonly property real defaultListViewTumblerOffset: -defaultImplicitDelegateHeight
+ readonly property real tumblerDelegateHeight: tumbler ? tumbler.availableHeight / tumbler.visibleItemCount : 0
+ property Item tumblerView: null
+
+ Component {
+ id: tumblerComponent
+
+ Tumbler {
+ visibleItemCount: 3
+ }
+ }
+
+ Component {
+ id: itemComponent
+
+ Item {
+ anchors.fill: parent
+ }
+ }
+
+ function createTumbler(args) {
+ tumbler = createTemporaryObject(tumblerComponent, testCase, args);
+ verify(tumbler, "Tumbler: failed to create an instance");
+ tumblerView = findView(tumbler);
+ verify(tumblerView);
+ }
+
+ function tumblerXCenter() {
+ return tumbler.leftPadding + tumbler.width / 2;
+ }
+
+ function tumblerYCenter() {
+ return tumbler.topPadding + tumbler.height / 2;
+ }
+
+ // visualItemIndex is from 0 to the amount of visible items.
+ function itemCenterPos(visualItemIndex) {
+ var halfDelegateHeight = tumblerDelegateHeight / 2;
+ var yCenter = tumbler.y + tumbler.topPadding + halfDelegateHeight
+ + (tumblerDelegateHeight * visualItemIndex);
+ return Qt.point(tumblerXCenter(), yCenter);
+ }
+
+ function itemTopLeftPos(visualItemIndex) {
+ return Qt.point(tumbler.leftPadding, tumbler.topPadding + (tumblerDelegateHeight * visualItemIndex));
+ }
+
+ function checkItemSizes() {
+ var contentChildren = tumbler.wrap ? tumblerView.children : tumblerView.contentItem.children;
+ verify(contentChildren.length >= tumbler.count);
+ for (var i = 0; i < contentChildren.length; ++i) {
+ compare(contentChildren[i].width, tumbler.availableWidth);
+ compare(contentChildren[i].height, tumblerDelegateHeight);
+ }
+ }
+
+ function findView(parent) {
+ for (var i = 0; i < parent.children.length; ++i) {
+ var child = parent.children[i];
+ if (child.hasOwnProperty("currentIndex")) {
+ return child;
+ }
+
+ var grandChild = findView(child);
+ if (grandChild)
+ return grandChild;
+ }
+
+ return null;
+ }
+
+ function findDelegateWithText(parent, text) {
+ for (var i = 0; i < parent.children.length; ++i) {
+ var child = parent.children[i];
+ if (child.hasOwnProperty("text") && child.text === text) {
+ return child;
+ }
+
+ var grandChild = findDelegateWithText(child, text);
+ if (grandChild)
+ return grandChild;
+ }
+
+ return null;
+ }
+
+ property Component noAttachedPropertiesDelegate: Text {
+ text: modelData
+ }
+
+ function test_wrapWithoutAttachedProperties() {
+ createTumbler();
+ verify(tumbler.wrap);
+
+ tumbler.delegate = noAttachedPropertiesDelegate;
+ // Shouldn't assert.
+ tumbler.wrap = false;
+ verify(findView(tumbler));
+ }
+
+ // TODO: test that currentIndex is maintained between contentItem changes...
+// function tst_dynamicContentItemChange() {
+// }
+
+ function test_currentIndex() {
+ createTumbler();
+ compare(tumbler.contentItem.parent, tumbler);
+
+ tumbler.model = 5;
+
+ compare(tumbler.currentIndex, 0);
+ waitForRendering(tumbler);
+
+ // Set it through user interaction.
+ var pos = Qt.point(tumblerXCenter(), tumbler.height / 2);
+ mouseDrag(tumbler, pos.x, pos.y, 0, tumbler.height / 3, Qt.LeftButton, Qt.NoModifier, 200);
+ tryCompare(tumblerView, "offset", 1);
+ compare(tumbler.currentIndex, 4);
+ compare(tumblerView.currentIndex, 4);
+
+ // Set it manually.
+ tumbler.currentIndex = 2;
+ tryCompare(tumbler, "currentIndex", 2);
+ compare(tumblerView.currentIndex, 2);
+
+ tumbler.model = null;
+ tryCompare(tumbler, "currentIndex", -1);
+ // PathView will only use 0 as the currentIndex when there are no items.
+ compare(tumblerView.currentIndex, 0);
+
+ tumbler.model = ["A", "B", "C"];
+ tryCompare(tumbler, "currentIndex", 0);
+
+ // Setting a negative current index should have no effect, because the model isn't empty.
+ tumbler.currentIndex = -1;
+ compare(tumbler.currentIndex, 0);
+
+ tumbler.model = 1;
+ compare(tumbler.currentIndex, 0);
+
+ tumbler.model = 5;
+ compare(tumbler.count, 5);
+ tumblerView = findView(tumbler);
+ tryCompare(tumblerView, "count", 5);
+ tumbler.currentIndex = 4;
+ compare(tumbler.currentIndex, 4);
+ compare(tumblerView.currentIndex, 4);
+
+ --tumbler.model;
+ compare(tumbler.count, 4);
+ compare(tumblerView.count, 4);
+ // Removing an item from an integer-based model will cause views to reset their currentIndex to 0.
+ compare(tumbler.currentIndex, 0);
+ compare(tumblerView.currentIndex, 0);
+
+ tumbler.model = 0;
+ compare(tumbler.currentIndex, -1);
+ }
+
+ Component {
+ id: currentIndexTumbler
+
+ Tumbler {
+ model: 5
+ currentIndex: 2
+ visibleItemCount: 3
+ }
+ }
+
+ Component {
+ id: currentIndexTumblerNoWrap
+
+ Tumbler {
+ model: 5
+ currentIndex: 2
+ wrap: false
+ visibleItemCount: 3
+ }
+ }
+
+ Component {
+ id: currentIndexTumblerNoWrapReversedOrder
+
+ Tumbler {
+ model: 5
+ wrap: false
+ currentIndex: 2
+ visibleItemCount: 3
+ }
+ }
+
+ Component {
+ id: negativeCurrentIndexTumblerNoWrap
+
+ Tumbler {
+ model: 5
+ wrap: false
+ currentIndex: -1
+ visibleItemCount: 3
+ }
+ }
+
+ Component {
+ id: currentIndexTooLargeTumbler
+
+ Tumbler {
+ objectName: "currentIndexTooLargeTumbler"
+ model: 10
+ currentIndex: 10
+ }
+ }
+
+
+ function test_currentIndexAtCreation_data() {
+ return [
+ { tag: "wrap: implicit, expected currentIndex: 2", currentIndex: 2, wrap: true, component: currentIndexTumbler },
+ { tag: "wrap: false, expected currentIndex: 2", currentIndex: 2, wrap: false, component: currentIndexTumblerNoWrap },
+ // Order of property assignments shouldn't matter
+ { tag: "wrap: false, expected currentIndex: 2, reversed property assignment order",
+ currentIndex: 2, wrap: false, component: currentIndexTumblerNoWrapReversedOrder },
+ { tag: "wrap: false, expected currentIndex: 0", currentIndex: 0, wrap: false, component: negativeCurrentIndexTumblerNoWrap },
+ { tag: "wrap: implicit, expected currentIndex: 0", currentIndex: 0, wrap: true, component: currentIndexTooLargeTumbler }
+ ]
+ }
+
+ function test_currentIndexAtCreation(data) {
+ // Test setting currentIndex at creation time
+ tumbler = createTemporaryObject(data.component, testCase);
+ verify(tumbler);
+ // A "statically declared" currentIndex will be pending until the count has changed,
+ // which happens when the model is set, which happens on the TumblerView's next polish.
+ tryCompare(tumbler, "currentIndex", data.currentIndex);
+
+ tumblerView = findView(tumbler);
+ tryVerify(function() { return tumblerView.currentItem });
+ compare(tumblerView.currentIndex, data.currentIndex);
+ compare(tumblerView.currentItem.text, data.currentIndex.toString());
+
+ if (data.wrap) {
+ tryCompare(tumblerView, "offset", data.currentIndex > 0 ? tumblerView.count - data.currentIndex : 0);
+ } else {
+ tryCompare(tumblerView, "contentY", tumblerDelegateHeight * data.currentIndex - tumblerView.preferredHighlightBegin);
+ }
+ }
+
+ function test_keyboardNavigation() {
+ createTumbler();
+
+ tumbler.model = 5;
+ tumbler.forceActiveFocus();
+ tumblerView.highlightMoveDuration = 0;
+
+ // Navigate upwards through entire wheel.
+ for (var j = 0; j < tumbler.count - 1; ++j) {
+ keyClick(Qt.Key_Up, Qt.NoModifier);
+ tryCompare(tumblerView, "offset", j + 1);
+ compare(tumbler.currentIndex, tumbler.count - 1 - j);
+ }
+
+ keyClick(Qt.Key_Up, Qt.NoModifier);
+ tryCompare(tumblerView, "offset", 0);
+ compare(tumbler.currentIndex, 0);
+
+ // Navigate downwards through entire wheel.
+ for (j = 0; j < tumbler.count - 1; ++j) {
+ keyClick(Qt.Key_Down, Qt.NoModifier);
+ tryCompare(tumblerView, "offset", tumbler.count - 1 - j);
+ compare(tumbler.currentIndex, j + 1);
+ }
+
+ keyClick(Qt.Key_Down, Qt.NoModifier);
+ tryCompare(tumblerView, "offset", 0);
+ compare(tumbler.currentIndex, 0);
+ }
+
+ function test_itemsCorrectlyPositioned() {
+ createTumbler();
+
+ tumbler.model = 4;
+ tumbler.height = 120;
+ compare(tumblerDelegateHeight, 40);
+ checkItemSizes();
+
+ wait(tumblerView.highlightMoveDuration);
+ var firstItemCenterPos = itemCenterPos(1);
+ var firstItem = tumblerView.itemAt(firstItemCenterPos.x, firstItemCenterPos.y);
+ var actualPos = testCase.mapFromItem(firstItem, 0, 0);
+ compare(actualPos.x, tumbler.leftPadding);
+ compare(actualPos.y, tumbler.topPadding + 40);
+
+ tumbler.forceActiveFocus();
+ keyClick(Qt.Key_Down);
+ tryCompare(tumblerView, "offset", 3.0);
+ tryCompare(tumbler, "moving", false);
+ firstItemCenterPos = itemCenterPos(0);
+ firstItem = tumblerView.itemAt(firstItemCenterPos.x, firstItemCenterPos.y);
+ verify(firstItem);
+ // Test QTBUG-40298.
+ actualPos = testCase.mapFromItem(firstItem, 0, 0);
+ fuzzyCompare(actualPos.x, tumbler.leftPadding, 0.0001);
+ fuzzyCompare(actualPos.y, tumbler.topPadding, 0.0001);
+
+ var secondItemCenterPos = itemCenterPos(1);
+ var secondItem = tumblerView.itemAt(secondItemCenterPos.x, secondItemCenterPos.y);
+ verify(secondItem);
+ verify(firstItem.y < secondItem.y);
+
+ var thirdItemCenterPos = itemCenterPos(2);
+ var thirdItem = tumblerView.itemAt(thirdItemCenterPos.x, thirdItemCenterPos.y);
+ verify(thirdItem);
+ verify(firstItem.y < thirdItem.y);
+ verify(secondItem.y < thirdItem.y);
+ }
+
+ function test_focusPastTumbler() {
+ tumbler = createTemporaryObject(tumblerComponent, testCase);
+ verify(tumbler);
+
+ var mouseArea = createTemporaryQmlObject(
+ "import QtQuick; TextInput { activeFocusOnTab: true; width: 50; height: 50 }", testCase, "");
+
+ tumbler.forceActiveFocus();
+ verify(tumbler.activeFocus);
+
+ keyClick(Qt.Key_Tab);
+ verify(!tumbler.activeFocus);
+ verify(mouseArea.activeFocus);
+ }
+
+ function test_datePicker() {
+ var component = Qt.createComponent("TumblerDatePicker.qml");
+ compare(component.status, Component.Ready, component.errorString());
+ tumbler = createTemporaryObject(component, testCase);
+ // Should not be any warnings.
+
+ tryCompare(tumbler.dayTumbler, "currentIndex", 0);
+ compare(tumbler.dayTumbler.count, 31);
+ compare(tumbler.monthTumbler.currentIndex, 0);
+ compare(tumbler.monthTumbler.count, 12);
+ compare(tumbler.yearTumbler.currentIndex, 0);
+ tryCompare(tumbler.yearTumbler, "count", 100);
+
+ verify(findView(tumbler.dayTumbler).children.length >= tumbler.dayTumbler.visibleItemCount);
+ verify(findView(tumbler.monthTumbler).children.length >= tumbler.monthTumbler.visibleItemCount);
+ // TODO: do this properly somehow
+ wait(100);
+ verify(findView(tumbler.yearTumbler).children.length >= tumbler.yearTumbler.visibleItemCount);
+
+ // March.
+ tumbler.monthTumbler.currentIndex = 2;
+ tryCompare(tumbler.monthTumbler, "currentIndex", 2);
+
+ // 30th of March.
+ tumbler.dayTumbler.currentIndex = 29;
+ tryCompare(tumbler.dayTumbler, "currentIndex", 29);
+
+ // February.
+ tumbler.monthTumbler.currentIndex = 1;
+ tryCompare(tumbler.monthTumbler, "currentIndex", 1);
+ tryCompare(tumbler.dayTumbler, "currentIndex", 27);
+ }
+
+ Component {
+ id: timePickerComponent
+
+ Row {
+ property alias minuteTumbler: minuteTumbler
+ property alias amPmTumbler: amPmTumbler
+
+ Tumbler {
+ id: minuteTumbler
+ currentIndex: 6
+ model: 60
+ width: 50
+ height: 150
+ }
+
+ Tumbler {
+ id: amPmTumbler
+ model: ["AM", "PM"]
+ width: 50
+ height: 150
+ contentItem: ListView {
+ anchors.fill: parent
+ model: amPmTumbler.model
+ delegate: amPmTumbler.delegate
+ }
+ }
+ }
+ }
+
+ function test_listViewTimePicker() {
+ var root = createTemporaryObject(timePickerComponent, testCase);
+ verify(root);
+
+ mouseDrag(root.minuteTumbler, root.minuteTumbler.width / 2, root.minuteTumbler.height / 2, 0, 50);
+ // Shouldn't crash.
+ mouseDrag(root.amPmTumbler, root.amPmTumbler.width / 2, root.amPmTumbler.height / 2, 0, 50);
+ }
+
+ function test_displacement_data() {
+ var data = [
+ // At 0 offset, the first item is current.
+ { count: 6, index: 0, offset: 0, expectedDisplacement: 0 },
+ { count: 6, index: 1, offset: 0, expectedDisplacement: -1 },
+ { count: 6, index: 5, offset: 0, expectedDisplacement: 1 },
+ // When we start to move the first item down, the second item above it starts to become current.
+ { count: 6, index: 0, offset: 0.25, expectedDisplacement: -0.25 },
+ { count: 6, index: 1, offset: 0.25, expectedDisplacement: -1.25 },
+ { count: 6, index: 5, offset: 0.25, expectedDisplacement: 0.75 },
+ { count: 6, index: 0, offset: 0.5, expectedDisplacement: -0.5 },
+ { count: 6, index: 1, offset: 0.5, expectedDisplacement: -1.5 },
+ { count: 6, 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.
+ { count: 6, index: 0, offset: 0.75, expectedDisplacement: -0.75 },
+ { count: 6, index: 5, offset: 0.75, expectedDisplacement: 0.25 },
+ { count: 6, index: 0, offset: 4.75, expectedDisplacement: 1.25 },
+ { count: 6, index: 1, offset: 4.75, expectedDisplacement: 0.25 },
+ { count: 6, index: 0, offset: 4.5, expectedDisplacement: 1.5 },
+ { count: 6, index: 1, offset: 4.5, expectedDisplacement: 0.5 },
+ { count: 6, index: 0, offset: 4.25, expectedDisplacement: 1.75 },
+ { count: 6, index: 1, offset: 4.25, expectedDisplacement: 0.75 },
+ // count == visibleItemCount
+ { count: 3, index: 0, offset: 0, expectedDisplacement: 0 },
+ { count: 3, index: 1, offset: 0, expectedDisplacement: -1 },
+ { count: 3, index: 2, offset: 0, expectedDisplacement: 1 },
+ // count < visibleItemCount
+ { count: 2, index: 0, offset: 0, expectedDisplacement: 0 },
+ { count: 2, index: 1, offset: 0, expectedDisplacement: 1 },
+ // count == 1
+ { count: 1, index: 0, offset: 0, expectedDisplacement: 0 }
+ ];
+ 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;
+ }
+
+ property Component displacementDelegate: Text {
+ objectName: "delegate" + index
+ text: modelData
+ opacity: 0.2 + Math.max(0, 1 - Math.abs(Tumbler.displacement)) * 0.8
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+
+ Text {
+ text: parent.displacement.toFixed(2)
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ }
+
+ property real displacement: Tumbler.displacement
+ }
+
+ function test_displacement(data) {
+ createTumbler();
+
+ // TODO: test setting these in the opposite order (delegate after model
+ // doesn't seem to cause a change in delegates in PathView)
+ tumbler.wrap = true;
+ tumbler.delegate = displacementDelegate;
+ tumbler.model = data.count;
+ compare(tumbler.count, data.count);
+
+ var delegate = findChild(tumblerView, "delegate" + data.index);
+ verify(delegate);
+
+ tumblerView.offset = data.offset;
+ compare(delegate.displacement, data.expectedDisplacement);
+
+ // test displacement after adding and removing items
+ }
+
+ function test_wrap() {
+ createTumbler();
+
+ tumbler.model = 5;
+ compare(tumbler.count, 5);
+
+ tumbler.currentIndex = 2;
+ compare(tumblerView.currentIndex, 2);
+
+ tumbler.wrap = false;
+ tumblerView = findView(tumbler);
+ compare(tumbler.count, 5);
+ compare(tumbler.currentIndex, 2);
+ // Tumbler's count hasn't changed (the model hasn't changed),
+ // but the new view needs time to instantiate its items.
+ tryCompare(tumblerView, "count", 5);
+ compare(tumblerView.currentIndex, 2);
+ }
+
+ Component {
+ id: twoItemTumbler
+
+ Tumbler {
+ model: 2
+ }
+ }
+
+ Component {
+ id: tenItemTumbler
+
+ Tumbler {
+ model: 10
+ }
+ }
+
+ function test_countWrap() {
+ tumbler = createTemporaryObject(tumblerComponent, testCase);
+ verify(tumbler);
+
+ // Check that a count that is less than visibleItemCount results in wrap being set to false.
+ verify(2 < tumbler.visibleItemCount);
+ tumbler.model = 2;
+ compare(tumbler.count, 2);
+ compare(tumbler.wrap, false);
+ }
+
+ function test_explicitlyNonwrapping() {
+ // Check that explicitly setting wrap to false works even when it was implicitly false.
+ var explicitlyNonWrapping = createTemporaryObject(twoItemTumbler, testCase);
+ verify(explicitlyNonWrapping);
+ tryCompare(explicitlyNonWrapping, "wrap", false);
+
+ explicitlyNonWrapping.wrap = false;
+ // wrap shouldn't be set to true now that there are more items than there are visible ones.
+ verify(10 > explicitlyNonWrapping.visibleItemCount);
+ explicitlyNonWrapping.model = 10;
+ compare(explicitlyNonWrapping.wrap, false);
+
+ // Test resetting wrap back to the default behavior.
+ explicitlyNonWrapping.wrap = undefined;
+ compare(explicitlyNonWrapping.wrap, true);
+ }
+
+ function test_explicitlyWrapping() {
+ // Check that explicitly setting wrap to true works even when it was implicitly true.
+ var explicitlyWrapping = createTemporaryObject(tenItemTumbler, testCase);
+ verify(explicitlyWrapping);
+ compare(explicitlyWrapping.wrap, true);
+
+ explicitlyWrapping.wrap = true;
+ // wrap shouldn't be set to false now that there are more items than there are visible ones.
+ explicitlyWrapping.model = 2;
+ compare(explicitlyWrapping.wrap, true);
+
+ // Test resetting wrap back to the default behavior.
+ explicitlyWrapping.wrap = undefined;
+ compare(explicitlyWrapping.wrap, false);
+ }
+
+ Component {
+ id: customListViewTumblerComponent
+
+ Tumbler {
+ id: listViewTumbler
+
+ contentItem: ListView {
+ anchors.fill: parent
+ model: listViewTumbler.model
+ delegate: listViewTumbler.delegate
+
+ snapMode: ListView.SnapToItem
+ highlightRangeMode: ListView.StrictlyEnforceRange
+ preferredHighlightBegin: height / 2 - (height / listViewTumbler.visibleItemCount / 2)
+ preferredHighlightEnd: height / 2 + (height / listViewTumbler.visibleItemCount / 2)
+ clip: true
+ }
+ }
+ }
+
+ Component {
+ id: customPathViewTumblerComponent
+
+ Tumbler {
+ id: pathViewTumbler
+
+ contentItem: PathView {
+ id: pathView
+ model: pathViewTumbler.model
+ delegate: pathViewTumbler.delegate
+ clip: true
+ pathItemCount: pathViewTumbler.visibleItemCount + 1
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+ dragMargin: width / 2
+
+ path: Path {
+ startX: pathView.width / 2
+ startY: -pathView.delegateHeight / 2
+ PathLine {
+ x: pathView.width / 2
+ y: pathView.pathItemCount * pathView.delegateHeight - pathView.delegateHeight / 2
+ }
+ }
+
+ property real delegateHeight: pathViewTumbler.availableHeight / pathViewTumbler.visibleItemCount
+ }
+ }
+ }
+
+ function test_customContentItemAtConstruction_data() {
+ return [
+ { tag: "ListView", component: customListViewTumblerComponent },
+ { tag: "PathView", component: customPathViewTumblerComponent }
+ ];
+ }
+
+ function test_customContentItemAtConstruction(data) {
+ var tumbler = createTemporaryObject(data.component, testCase);
+ // Shouldn't assert.
+
+ tumbler.model = 5;
+ compare(tumbler.count, 5);
+
+ tumbler.currentIndex = 2;
+ var tumblerView = findView(tumbler);
+ compare(tumblerView.currentIndex, 2);
+
+ tumblerView.incrementCurrentIndex();
+ compare(tumblerView.currentIndex, 3);
+ compare(tumbler.currentIndex, 3);
+
+ // Shouldn't have any affect.
+ tumbler.wrap = false;
+ compare(tumbler.count, 5);
+ compare(tumblerView.currentIndex, 3);
+ compare(tumbler.currentIndex, 3);
+ }
+
+ function findFirstDelegateWithText(view, text) {
+ var delegate = null;
+ var contentItem = view.hasOwnProperty("contentItem") ? view.contentItem : view;
+ for (var i = 0; i < contentItem.children.length && !delegate; ++i) {
+ var child = contentItem.children[i];
+ if (child.hasOwnProperty("text") && child.text === text)
+ delegate = child;
+ }
+ return delegate;
+ }
+
+ function test_customContentItemAfterConstruction_data() {
+ return [
+ { tag: "ListView", componentPath: "TumblerListView.qml" },
+ { tag: "PathView", componentPath: "TumblerPathView.qml" }
+ ];
+ }
+
+ function test_customContentItemAfterConstruction(data) {
+ createTumbler();
+
+ tumbler.model = 5;
+ compare(tumbler.count, 5);
+
+ tumbler.currentIndex = 2;
+ compare(tumblerView.currentIndex, 2);
+
+ var contentItemComponent = Qt.createComponent(data.componentPath);
+ compare(contentItemComponent.status, Component.Ready);
+
+ var customContentItem = createTemporaryObject(contentItemComponent, tumbler);
+ tumbler.contentItem = customContentItem;
+ compare(tumbler.count, 5);
+ tumblerView = findView(tumbler);
+ compare(tumblerView.currentIndex, 2);
+
+ var delegate = findFirstDelegateWithText(tumblerView, "Custom2");
+ verify(delegate);
+ compare(delegate.height, defaultImplicitDelegateHeight);
+ tryCompare(delegate.Tumbler, "displacement", 0);
+
+ tumblerView.incrementCurrentIndex();
+ compare(tumblerView.currentIndex, 3);
+ compare(tumbler.currentIndex, 3);
+ }
+
+ function test_displacementListView_data() {
+ var offset = defaultListViewTumblerOffset;
+
+ var data = [
+ // At 0 contentY, the first item is current.
+ { contentY: offset, expectedDisplacements: [
+ { index: 0, displacement: 0 },
+ { index: 1, displacement: -1 },
+ { index: 2, displacement: -2 } ]
+ },
+ // When we start to move the first item down, the second item above it starts to become current.
+ { contentY: offset + defaultImplicitDelegateHeight * 0.25, expectedDisplacements: [
+ { index: 0, displacement: 0.25 },
+ { index: 1, displacement: -0.75 },
+ { index: 2, displacement: -1.75 } ]
+ },
+ { contentY: offset + defaultImplicitDelegateHeight * 0.5, expectedDisplacements: [
+ { index: 0, displacement: 0.5 },
+ { index: 1, displacement: -0.5 },
+ { index: 2, displacement: -1.5 } ]
+ },
+ { contentY: offset + defaultImplicitDelegateHeight * 0.75, expectedDisplacements: [
+ { index: 0, displacement: 0.75 },
+ { index: 1, displacement: -0.25 } ]
+ },
+ { contentY: offset + defaultImplicitDelegateHeight * 3.5, expectedDisplacements: [
+ { index: 3, displacement: 0.5 },
+ { index: 4, displacement: -0.5 } ]
+ }
+ ];
+ for (var i = 0; i < data.length; ++i) {
+ var row = data[i];
+ row.tag = "contentY=" + row.contentY;
+ }
+ return data;
+ }
+
+ function test_displacementListView(data) {
+ createTumbler();
+
+ tumbler.wrap = false;
+ tumbler.delegate = displacementDelegate;
+ tumbler.model = 5;
+ compare(tumbler.count, 5);
+ // Ensure assumptions about the tumbler used in our data() function are correct.
+ tumblerView = findView(tumbler);
+ compare(tumblerView.contentY, -defaultImplicitDelegateHeight);
+ var delegateCount = 0;
+ var listView = tumblerView;
+ var listViewContentItem = tumblerView.contentItem;
+
+ // We use the mouse instead of setting contentY directly, otherwise the
+ // items snap back into place. This doesn't seem to be an issue for
+ // PathView for some reason.
+ //
+ // I tried lots of things to get this test to work with small changes
+ // in ListView's contentY (to match the tests for a PathView-based Tumbler), but they didn't work:
+ //
+ // - Pressing once and then directly moving the mouse to the correct location
+ // - Pressing once and interpolating the mouse position to the correct location
+ // - Pressing once and doing some dragging up and down to trigger the
+ // overThreshold of QQuickFlickable
+ //
+ // Even after the last item above, QQuickFlickable wouldn't consider it a drag.
+ // It seems that overThreshold is set too late, and because the drag distance is quite small
+ // to begin with, nothing changes (the displacement was always very close to 0 in the end).
+
+ // Ensure that we at least cover the distance required to reach the desired contentY.
+ var distanceToReachContentY = data.contentY - defaultListViewTumblerOffset;
+ var distance = Math.abs(distanceToReachContentY) + tumbler.height / 2;
+ // If distanceToReachContentY is 0, we're testing 0 displacement, so we don't need to do anything.
+ if (distanceToReachContentY != 0) {
+ mousePress(tumbler, tumblerXCenter(), tumblerYCenter());
+
+ var dragDirection = distanceToReachContentY > 0 ? -1 : 1;
+ for (var i = 0; i < distance && Math.floor(listView.contentY) !== Math.floor(data.contentY); ++i) {
+ mouseMove(tumbler, tumblerXCenter(), tumblerYCenter() + i * dragDirection);
+ wait(1); // because Flickable pays attention to velocity, we need some time between movements (qtdeclarative ebf07c3)
+ }
+ }
+
+ for (var i = 0; i < data.expectedDisplacements.length; ++i) {
+ var delegate = findChild(listViewContentItem, "delegate" + data.expectedDisplacements[i].index);
+ verify(delegate);
+ compare(delegate.height, defaultImplicitDelegateHeight);
+ // Due to the way we must perform this test, we can't expect high precision.
+ var expectedDisplacement = data.expectedDisplacements[i].displacement;
+ fuzzyCompare(delegate.displacement, expectedDisplacement, 0.1,
+ "Delegate of ListView-based Tumbler at index " + data.expectedDisplacements[i].index
+ + " has displacement of " + delegate.displacement + " when it should be " + expectedDisplacement);
+ }
+
+ if (distanceToReachContentY != 0)
+ mouseRelease(tumbler, tumblerXCenter(), itemCenterPos(1) + (data.contentY - defaultListViewTumblerOffset), Qt.LeftButton);
+ }
+
+ function test_listViewFlickAboveBounds_data() {
+ // Tests that flicking above the bounds when already at the top of the
+ // tumbler doesn't result in an incorrect displacement.
+ var data = [];
+ // Less than two items doesn't make sense. The default visibleItemCount
+ // is 3, so we test a bit more than double that.
+ for (var i = 2; i <= 7; ++i) {
+ data.push({ tag: i + " items", model: i });
+ }
+ return data;
+ }
+
+ function test_listViewFlickAboveBounds(data) {
+ createTumbler();
+
+ tumbler.wrap = false;
+ tumbler.delegate = displacementDelegate;
+ tumbler.model = data.model;
+ tumblerView = findView(tumbler);
+
+ mousePress(tumbler, tumblerXCenter(), tumblerYCenter());
+
+ // Ensure it's stationary.
+ var listView = tumblerView;
+ compare(listView.contentY, defaultListViewTumblerOffset);
+
+ // We could just move up until the contentY changed, but this is safer.
+ var distance = tumbler.height;
+ var changed = false;
+
+ for (var i = 0; i < distance && !changed; ++i) {
+ mouseMove(tumbler, tumblerXCenter(), tumblerYCenter() + i, 10);
+
+ // Don't test until the contentY has actually changed.
+ if (Math.abs(listView.contentY) - listView.preferredHighlightBegin > 0.01) {
+
+ for (var delegateIndex = 0; delegateIndex < Math.min(tumbler.count, tumbler.visibleItemCount); ++delegateIndex) {
+ var delegate = findChild(listView.contentItem, "delegate" + delegateIndex);
+ verify(delegate);
+
+ verify(delegate.displacement <= -delegateIndex, "Delegate at index " + delegateIndex + " has a displacement of "
+ + delegate.displacement + " when it should be less than or equal to " + -delegateIndex);
+ verify(delegate.displacement > -delegateIndex - 0.1, "Delegate at index 0 has a displacement of "
+ + delegate.displacement + " when it should be greater than ~ " + -delegateIndex - 0.1);
+ }
+
+ changed = true;
+ }
+ }
+
+ // Sanity check that something was actually tested.
+ verify(changed);
+
+ mouseRelease(tumbler, tumblerXCenter(), tumbler.topPadding);
+ }
+
+ property Component objectNameDelegate: Text {
+ objectName: "delegate" + index
+ text: modelData
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ 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) {
+ createTumbler();
+
+ tumbler.delegate = objectNameDelegate;
+ tumbler.visibleItemCount = data.visibleItemCount;
+
+ tumbler.model = data.model;
+ compare(tumbler.count, data.model);
+
+ for (var delegateIndex = 0; delegateIndex < data.visibleItemCount; ++delegateIndex) {
+ if (data.expectedYPositions.hasOwnProperty(delegateIndex)) {
+ var delegate = findChild(tumblerView, "delegate" + delegateIndex);
+ verify(delegate, "Delegate found at index " + delegateIndex);
+ var expectedYPos = data.expectedYPositions[delegateIndex] * tumblerDelegateHeight;
+ compare(delegate.mapToItem(tumbler.contentItem, 0, 0).y, expectedYPos);
+ }
+ }
+ }
+
+ property Component wrongDelegateTypeComponent: QtObject {
+ property real displacement: Tumbler.displacement
+ }
+
+ property Component noParentDelegateComponent: Item {
+ property real displacement: Tumbler.displacement
+ }
+
+ function test_attachedProperties() {
+ tumbler = createTemporaryObject(tumblerComponent, testCase);
+ verify(tumbler);
+
+ // TODO: crashes somewhere in QML's guts
+// tumbler.model = 5;
+// tumbler.delegate = wrongDelegateTypeComponent;
+// ignoreWarning("Attached properties of Tumbler must be accessed from within a delegate item");
+// // Cause displacement to be changed. The warning isn't triggered if we don't do this.
+// tumbler.contentItem.offset += 1;
+
+ ignoreWarning("Tumbler: attached properties must be accessed through a delegate item that has a parent");
+ createTemporaryObject(noParentDelegateComponent, null);
+
+ ignoreWarning("Tumbler: attempting to access attached property on item without an \"index\" property");
+ var object = createTemporaryObject(noParentDelegateComponent, testCase);
+ verify(object);
+ }
+
+ property Component paddingDelegate: Text {
+ objectName: "delegate" + index
+ text: modelData
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+
+ Rectangle {
+ anchors.fill: parent
+ color: "transparent"
+ border.color: "red"
+ border.width: 1
+ }
+ }
+
+ function test_padding_data() {
+ var data = [];
+
+ data.push({ padding: 0 });
+ data.push({ padding: 10 });
+ data.push({ left: 10, top: 10 });
+ data.push({ right: 10, bottom: 10 });
+
+ for (var i = 0; i < data.length; ++i) {
+ var tag = "";
+
+ if (data[i].padding !== undefined)
+ tag += "padding: " + data[i].padding + " ";
+ if (data[i].left !== undefined)
+ tag += "left: " + data[i].left + " ";
+ if (data[i].right !== undefined)
+ tag += "right: " + data[i].right + " ";
+ if (data[i].top !== undefined)
+ tag += "top: " + data[i].top + " ";
+ if (data[i].bottom !== undefined)
+ tag += "bottom: " + data[i].bottom + " ";
+ tag = tag.slice(0, -1);
+
+ data[i].tag = tag;
+ }
+
+ return data;
+ }
+
+ function test_padding(data) {
+ createTumbler();
+
+ tumbler.delegate = paddingDelegate;
+ tumbler.model = 5;
+ compare(tumbler.padding, 0);
+ compare(tumbler.leftPadding, 0);
+ compare(tumbler.rightPadding, 0);
+ compare(tumbler.topPadding, 0);
+ compare(tumbler.bottomPadding, 0);
+ compare(tumbler.contentItem.x, 0);
+ compare(tumbler.contentItem.y, 0);
+
+ if (data.padding !== undefined)
+ tumbler.padding = data.padding;
+ if (data.left !== undefined)
+ tumbler.leftPadding = data.left;
+ if (data.right !== undefined)
+ tumbler.rightPadding = data.right;
+ if (data.top !== undefined)
+ tumbler.topPadding = data.top;
+ if (data.bottom !== undefined)
+ tumbler.bottomPadding = data.bottom;
+
+ compare(tumbler.availableWidth, tumbler.implicitWidth - tumbler.leftPadding - tumbler.rightPadding);
+ compare(tumbler.availableHeight, tumbler.implicitHeight - tumbler.topPadding - tumbler.bottomPadding);
+ compare(tumbler.contentItem.x, tumbler.leftPadding);
+ compare(tumbler.contentItem.y, tumbler.topPadding);
+
+ var pathView = tumbler.contentItem;
+ var expectedDelegateHeight = tumbler.availableHeight / tumbler.visibleItemCount;
+ var itemIndicesInVisualOrder = [4, 0, 1];
+ for (var i = 0; i < itemIndicesInVisualOrder.length; ++i) {
+ var delegate = findChild(pathView, "delegate" + itemIndicesInVisualOrder[i]);
+ verify(delegate, "Couldn't find delegate at index " + itemIndicesInVisualOrder[i]
+ + " (iteration " + i + " out of " + (pathView.children.length - 1) + ")");
+
+ compare(delegate.width, tumbler.availableWidth);
+ compare(delegate.height, expectedDelegateHeight);
+
+ var expectedY = tumbler.topPadding + i * expectedDelegateHeight;
+ var mappedPos = delegate.mapToItem(null, delegate.width / 2, 0);
+ fuzzyCompare(mappedPos.y, expectedY, 0.5,
+ "Tumbler's PathView delegate at index " + itemIndicesInVisualOrder[i]
+ + " should have a y pos of " + expectedY + ", but it's actually " + mappedPos.y.toFixed(20));
+
+ var expectedX = tumbler.leftPadding;
+ compare(delegate.mapToItem(null, 0, 0).x, expectedX,
+ "Tumbler's PathView delegate at index " + itemIndicesInVisualOrder[i]
+ + " should have a x pos of " + expectedX + ", but it's actually " + mappedPos.x.toFixed(20));
+ }
+
+ // Force new items to be created, as there was a bug where the path was correct until this happened.
+ compare(tumblerView.offset, 0);
+ ++tumbler.currentIndex;
+ tryCompare(tumblerView, "offset", 4, tumblerView.highlightMoveDuration * 2);
+ }
+
+ function test_moving_data() {
+ return [
+ { tag: "wrap:true", wrap: true },
+ { tag: "wrap:false", wrap: false }
+ ]
+ }
+
+ function test_moving(data) {
+ createTumbler({wrap: data.wrap, model: 5})
+ compare(tumbler.wrap, data.wrap)
+ compare(tumbler.moving, false)
+
+ waitForRendering(tumbler)
+
+ mousePress(tumbler, tumbler.width / 2, tumbler.height / 2, Qt.LeftButton)
+ compare(tumbler.moving, false)
+
+ for (var y = tumbler.height / 2; y >= tumbler.height / 4; y -= 10)
+ mouseMove(tumbler, tumbler.width / 2, y, 1)
+ compare(tumbler.moving, true)
+
+ mouseRelease(tumbler, tumbler.width / 2, tumbler.height / 4, Qt.LeftButton)
+ compare(tumbler.moving, true)
+ tryCompare(tumbler, "moving", false)
+ }
+
+ Component {
+ id: qtbug61374Component
+
+ Row {
+ property alias tumbler: tumbler
+ property alias label: label
+
+ Component.onCompleted: {
+ tumbler.currentIndex = 2
+ }
+
+ Tumbler {
+ id: tumbler
+ model: 5
+ // ...
+ }
+
+ Label {
+ id: label
+ text: tumbler.currentItem.text
+ }
+ }
+ }
+
+ function test_qtbug61374() {
+ var row = createTemporaryObject(qtbug61374Component, testCase);
+ verify(row);
+
+ var tumbler = row.tumbler;
+ tryCompare(tumbler, "currentIndex", 2);
+
+ tumblerView = findView(tumbler);
+
+ var label = row.label;
+ compare(label.text, "2");
+ }
+
+ function test_positionViewAtIndex_data() {
+ return [
+ // Should be 20, 21, ... but there is a documented limitation for this in positionViewAtIndex()'s docs.
+ { tag: "wrap=true, mode=Beginning", wrap: true, mode: Tumbler.Beginning, expectedVisibleIndices: [21, 22, 23, 24, 25] },
+ { tag: "wrap=true, mode=Center", wrap: true, mode: Tumbler.Center, expectedVisibleIndices: [18, 19, 20, 21, 22] },
+ { tag: "wrap=true, mode=End", wrap: true, mode: Tumbler.End, expectedVisibleIndices: [16, 17, 18, 19, 20] },
+ // Same as Beginning; should start at 20.
+ { tag: "wrap=true, mode=Contain", wrap: true, mode: Tumbler.Contain, expectedVisibleIndices: [21, 22, 23, 24, 25] },
+ { tag: "wrap=true, mode=SnapPosition", wrap: true, mode: Tumbler.SnapPosition, expectedVisibleIndices: [18, 19, 20, 21, 22] },
+ { tag: "wrap=false, mode=Beginning", wrap: false, mode: Tumbler.Beginning, expectedVisibleIndices: [20, 21, 22, 23, 24] },
+ { tag: "wrap=false, mode=Center", wrap: false, mode: Tumbler.Center, expectedVisibleIndices: [18, 19, 20, 21, 22] },
+ { tag: "wrap=false, mode=End", wrap: false, mode: Tumbler.End, expectedVisibleIndices: [16, 17, 18, 19, 20] },
+ { tag: "wrap=false, mode=Visible", wrap: false, mode: Tumbler.Visible, expectedVisibleIndices: [16, 17, 18, 19, 20] },
+ { tag: "wrap=false, mode=Contain", wrap: false, mode: Tumbler.Contain, expectedVisibleIndices: [16, 17, 18, 19, 20] },
+ { tag: "wrap=false, mode=SnapPosition", wrap: false, mode: Tumbler.SnapPosition, expectedVisibleIndices: [18, 19, 20, 21, 22] }
+ ]
+ }
+
+ function test_positionViewAtIndex(data) {
+ createTumbler({ wrap: data.wrap, model: 40, visibleItemCount: 5 })
+ compare(tumbler.wrap, data.wrap)
+
+ waitForRendering(tumbler)
+
+ tumbler.positionViewAtIndex(20, data.mode)
+ tryCompare(tumbler, "moving", false)
+
+ compare(tumbler.visibleItemCount, 5)
+ for (var i = 0; i < 5; ++i) {
+ // Find the item through its text, as that's easier than child/itemAt().
+ var text = data.expectedVisibleIndices[i].toString()
+ var item = findDelegateWithText(tumblerView, text)
+ verify(item, "found no item with text \"" + text + "\"")
+ compare(item.text, data.expectedVisibleIndices[i].toString())
+
+ // Ensure that it's at the position we expect.
+ var expectedPos = itemTopLeftPos(i)
+ var actualPos = testCase.mapFromItem(item, 0, 0)
+ compare(actualPos.x, expectedPos.x, "expected delegate with text " + item.text
+ + " to have an x pos of " + expectedPos.x + " but it was " + actualPos.x)
+ compare(actualPos.y, expectedPos.y, "expected delegate with text " + item.text
+ + " to have an y pos of " + expectedPos.y + " but it was " + actualPos.y)
+ }
+ }
+
+ Component {
+ id: setCurrentIndexOnImperativeModelChangeComponent
+
+ Tumbler {
+ onModelChanged: currentIndex = model - 2
+ }
+ }
+
+ function test_setCurrentIndexOnImperativeModelChange() {
+ var tumbler = createTemporaryObject(setCurrentIndexOnImperativeModelChangeComponent, testCase);
+ verify(tumbler);
+
+ tumbler.model = 4
+ compare(tumbler.count, 4);
+ tumblerView = findView(tumbler);
+ tryCompare(tumblerView, "count", 4);
+
+ // 4 - 2 = 2
+ compare(tumbler.currentIndex, 2);
+
+ ++tumbler.model;
+ compare(tumbler.count, 5);
+ compare(tumbler.wrap, true);
+ tumblerView = findView(tumbler);
+ tryCompare(tumblerView, "count", 5);
+ // 5 - 2 = 3
+ compare(tumbler.currentIndex, 3);
+ }
+
+ Component {
+ id: setCurrentIndexOnDeclarativeModelChangeComponent
+
+ Item {
+ property alias tumbler: tumbler
+
+ property int setting: 4
+
+ Tumbler {
+ id: tumbler
+ model: setting
+ onModelChanged: currentIndex = model - 2
+ }
+ }
+ }
+
+ function test_setCurrentIndexOnDeclarativeModelChange() {
+ var root = createTemporaryObject(setCurrentIndexOnDeclarativeModelChangeComponent, testCase);
+ verify(root);
+
+ var tumbler = root.tumbler;
+ compare(tumbler.count, 4);
+ compare(tumbler.wrap, false);
+ tumblerView = findView(tumbler);
+ tryCompare(tumblerView, "count", 4);
+ // 4 - 2 = 2
+ compare(tumbler.currentIndex, 2);
+
+ ++root.setting;
+ compare(tumbler.count, 5);
+ compare(tumbler.wrap, true);
+ tumblerView = findView(tumbler);
+ tryCompare(tumblerView, "count", 5);
+ // 5 - 2 = 3
+ compare(tumbler.currentIndex, 3);
+ }
+
+ function test_displacementAfterResizing() {
+ createTumbler({
+ width: 200,
+ wrap: false,
+ delegate: displacementDelegate,
+ model: 30,
+ visibleItemCount: 7,
+ currentIndex: 15
+ })
+
+ var delegate = findChild(tumblerView, "delegate15")
+ verify(delegate)
+
+ tryCompare(delegate, "displacement", 0)
+
+ // Resizing the Tumbler shouldn't affect the displacement.
+ tumbler.height *= 1.4
+ tryCompare(delegate, "displacement", 0)
+ }
+
+ //QTBUG-84426
+ Component {
+ id: initialCurrentIndexTumbler
+
+ Tumbler {
+ anchors.centerIn: parent
+ width: 60
+ height: 200
+ delegate: Text {text: modelData}
+ model: 10
+ currentIndex: 4
+ }
+ }
+
+ function test_initialCurrentIndex() {
+ var tumbler = createTemporaryObject(initialCurrentIndexTumbler, testCase, {wrap: true});
+ compare(tumbler.currentIndex, 4);
+ tumbler = createTemporaryObject(initialCurrentIndexTumbler, testCase, {wrap: false});
+ compare(tumbler.currentIndex, 4);
+ }
+}