| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:BSD$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** BSD License Usage |
| ** Alternatively, you may use this file under the terms of the BSD license |
| ** as follows: |
| ** |
| ** "Redistribution and use in source and binary forms, with or without |
| ** modification, are permitted provided that the following conditions are |
| ** met: |
| ** * Redistributions of source code must retain the above copyright |
| ** notice, this list of conditions and the following disclaimer. |
| ** * Redistributions in binary form must reproduce the above copyright |
| ** notice, this list of conditions and the following disclaimer in |
| ** the documentation and/or other materials provided with the |
| ** distribution. |
| ** * Neither the name of The Qt Company Ltd nor the names of its |
| ** contributors may be used to endorse or promote products derived |
| ** from this software without specific prior written permission. |
| ** |
| ** |
| ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| import QtQuick 2.12 |
| import QtTest 1.0 |
| import QtQuick.Controls 2.12 |
| |
| |
| TestCase { |
| id: testCase |
| width: 200 |
| height: 200 |
| visible: true |
| when: windowShown |
| name: "SwipeDelegate" |
| |
| readonly property int dragDistance: Math.max(20, Qt.styleHints.startDragDistance + 5) |
| |
| Component { |
| id: backgroundFillComponent |
| SwipeDelegate { |
| background: Item { anchors.fill: parent } |
| } |
| } |
| |
| Component { |
| id: backgroundCenterInComponent |
| SwipeDelegate { |
| background: Item { anchors.centerIn: parent } |
| } |
| } |
| |
| Component { |
| id: backgroundLeftComponent |
| SwipeDelegate { |
| background: Item { anchors.left: parent.left } |
| } |
| } |
| |
| Component { |
| id: backgroundRightComponent |
| SwipeDelegate { |
| background: Item { anchors.right: parent.right } |
| } |
| } |
| |
| Component { |
| id: contentItemFillComponent |
| SwipeDelegate { |
| contentItem: Item { anchors.fill: parent } |
| } |
| } |
| |
| Component { |
| id: contentItemCenterInComponent |
| SwipeDelegate { |
| contentItem: Item { anchors.centerIn: parent } |
| } |
| } |
| |
| Component { |
| id: contentItemLeftComponent |
| SwipeDelegate { |
| contentItem: Item { anchors.left: parent.left } |
| } |
| } |
| |
| Component { |
| id: contentItemRightComponent |
| SwipeDelegate { |
| contentItem: Item { anchors.right: parent.right } |
| } |
| } |
| |
| function test_horizontalAnchors_data() { |
| return [ |
| { tag: "background, fill", component: backgroundFillComponent, itemName: "background", warningLocation: ":69:25" }, |
| { tag: "background, centerIn", component: backgroundCenterInComponent, itemName: "background", warningLocation: ":76:25" }, |
| { tag: "background, left", component: backgroundLeftComponent, itemName: "background", warningLocation: ":83:25" }, |
| { tag: "background, right", component: backgroundRightComponent, itemName: "background", warningLocation: ":90:25" }, |
| { tag: "contentItem, fill", component: contentItemFillComponent, itemName: "contentItem", warningLocation: ":97:26" }, |
| { tag: "contentItem, centerIn", component: contentItemCenterInComponent, itemName: "contentItem", warningLocation: ":104:26" }, |
| { tag: "contentItem, left", component: contentItemLeftComponent, itemName: "contentItem", warningLocation: ":111:26" }, |
| { tag: "contentItem, right", component: contentItemRightComponent, itemName: "contentItem", warningLocation: ":118:26" } |
| ]; |
| } |
| |
| function test_horizontalAnchors(data) { |
| var warningMessage = Qt.resolvedUrl("tst_swipedelegate.qml") + data.warningLocation |
| + ": QML QQuickItem: SwipeDelegate: cannot use horizontal anchors with " + data.itemName + "; unable to layout the item." |
| |
| ignoreWarning(warningMessage); |
| |
| var control = createTemporaryObject(data.component, testCase); |
| verify(control.contentItem); |
| } |
| |
| Component { |
| id: greenLeftComponent |
| |
| Rectangle { |
| objectName: "leftItem" |
| anchors.fill: parent |
| color: "green" |
| } |
| } |
| |
| Component { |
| id: redRightComponent |
| |
| Rectangle { |
| objectName: "rightItem" |
| anchors.fill: parent |
| color: "red" |
| } |
| } |
| |
| Component { |
| id: swipeDelegateComponent |
| |
| SwipeDelegate { |
| id: swipeDelegate |
| text: "SwipeDelegate" |
| width: 150 |
| swipe.left: greenLeftComponent |
| swipe.right: redRightComponent |
| } |
| } |
| |
| Component { |
| id: signalSpyComponent |
| |
| SignalSpy {} |
| } |
| |
| Component { |
| id: itemComponent |
| |
| Item {} |
| } |
| |
| // Assumes that the delegate is smaller than the width of the control. |
| function swipe(control, from, to) { |
| // Sanity check. |
| compare(control.swipe.position, from); |
| |
| var distance = (to - from) * control.width; |
| |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width / 2 + distance, control.height / 2); |
| mouseRelease(control, control.width / 2 + distance, control.height / 2, Qt.LeftButton); |
| compare(control.swipe.position, to, "Expected swipe.position to be " + to |
| + " after swiping from " + from + ", but it's " + control.swipe.position); |
| |
| if (control.swipe.position === -1.0) { |
| if (control.swipe.right) |
| verify(control.swipe.rightItem); |
| else if (control.swipe.behind) |
| verify(control.swipe.behindItem); |
| } else if (control.swipe.position === 1.0) { |
| if (control.swipe.left) |
| verify(control.swipe.leftItem); |
| else if (control.swipe.behind) |
| verify(control.swipe.behindItem); |
| } |
| } |
| |
| function test_settingDelegates() { |
| var control = createTemporaryObject(swipeDelegateComponent, testCase); |
| verify(control); |
| |
| ignoreWarning(Qt.resolvedUrl("tst_swipedelegate.qml") + |
| ":168:9: QML SwipeDelegate: cannot set both behind and left/right properties") |
| control.swipe.behind = itemComponent; |
| |
| // Shouldn't be any warnings when unsetting delegates. |
| control.swipe.left = null; |
| compare(control.swipe.leftItem, null); |
| |
| // right is still set. |
| ignoreWarning(Qt.resolvedUrl("tst_swipedelegate.qml") + |
| ":168:9: QML SwipeDelegate: cannot set both behind and left/right properties") |
| control.swipe.behind = itemComponent; |
| |
| control.swipe.right = null; |
| compare(control.swipe.rightItem, null); |
| |
| control.swipe.behind = itemComponent; |
| |
| ignoreWarning(Qt.resolvedUrl("tst_swipedelegate.qml") + |
| ":168:9: QML SwipeDelegate: cannot set both behind and left/right properties") |
| control.swipe.left = itemComponent; |
| |
| ignoreWarning(Qt.resolvedUrl("tst_swipedelegate.qml") + |
| ":168:9: QML SwipeDelegate: cannot set both behind and left/right properties") |
| control.swipe.right = itemComponent; |
| |
| control.swipe.behind = null; |
| control.swipe.left = greenLeftComponent; |
| control.swipe.right = redRightComponent; |
| |
| // Test that the user is warned when attempting to set or unset left or |
| // right item while they're exposed. |
| // First, try the left item. |
| swipe(control, 0.0, 1.0); |
| |
| var oldLeft = control.swipe.left; |
| var oldLeftItem = control.swipe.leftItem; |
| ignoreWarning(Qt.resolvedUrl("tst_swipedelegate.qml") + |
| ":168:9: QML SwipeDelegate: left/right/behind properties may only be set when swipe.position is 0") |
| control.swipe.left = null; |
| compare(control.swipe.left, oldLeft); |
| compare(control.swipe.leftItem, oldLeftItem); |
| |
| // Try the same thing with the right item. |
| swipe(control, 1.0, -1.0); |
| |
| var oldRight = control.swipe.right; |
| var oldRightItem = control.swipe.rightItem; |
| ignoreWarning(Qt.resolvedUrl("tst_swipedelegate.qml") + |
| ":168:9: QML SwipeDelegate: left/right/behind properties may only be set when swipe.position is 0") |
| control.swipe.right = null; |
| compare(control.swipe.right, oldRight); |
| compare(control.swipe.rightItem, oldRightItem); |
| |
| // Return to the default position. |
| swipe(control, -1.0, 0.0); |
| |
| tryCompare(control.background, "x", 0, 1000); |
| |
| // Try the same thing with the behind item. |
| control.swipe.left = null; |
| verify(!control.swipe.left); |
| verify(!control.swipe.leftItem); |
| control.swipe.right = null; |
| verify(!control.swipe.right); |
| verify(!control.swipe.rightItem); |
| control.swipe.behind = greenLeftComponent; |
| verify(control.swipe.behind); |
| verify(!control.swipe.behindItem); |
| |
| swipe(control, 0.0, 1.0); |
| |
| var oldBehind = control.swipe.behind; |
| var oldBehindItem = control.swipe.behindItem; |
| ignoreWarning(Qt.resolvedUrl("tst_swipedelegate.qml") + |
| ":168:9: QML SwipeDelegate: left/right/behind properties may only be set when swipe.position is 0") |
| control.swipe.behind = null; |
| compare(control.swipe.behind, oldBehind); |
| compare(control.swipe.behindItem, oldBehindItem); |
| } |
| |
| function test_defaults() { |
| var control = createTemporaryObject(swipeDelegateComponent, testCase); |
| verify(control); |
| |
| compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset); |
| compare(control.swipe.position, 0); |
| verify(!control.pressed); |
| verify(!control.swipe.complete); |
| } |
| |
| SignalSequenceSpy { |
| id: mouseSignalSequenceSpy |
| signals: ["pressed", "released", "canceled", "clicked", "doubleClicked", "pressedChanged", "pressAndHold"] |
| } |
| |
| function test_swipe() { |
| var control = createTemporaryObject(swipeDelegateComponent, testCase); |
| verify(control); |
| |
| var overDragDistance = Math.round(dragDistance * 1.1); |
| |
| var completedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "completed" }); |
| verify(completedSpy); |
| verify(completedSpy.valid); |
| |
| var openedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "opened" }); |
| verify(openedSpy); |
| verify(openedSpy.valid); |
| |
| var closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" }); |
| verify(closedSpy); |
| verify(closedSpy.valid); |
| |
| mouseSignalSequenceSpy.target = control; |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }], "pressed"]; |
| mousePress(control, control.width / 2, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, 0.0); |
| verify(!control.swipe.complete); |
| compare(completedSpy.count, 0); |
| compare(openedSpy.count, 0); |
| compare(closedSpy.count, 0); |
| verify(mouseSignalSequenceSpy.success); |
| verify(!control.swipe.leftItem); |
| verify(!control.swipe.rightItem); |
| |
| // Drag to the right so that leftItem is created and visible. |
| mouseMove(control, control.width / 2 + overDragDistance, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, overDragDistance / control.width); |
| verify(!control.swipe.complete); |
| compare(completedSpy.count, 0); |
| compare(openedSpy.count, 0); |
| compare(closedSpy.count, 0); |
| verify(control.swipe.leftItem); |
| verify(control.swipe.leftItem.visible); |
| compare(control.swipe.leftItem.parent, control); |
| compare(control.swipe.leftItem.objectName, "leftItem"); |
| verify(!control.swipe.rightItem); |
| |
| // Go back to 0. |
| mouseMove(control, control.width / 2, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, 0.0); |
| verify(!control.swipe.complete); |
| compare(completedSpy.count, 0); |
| compare(openedSpy.count, 0); |
| compare(closedSpy.count, 0); |
| verify(control.swipe.leftItem); |
| verify(control.swipe.leftItem.visible); |
| compare(control.swipe.leftItem.parent, control); |
| compare(control.swipe.leftItem.objectName, "leftItem"); |
| verify(!control.swipe.rightItem); |
| |
| // Try the other direction. The right item should be created and visible, |
| // and the left item should be hidden. |
| mouseMove(control, control.width / 2 - overDragDistance, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, -overDragDistance / control.width); |
| verify(!control.swipe.complete); |
| compare(completedSpy.count, 0); |
| compare(openedSpy.count, 0); |
| compare(closedSpy.count, 0); |
| verify(control.swipe.leftItem); |
| verify(!control.swipe.leftItem.visible); |
| verify(control.swipe.rightItem); |
| verify(control.swipe.rightItem.visible); |
| compare(control.swipe.rightItem.parent, control); |
| compare(control.swipe.rightItem.objectName, "rightItem"); |
| |
| // Now release outside the right edge of the control. |
| mouseMove(control, control.width * 1.1, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, 0.6); |
| verify(!control.swipe.complete); |
| compare(completedSpy.count, 0); |
| compare(openedSpy.count, 0); |
| compare(closedSpy.count, 0); |
| verify(control.swipe.leftItem); |
| verify(control.swipe.leftItem.visible); |
| verify(control.swipe.rightItem); |
| verify(!control.swipe.rightItem.visible); |
| |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": false }], "canceled"]; |
| mouseRelease(control, control.width / 2, control.height / 2); |
| verify(!control.pressed); |
| tryCompare(control.swipe, "position", 1.0); |
| tryCompare(control.swipe, "complete", true); |
| compare(completedSpy.count, 1); |
| compare(openedSpy.count, 1); |
| compare(closedSpy.count, 0); |
| verify(mouseSignalSequenceSpy.success); |
| verify(control.swipe.leftItem); |
| verify(control.swipe.leftItem.visible); |
| verify(control.swipe.rightItem); |
| verify(!control.swipe.rightItem.visible); |
| tryCompare(control.contentItem, "x", control.width + control.leftPadding); |
| |
| // Swiping from the right and releasing early should return position to 1.0. |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }], "pressed"]; |
| mousePress(control, control.width / 2, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, 1.0); |
| // complete should still be true, because we haven't moved yet, and hence |
| // haven't started grabbing behind's mouse events. |
| verify(control.swipe.complete); |
| compare(completedSpy.count, 1); |
| compare(openedSpy.count, 1); |
| compare(closedSpy.count, 0); |
| verify(mouseSignalSequenceSpy.success); |
| |
| mouseMove(control, control.width / 2 - overDragDistance, control.height / 2); |
| verify(control.pressed); |
| verify(!control.swipe.complete); |
| compare(completedSpy.count, 1); |
| compare(openedSpy.count, 1); |
| compare(closedSpy.count, 0); |
| compare(control.swipe.position, 1.0 - overDragDistance / control.width); |
| |
| // Since we went over the drag distance, we should expect canceled() to be emitted. |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": false }], "canceled"]; |
| mouseRelease(control, control.width * 0.4, control.height / 2); |
| verify(!control.pressed); |
| tryCompare(control.swipe, "position", 1.0); |
| tryCompare(control.swipe, "complete", true); |
| compare(completedSpy.count, 2); |
| compare(openedSpy.count, 2); |
| compare(closedSpy.count, 0); |
| verify(mouseSignalSequenceSpy.success); |
| tryCompare(control.contentItem, "x", control.width + control.leftPadding); |
| |
| // Swiping from the right and releasing should return contents to default position. |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }], "pressed"]; |
| mousePress(control, control.width / 2, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, 1.0); |
| verify(control.swipe.complete); |
| compare(completedSpy.count, 2); |
| compare(openedSpy.count, 2); |
| compare(closedSpy.count, 0); |
| verify(mouseSignalSequenceSpy.success); |
| |
| mouseMove(control, control.width * -0.1, control.height / 2); |
| verify(control.pressed); |
| verify(!control.swipe.complete); |
| compare(completedSpy.count, 2); |
| compare(openedSpy.count, 2); |
| compare(closedSpy.count, 0); |
| compare(control.swipe.position, 0.4); |
| |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": false }], "canceled"]; |
| mouseRelease(control, control.width * -0.1, control.height / 2); |
| verify(!control.pressed); |
| tryCompare(control.swipe, "position", 0.0); |
| verify(!control.swipe.complete); |
| compare(completedSpy.count, 2); |
| compare(openedSpy.count, 2); |
| tryCompare(closedSpy, "count", 1); |
| verify(mouseSignalSequenceSpy.success); |
| tryCompare(control.contentItem, "x", control.leftPadding); |
| } |
| |
| function test_swipeVelocity_data() { |
| return [ |
| { tag: "positive velocity", direction: 1 }, |
| { tag: "negative velocity", direction: -1 } |
| ]; |
| } |
| |
| function test_swipeVelocity(data) { |
| skip("QTBUG-52003"); |
| |
| var control = createTemporaryObject(swipeDelegateComponent, testCase); |
| verify(control); |
| |
| var distance = Math.round(dragDistance * 1.1); |
| if (distance >= control.width / 2) |
| skip("This test requires a startDragDistance that is less than half the width of the control"); |
| |
| distance *= data.direction; |
| |
| mouseSignalSequenceSpy.target = control; |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }], "pressed"]; |
| mousePress(control, control.width / 2, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, 0.0); |
| verify(!control.swipe.complete); |
| verify(mouseSignalSequenceSpy.success); |
| verify(!control.swipe.leftItem); |
| verify(!control.swipe.rightItem); |
| |
| // Swipe quickly to the side over a distance that is longer than the drag threshold, |
| // quicker than the expose velocity threshold, but shorter than the halfway mark. |
| mouseMove(control, control.width / 2 + distance, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, distance / control.width); |
| verify(control.swipe.position < 0.5); |
| verify(!control.swipe.complete); |
| |
| var expectedVisibleItem; |
| var expectedVisibleObjectName; |
| var expectedHiddenItem; |
| var expectedContentItemX; |
| if (distance > 0) { |
| expectedVisibleObjectName = "leftItem"; |
| expectedVisibleItem = control.swipe.leftItem; |
| expectedHiddenItem = control.swipe.rightItem; |
| expectedContentItemX = control.width + control.leftPadding; |
| } else { |
| expectedVisibleObjectName = "rightItem"; |
| expectedVisibleItem = control.swipe.rightItem; |
| expectedHiddenItem = control.swipe.leftItem; |
| expectedContentItemX = -control.width + control.leftPadding; |
| } |
| verify(expectedVisibleItem); |
| verify(expectedVisibleItem.visible); |
| compare(expectedVisibleItem.parent, control); |
| compare(expectedVisibleItem.objectName, expectedVisibleObjectName); |
| verify(!expectedHiddenItem); |
| |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": false }], "released", "clicked"]; |
| // Add a delay to ensure that the release event doesn't happen too quickly, |
| // and hence that the second timestamp isn't zero (can happen with e.g. release builds). |
| mouseRelease(control, control.width / 2 + distance, control.height / 2, Qt.LeftButton, Qt.NoModifier, 30); |
| verify(!control.pressed); |
| compare(control.swipe.position, data.direction); |
| verify(control.swipe.complete); |
| verify(mouseSignalSequenceSpy.success); |
| verify(expectedVisibleItem); |
| verify(expectedVisibleItem.visible); |
| verify(!expectedHiddenItem); |
| tryCompare(control.contentItem, "x", expectedContentItemX); |
| } |
| |
| Component { |
| id: swipeDelegateWithButtonComponent |
| SwipeDelegate { |
| text: "SwipeDelegate" |
| width: 150 |
| swipe.right: Button { |
| width: parent.width |
| height: parent.height |
| text: "Boo!" |
| } |
| } |
| } |
| |
| function test_eventsToLeftAndRight() { |
| var control = createTemporaryObject(swipeDelegateWithButtonComponent, testCase); |
| verify(control); |
| |
| var closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" }); |
| verify(closedSpy); |
| verify(closedSpy.valid); |
| |
| // The button should be pressed instead of the SwipeDelegate. |
| mouseDrag(control, control.width / 2, 0, -control.width, 0); |
| // Mouse has been released by this stage. |
| verify(!control.pressed); |
| compare(control.swipe.position, -1.0); |
| verify(control.swipe.rightItem); |
| verify(control.swipe.rightItem.visible); |
| compare(control.swipe.rightItem.parent, control); |
| |
| var buttonPressedSpy = signalSpyComponent.createObject(control, { target: control.swipe.rightItem, signalName: "pressed" }); |
| verify(buttonPressedSpy); |
| verify(buttonPressedSpy.valid); |
| var buttonReleasedSpy = signalSpyComponent.createObject(control, { target: control.swipe.rightItem, signalName: "released" }); |
| verify(buttonReleasedSpy); |
| verify(buttonReleasedSpy.valid); |
| var buttonClickedSpy = signalSpyComponent.createObject(control, { target: control.swipe.rightItem, signalName: "clicked" }); |
| verify(buttonClickedSpy); |
| verify(buttonClickedSpy.valid); |
| |
| // Now press the button. |
| mousePress(control, control.width / 2, control.height / 2); |
| verify(!control.pressed); |
| var button = control.swipe.rightItem; |
| verify(button.pressed); |
| compare(buttonPressedSpy.count, 1); |
| compare(buttonReleasedSpy.count, 0); |
| compare(buttonClickedSpy.count, 0); |
| |
| mouseRelease(control, control.width / 2, control.height / 2); |
| verify(!button.pressed); |
| compare(buttonPressedSpy.count, 1); |
| compare(buttonReleasedSpy.count, 1); |
| compare(buttonClickedSpy.count, 1); |
| |
| // Returning back to a position of 0 and pressing on the control should |
| // result in the control being pressed. |
| mouseDrag(control, control.width / 2, 0, control.width * 0.6, 0); |
| tryCompare(closedSpy, "count", 1); |
| compare(control.swipe.position, 0); |
| mousePress(control, control.width / 2, control.height / 2); |
| verify(control.pressed); |
| verify(!button.pressed); |
| mouseRelease(control, control.width / 2, control.height / 2); |
| verify(!control.pressed); |
| } |
| |
| function test_mouseButtons() { |
| var control = createTemporaryObject(swipeDelegateComponent, testCase); |
| verify(control); |
| |
| // click |
| mouseSignalSequenceSpy.target = control; |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }], "pressed"]; |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| compare(control.pressed, true); |
| |
| verify(mouseSignalSequenceSpy.success); |
| |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": false }], "released", "clicked"]; |
| mouseRelease(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| compare(control.pressed, false); |
| verify(mouseSignalSequenceSpy.success); |
| |
| // right button |
| mouseSignalSequenceSpy.expectedSequence = []; |
| mousePress(control, control.width / 2, control.height / 2, Qt.RightButton); |
| compare(control.pressed, false); |
| |
| mouseRelease(control, control.width / 2, control.height / 2, Qt.RightButton); |
| compare(control.pressed, false); |
| verify(mouseSignalSequenceSpy.success); |
| |
| // double click |
| mouseSignalSequenceSpy.expectedSequence = [ |
| ["pressedChanged", { "pressed": true }], |
| "pressed", |
| ["pressedChanged", { "pressed": false }], |
| "released", |
| "clicked", |
| ["pressedChanged", { "pressed": true }], |
| "pressed", |
| "doubleClicked", |
| ["pressedChanged", { "pressed": false }], |
| "released" |
| ]; |
| mouseDoubleClickSequence(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| verify(mouseSignalSequenceSpy.success); |
| |
| // press and hold |
| var pressAndHoldSpy = signalSpyComponent.createObject(control, { target: control, signalName: "pressAndHold" }); |
| verify(pressAndHoldSpy); |
| verify(pressAndHoldSpy.valid); |
| |
| mouseSignalSequenceSpy.expectedSequence = [ |
| ["pressedChanged", { "pressed": true }], |
| "pressed", |
| "pressAndHold", |
| ["pressedChanged", { "pressed": false }], |
| "released" |
| ]; |
| mousePress(control); |
| compare(control.pressed, true); |
| tryCompare(pressAndHoldSpy, "count", 1); |
| |
| mouseRelease(control); |
| compare(control.pressed, false); |
| verify(mouseSignalSequenceSpy.success); |
| } |
| |
| Component { |
| id: removableDelegatesComponent |
| |
| ListView { |
| id: listView |
| width: 100 |
| height: 120 |
| |
| model: ListModel { |
| ListElement { name: "Apple" } |
| ListElement { name: "Orange" } |
| ListElement { name: "Pear" } |
| } |
| |
| delegate: SwipeDelegate { |
| id: rootDelegate |
| text: modelData |
| width: listView.width |
| |
| property alias removeAnimation: onRemoveAnimation |
| |
| ListView.onRemove: SequentialAnimation { |
| id: onRemoveAnimation |
| |
| PropertyAction { |
| target: rootDelegate |
| property: "ListView.delayRemove" |
| value: true |
| } |
| NumberAnimation { |
| target: rootDelegate |
| property: "height" |
| to: 0 |
| easing.type: Easing.InOutQuad |
| } |
| PropertyAction { |
| target: rootDelegate; |
| property: "ListView.delayRemove"; |
| value: false |
| } |
| } |
| |
| swipe.left: Rectangle { |
| objectName: "rectangle" |
| color: SwipeDelegate.pressed ? "#333" : "#444" |
| anchors.fill: parent |
| |
| SwipeDelegate.onClicked: listView.model.remove(index) |
| |
| Label { |
| objectName: "label" |
| text: "Remove" |
| color: "white" |
| anchors.centerIn: parent |
| } |
| } |
| } |
| } |
| } |
| |
| function test_removableDelegates() { |
| var listView = createTemporaryObject(removableDelegatesComponent, testCase); |
| verify(listView); |
| compare(listView.count, 3); |
| |
| // Expose the remove button. |
| var firstItem = listView.itemAt(0, 0); |
| mousePress(listView, firstItem.width / 2, firstItem.height / 2); |
| verify(firstItem.pressed); |
| compare(firstItem.swipe.position, 0.0); |
| verify(!firstItem.swipe.complete); |
| verify(!firstItem.swipe.leftItem); |
| |
| mouseMove(listView, firstItem.width * 1.1, firstItem.height / 2); |
| verify(firstItem.pressed); |
| compare(firstItem.swipe.position, 0.6); |
| verify(!firstItem.swipe.complete); |
| verify(firstItem.swipe.leftItem); |
| verify(!firstItem.swipe.leftItem.SwipeDelegate.pressed); |
| |
| mouseRelease(listView, firstItem.width / 2, firstItem.height / 2); |
| verify(!firstItem.pressed); |
| tryCompare(firstItem.swipe, "position", 1.0); |
| tryCompare(firstItem.swipe, "complete", true); |
| compare(listView.count, 3); |
| |
| // Wait for it to settle down. |
| tryCompare(firstItem.contentItem, "x", firstItem.leftPadding + firstItem.width); |
| |
| var leftClickedSpy = signalSpyComponent.createObject(firstItem.swipe.leftItem, |
| { target: firstItem.swipe.leftItem.SwipeDelegate, signalName: "clicked" }); |
| verify(leftClickedSpy); |
| verify(leftClickedSpy.valid); |
| |
| // Click the left item to remove the delegate from the list. |
| var contentItemX = firstItem.contentItem.x; |
| mousePress(listView, firstItem.width / 2, firstItem.height / 2); |
| verify(firstItem.swipe.leftItem.SwipeDelegate.pressed); |
| compare(leftClickedSpy.count, 0); |
| verify(!firstItem.pressed); |
| |
| mouseRelease(listView, firstItem.width / 2, firstItem.height / 2); |
| verify(!firstItem.swipe.leftItem.SwipeDelegate.pressed); |
| compare(leftClickedSpy.count, 1); |
| verify(!firstItem.pressed); |
| leftClickedSpy = null; |
| tryCompare(firstItem.removeAnimation, "running", true); |
| // There was a bug where the resizeContent() would be called because the height |
| // of the control was changing due to the animation. contentItem would then |
| // change x position and hence be visible when it shouldn't be. |
| verify(firstItem.removeAnimation.running); |
| while (1) { |
| wait(10) |
| if (firstItem && firstItem.removeAnimation && firstItem.removeAnimation.running) |
| compare(firstItem.contentItem.x, contentItemX); |
| else |
| break; |
| } |
| compare(listView.count, 2); |
| } |
| |
| Component { |
| id: leadingTrailingXComponent |
| SwipeDelegate { |
| id: delegate |
| width: 150 |
| text: "SwipeDelegate" |
| |
| swipe.left: Rectangle { |
| x: delegate.background.x - width |
| width: delegate.width |
| height: delegate.height |
| color: "green" |
| } |
| |
| swipe.right: Rectangle { |
| x: delegate.background.x + delegate.background.width |
| width: delegate.width |
| height: delegate.height |
| color: "red" |
| } |
| } |
| } |
| |
| Component { |
| id: leadingTrailingAnchorsComponent |
| SwipeDelegate { |
| id: delegate |
| width: 150 |
| text: "SwipeDelegate" |
| |
| swipe.left: Rectangle { |
| anchors.right: delegate.background.left |
| width: delegate.width |
| height: delegate.height |
| color: "green" |
| } |
| |
| swipe.right: Rectangle { |
| anchors.left: delegate.background.right |
| width: delegate.width |
| height: delegate.height |
| color: "red" |
| } |
| } |
| } |
| |
| function test_leadingTrailing_data() { |
| return [ |
| { tag: "x", component: leadingTrailingXComponent }, |
| { tag: "anchors", component: leadingTrailingAnchorsComponent }, |
| ]; |
| } |
| |
| function test_leadingTrailing(data) { |
| var control = createTemporaryObject(data.component, testCase); |
| verify(control); |
| |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width, control.height / 2); |
| verify(control.swipe.leftItem); |
| compare(control.swipe.leftItem.x, -control.width / 2); |
| mouseRelease(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| } |
| |
| function test_minMaxPosition() { |
| var control = createTemporaryObject(leadingTrailingXComponent, testCase); |
| verify(control); |
| |
| // Should be limited within the range -1.0 to 1.0. |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width * 1.5, control.height / 2); |
| compare(control.swipe.position, 1.0); |
| mouseMove(control, control.width * 1.6, control.height / 2); |
| compare(control.swipe.position, 1.0); |
| mouseMove(control, control.width * -1.6, control.height / 2); |
| compare(control.swipe.position, -1.0); |
| mouseRelease(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| } |
| |
| Component { |
| id: emptySwipeDelegateComponent |
| |
| SwipeDelegate { |
| text: "SwipeDelegate" |
| width: 150 |
| } |
| } |
| |
| Component { |
| id: smallLeftComponent |
| |
| Rectangle { |
| width: 80 |
| height: 40 |
| color: "green" |
| } |
| } |
| |
| // swipe.position should be scaled to the width of the relevant delegate, |
| // and it shouldn't be possible to drag past the delegate (so that content behind the control is visible). |
| function test_delegateWidth() { |
| var control = createTemporaryObject(emptySwipeDelegateComponent, testCase); |
| verify(control); |
| |
| control.swipe.left = smallLeftComponent; |
| |
| // Ensure that the position is scaled to the width of the currently visible delegate. |
| var overDragDistance = Math.round(dragDistance * 1.1); |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width / 2 + overDragDistance, control.height / 2); |
| verify(control.swipe.leftItem); |
| compare(control.swipe.position, overDragDistance / control.swipe.leftItem.width); |
| |
| mouseMove(control, control.width / 2 + control.swipe.leftItem.width, control.height / 2); |
| compare(control.swipe.position, 1.0); |
| |
| // Ensure that it's not possible to drag past the (left) delegate. |
| mouseMove(control, control.width / 2 + control.swipe.leftItem.width + 1, control.height / 2); |
| compare(control.swipe.position, 1.0); |
| |
| // Now release over the right side; the position should be 1.0 and the background |
| // should be "anchored" to the right side of the left delegate item. |
| mouseMove(control, control.width / 2 + control.swipe.leftItem.width, control.height / 2); |
| mouseRelease(control, control.width / 2 + control.swipe.leftItem.width, control.height / 2, Qt.LeftButton); |
| compare(control.swipe.position, 1.0); |
| tryCompare(control.background, "x", control.swipe.leftItem.width, 1000); |
| } |
| |
| SignalSpy { |
| id: leftVisibleSpy |
| signalName: "visibleChanged" |
| } |
| |
| SignalSpy { |
| id: rightVisibleSpy |
| signalName: "visibleChanged" |
| } |
| |
| function test_positionAfterSwipeCompleted() { |
| var control = createTemporaryObject(swipeDelegateComponent, testCase); |
| verify(control); |
| |
| // Ensure that both delegates are constructed. |
| mousePress(control, 0, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width * 1.1, control.height / 2); |
| verify(control.swipe.leftItem); |
| mouseMove(control, control.width * -0.1, control.height / 2); |
| verify(control.swipe.rightItem); |
| |
| // Expose the left delegate. |
| mouseMove(control, control.swipe.leftItem.width, control.height / 2); |
| mouseRelease(control, control.swipe.leftItem.width, control.height / 2); |
| verify(control.swipe.complete); |
| compare(control.swipe.position, 1.0); |
| |
| leftVisibleSpy.target = control.swipe.leftItem; |
| rightVisibleSpy.target = control.swipe.rightItem; |
| |
| // Swipe from right to left without exposing the right item, |
| // and make sure that the right item never becomes visible |
| // (and hence that the left item never loses visibility). |
| mousePress(control, control.swipe.leftItem.width, control.height / 2, Qt.LeftButton); |
| compare(leftVisibleSpy.count, 0); |
| compare(rightVisibleSpy.count, 0); |
| var newX = control.swipe.leftItem.width - Math.round(dragDistance * 1.1); |
| mouseMove(control, newX, control.height / 2); |
| compare(leftVisibleSpy.count, 0); |
| compare(rightVisibleSpy.count, 0); |
| compare(control.swipe.position, newX / control.swipe.leftItem.width); |
| |
| mouseMove(control, 0, control.height / 2); |
| compare(control.swipe.position, 0); |
| |
| // Test swiping over a distance that is greater than the width of the left item. |
| mouseMove(control, -1, control.height / 2); |
| verify(control.swipe.rightItem); |
| compare(control.swipe.position, -1 / control.swipe.rightItem.width); |
| |
| // Now go back to 1.0. |
| mouseMove(control, control.swipe.leftItem.width, control.height / 2); |
| compare(control.swipe.position, 1.0); |
| tryCompare(control.background, "x", control.swipe.leftItem.width, 1000); |
| mouseRelease(control, control.swipe.leftItem.width, control.height / 2, Qt.LeftButton); |
| } |
| |
| // TODO: this somehow results in the behind item having a negative width |
| // Component { |
| // id: behindSwipeDelegateComponent |
| // SwipeDelegate { |
| // anchors.centerIn: parent |
| // swipe.behind: Rectangle { |
| // onXChanged: print("x changed", x) |
| // anchors.left: { |
| // print("anchors.left expression", swipe.position) |
| // swipe.position < 0 ? parent.background.right : undefined |
| // } |
| // anchors.right: { |
| // print("anchors.right expression", swipe.position) |
| // swipe.position > 0 ? parent.background.left : undefined |
| // } |
| // width: parent.width |
| // height: parent.height |
| // color: "green" |
| // } |
| // swipe.left: null |
| // swipe.right: null |
| // Rectangle { |
| // anchors.fill: parent |
| // color: "transparent" |
| // border.color: "darkorange" |
| // } |
| // } |
| // } |
| |
| Component { |
| id: behindSwipeDelegateComponent |
| SwipeDelegate { |
| text: "SwipeDelegate" |
| width: 150 |
| anchors.centerIn: parent |
| swipe.behind: Rectangle { |
| x: swipe.position < 0 ? parent.background.x + parent.background.width |
| : (swipe.position > 0 ? parent.background.x - width : 0) |
| width: parent.width |
| height: parent.height |
| color: "green" |
| } |
| swipe.left: null |
| swipe.right: null |
| } |
| } |
| |
| function test_leadingTrailingBehindItem() { |
| var control = createTemporaryObject(behindSwipeDelegateComponent, testCase); |
| verify(control); |
| |
| swipe(control, 0.0, 1.0); |
| verify(control.swipe.behindItem.visible); |
| compare(control.swipe.behindItem.x, control.background.x - control.background.width); |
| |
| swipe(control, 1.0, -1.0); |
| verify(control.swipe.behindItem.visible); |
| compare(control.swipe.behindItem.x, control.background.x + control.background.width); |
| |
| swipe(control, -1.0, 1.0); |
| verify(control.swipe.behindItem.visible); |
| compare(control.swipe.behindItem.x, control.background.x - control.background.width); |
| |
| // Should be possible to "wrap" with a behind delegate specified. |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width / 2 + control.swipe.behindItem.width * 0.8, control.height / 2); |
| compare(control.swipe.position, -0.2); |
| mouseRelease(control, control.width / 2 + control.swipe.behindItem.width * 0.8, control.height / 2, Qt.LeftButton); |
| tryCompare(control.swipe, "position", 0.0); |
| |
| // Try wrapping the other way. |
| swipe(control, 0.0, -1.0); |
| verify(control.swipe.behindItem.visible); |
| compare(control.swipe.behindItem.x, control.background.x + control.background.width); |
| |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width / 2 - control.swipe.behindItem.width * 0.8, control.height / 2); |
| compare(control.swipe.position, 0.2); |
| mouseRelease(control, control.width / 2 - control.swipe.behindItem.width * 0.8, control.height / 2, Qt.LeftButton); |
| tryCompare(control.swipe, "position", 0.0); |
| } |
| |
| Component { |
| id: closeSwipeDelegateComponent |
| |
| SwipeDelegate { |
| text: "SwipeDelegate" |
| width: 150 |
| |
| swipe.right: Item { |
| width: parent.width |
| height: parent.height |
| |
| SwipeDelegate.onClicked: swipe.close() |
| } |
| } |
| } |
| |
| function test_close() { |
| var control = createTemporaryObject(closeSwipeDelegateComponent, testCase); |
| verify(control); |
| |
| var closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" }); |
| verify(closedSpy); |
| verify(closedSpy.valid); |
| |
| swipe(control, 0.0, -1.0); |
| compare(control.swipe.rightItem.visible, true); |
| // Should animate, so it shouldn't change right away. |
| compare(control.swipe.rightItem.x, 0); |
| tryCompare(control.swipe.rightItem, "x", control.background.x + control.background.width); |
| |
| mousePress(control); |
| verify(control.swipe.rightItem.SwipeDelegate.pressed); |
| |
| mouseRelease(control); |
| verify(!control.swipe.rightItem.SwipeDelegate.pressed); |
| tryCompare(closedSpy, "count", 1); |
| compare(control.swipe.position, 0); |
| |
| // Swiping after closing should work as normal. |
| swipe(control, 0.0, -1.0); |
| } |
| |
| function test_callCloseWhenAlreadyClosed() { |
| let control = createTemporaryObject(swipeDelegateComponent, testCase) |
| verify(control) |
| |
| let closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" }) |
| verify(closedSpy) |
| verify(closedSpy.valid) |
| |
| // Calling close() when it's already closed should have no effect. |
| control.swipe.close() |
| compare(closedSpy.count, 0) |
| |
| // The game goes for calling close() in response to a click. |
| control.clicked.connect(function() { control.swipe.close() }) |
| mouseClick(control) |
| compare(closedSpy.count, 0) |
| } |
| |
| // Can't just connect to pressed in QML, because there is a pressed property |
| // that conflicts with the signal. |
| Component { |
| id: swipeDelegateCloseOnPressedComponent |
| |
| SwipeDelegate { |
| text: "SwipeDelegate" |
| width: 150 |
| swipe.right: Rectangle { |
| objectName: "rightItem" |
| width: parent.width / 2 |
| height: parent.height |
| color: "tomato" |
| } |
| |
| onPressed: swipe.close() |
| } |
| } |
| |
| /* |
| We don't want to support closing on pressed(); released() or clicked() |
| should be used instead. However, calling swipe.close() in response to |
| a press should still not cause closed() to be emitted. |
| */ |
| function test_closeOnPressed() { |
| let control = createTemporaryObject(swipeDelegateCloseOnPressedComponent, testCase) |
| verify(control) |
| |
| swipe(control, 0.0, -1.0) |
| |
| let closedSpy = signalSpyComponent.createObject(control, { target: control.swipe, signalName: "closed" }) |
| verify(closedSpy) |
| verify(closedSpy.valid) |
| |
| mousePress(control, control.width * 0.1) |
| compare(closedSpy.count, 0) |
| compare(control.swipe.position, -1.0) |
| |
| // Simulate a somewhat realistic delay between press and release |
| // to ensure that the bug is triggered. |
| wait(100) |
| mouseRelease(control, control.width * 0.1) |
| compare(closedSpy.count, 0) |
| compare(control.swipe.position, -1.0) |
| } |
| |
| Component { |
| id: multiActionSwipeDelegateComponent |
| |
| SwipeDelegate { |
| text: "SwipeDelegate" |
| width: 150 |
| |
| swipe.right: Item { |
| objectName: "rightItemRoot" |
| width: parent.width |
| height: parent.height |
| |
| property alias firstAction: firstAction |
| property alias secondAction: secondAction |
| |
| property int firstClickCount: 0 |
| property int secondClickCount: 0 |
| |
| Row { |
| anchors.fill: parent |
| anchors.margins: 5 |
| |
| Rectangle { |
| id: firstAction |
| width: parent.width / 2 |
| height: parent.height |
| color: "tomato" |
| |
| SwipeDelegate.onClicked: ++firstClickCount |
| } |
| Rectangle { |
| id: secondAction |
| width: parent.width / 2 |
| height: parent.height |
| color: "navajowhite" |
| |
| SwipeDelegate.onClicked: ++secondClickCount |
| } |
| } |
| } |
| } |
| } |
| |
| // Tests that it's possible to have multiple non-interactive items in one delegate |
| // (e.g. left/right/behind) that can each receive clicks. |
| function test_multipleClickableActions() { |
| var control = createTemporaryObject(multiActionSwipeDelegateComponent, testCase); |
| verify(control); |
| |
| swipe(control, 0.0, -1.0); |
| verify(control.swipe.rightItem); |
| tryCompare(control.swipe, "complete", true); |
| |
| var firstClickedSpy = signalSpyComponent.createObject(control, |
| { target: control.swipe.rightItem.firstAction.SwipeDelegate, signalName: "clicked" }); |
| verify(firstClickedSpy); |
| verify(firstClickedSpy.valid); |
| |
| // Clicked within rightItem, but not within an item using the attached properties. |
| mousePress(control, 2, 2); |
| compare(control.swipe.rightItem.firstAction.SwipeDelegate.pressed, false); |
| compare(firstClickedSpy.count, 0); |
| |
| mouseRelease(control, 2, 2); |
| compare(control.swipe.rightItem.firstAction.SwipeDelegate.pressed, false); |
| compare(firstClickedSpy.count, 0); |
| |
| // Click within the first item. |
| mousePress(control.swipe.rightItem.firstAction, 0, 0); |
| compare(control.swipe.rightItem.firstAction.SwipeDelegate.pressed, true); |
| compare(firstClickedSpy.count, 0); |
| |
| mouseRelease(control.swipe.rightItem.firstAction, 0, 0); |
| compare(control.swipe.rightItem.firstAction.SwipeDelegate.pressed, false); |
| compare(firstClickedSpy.count, 1); |
| compare(control.swipe.rightItem.firstClickCount, 1); |
| |
| var secondClickedSpy = signalSpyComponent.createObject(control, |
| { target: control.swipe.rightItem.secondAction.SwipeDelegate, signalName: "clicked" }); |
| verify(secondClickedSpy); |
| verify(secondClickedSpy.valid); |
| |
| // Click within the second item. |
| mousePress(control.swipe.rightItem.secondAction, 0, 0); |
| compare(control.swipe.rightItem.secondAction.SwipeDelegate.pressed, true); |
| compare(secondClickedSpy.count, 0); |
| |
| mouseRelease(control.swipe.rightItem.secondAction, 0, 0); |
| compare(control.swipe.rightItem.secondAction.SwipeDelegate.pressed, false); |
| compare(secondClickedSpy.count, 1); |
| compare(control.swipe.rightItem.secondClickCount, 1); |
| } |
| |
| // Pressing on a "side action" and then dragging should eventually |
| // cause the ListView to grab the mouse and start changing its contentY. |
| // When this happens, it will grab the mouse and hence we must clear |
| // that action's pressed state so that it doesn't stay pressed after releasing. |
| function test_dragSideAction() { |
| var listView = createTemporaryObject(removableDelegatesComponent, testCase); |
| verify(listView); |
| |
| var control = listView.itemAt(0, 0); |
| verify(control); |
| |
| // Expose the side action. |
| swipe(control, 0.0, 1.0); |
| verify(control.swipe.leftItem); |
| tryCompare(control.swipe, "complete", true); |
| |
| var pressedSpy = signalSpyComponent.createObject(control, |
| { target: control.swipe.leftItem.SwipeDelegate, signalName: "pressedChanged" }); |
| verify(pressedSpy); |
| verify(pressedSpy.valid); |
| |
| mouseDrag(listView, 20, 20, 0, listView.height); |
| compare(pressedSpy.count, 2); |
| verify(listView.contentY !== 0); |
| |
| compare(control.swipe.leftItem.SwipeDelegate.pressed, false); |
| } |
| |
| // When the width of a SwipeDelegate changes (as it does upon portrait => landscape |
| // rotation, for example), the positions of the contentItem and background items |
| // should be updated accordingly. |
| function test_contentItemPosOnWidthChanged() { |
| var control = createTemporaryObject(swipeDelegateComponent, testCase); |
| verify(control); |
| |
| swipe(control, 0.0, 1.0); |
| |
| var oldContentItemX = control.contentItem.x; |
| var oldBackgroundX = control.background.x; |
| control.width += 100; |
| compare(control.contentItem.x, oldContentItemX + 100); |
| compare(control.background.x, oldBackgroundX + 100); |
| } |
| |
| function test_contentItemHeightOnHeightChanged() { |
| var control = createTemporaryObject(swipeDelegateComponent, testCase); |
| verify(control); |
| |
| // Try when swipe.complete is false. |
| var originalHeight = control.height; |
| var originalContentItemHeight = control.contentItem.height; |
| verify(control.height !== 10); |
| control.height = 10; |
| compare(control.contentItem.height, control.availableHeight); |
| verify(control.contentItem.height < originalContentItemHeight); |
| compare(control.contentItem.y, control.topPadding); |
| |
| // Try when swipe.complete is true. |
| control.height = originalHeight; |
| swipe(control, 0.0, 1.0); |
| control.height = 10; |
| compare(control.contentItem.height, control.availableHeight); |
| verify(control.contentItem.height < originalContentItemHeight); |
| compare(control.contentItem.y, control.topPadding); |
| } |
| |
| function test_releaseOutside_data() { |
| return [ |
| { tag: "no delegates", component: emptySwipeDelegateComponent }, |
| { tag: "delegates", component: swipeDelegateComponent }, |
| ]; |
| } |
| |
| function test_releaseOutside(data) { |
| var control = createTemporaryObject(data.component, testCase); |
| verify(control); |
| |
| // Press and then release below the control. |
| mouseSignalSequenceSpy.target = control; |
| mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": true }], "pressed", ["pressedChanged", { "pressed": false }]]; |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width / 2, control.height + 10); |
| verify(mouseSignalSequenceSpy.success); |
| |
| mouseSignalSequenceSpy.expectedSequence = ["canceled"]; |
| mouseRelease(control, control.width / 2, control.height + 10, Qt.LeftButton); |
| verify(mouseSignalSequenceSpy.success); |
| |
| // Press and then release to the right of the control. |
| var hasDelegates = control.swipe.left || control.swipe.right || control.swipe.behind; |
| mouseSignalSequenceSpy.target = control; |
| mouseSignalSequenceSpy.expectedSequence = hasDelegates |
| ? [["pressedChanged", { "pressed": true }], "pressed"] |
| : [["pressedChanged", { "pressed": true }], "pressed", ["pressedChanged", { "pressed": false }]]; |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width + 10, control.height / 2); |
| if (hasDelegates) |
| verify(control.swipe.position > 0); |
| verify(mouseSignalSequenceSpy.success); |
| |
| mouseSignalSequenceSpy.expectedSequence = hasDelegates ? [["pressedChanged", { "pressed": false }], "canceled"] : ["canceled"]; |
| mouseRelease(control, control.width + 10, control.height / 2, Qt.LeftButton); |
| verify(mouseSignalSequenceSpy.success); |
| } |
| |
| Component { |
| id: leftRightWithLabelsComponent |
| |
| SwipeDelegate { |
| id: delegate |
| text: "SwipeDelegate" |
| width: 150 |
| |
| background.opacity: 0.5 |
| |
| swipe.left: Rectangle { |
| width: parent.width |
| height: parent.height |
| color: SwipeDelegate.pressed ? Qt.darker("green") : "green" |
| |
| property alias label: label |
| |
| Label { |
| id: label |
| text: "Left" |
| color: "white" |
| anchors.margins: 10 |
| anchors.left: parent.left |
| anchors.verticalCenter: parent.verticalCenter |
| } |
| |
| SwipeDelegate.onClicked: delegate.swipe.close() |
| } |
| |
| swipe.right: Rectangle { |
| width: parent.width |
| height: parent.height |
| anchors.right: parent.right |
| color: SwipeDelegate.pressed ? Qt.darker("green") : "red" |
| |
| property alias label: label |
| |
| Label { |
| id: label |
| text: "Right" |
| color: "white" |
| anchors.margins: 10 |
| anchors.right: parent.right |
| anchors.verticalCenter: parent.verticalCenter |
| } |
| |
| SwipeDelegate.onClicked: delegate.swipe.close() |
| } |
| } |
| } |
| |
| function test_beginSwipeOverRightItem() { |
| var control = createTemporaryObject(leftRightWithLabelsComponent, testCase); |
| verify(control); |
| |
| // Swipe to the left, exposing the right item. |
| swipe(control, 0.0, -1.0); |
| |
| // Click to close it and go back to a position of 0. |
| mouseClick(control); |
| |
| // TODO: Swipe to the left, with the mouse over the Label in the right item. |
| // The left item should not become visible at any point. |
| var rightLabel = control.swipe.rightItem.label; |
| var overDragDistance = Math.round(dragDistance * 1.1); |
| mousePress(rightLabel, rightLabel.width / 2, rightLabel.height / 2, Qt.rightButton); |
| mouseMove(rightLabel, rightLabel.width / 2 - overDragDistance, rightLabel.height / 2); |
| verify(!control.swipe.leftItem); |
| |
| mouseRelease(rightLabel, rightLabel.width / 2 - overDragDistance, control.height / 2, Qt.LeftButton); |
| verify(!control.swipe.leftItem); |
| } |
| |
| Component { |
| id: swipeDelegateDisabledComponent |
| |
| SwipeDelegate { |
| id: swipeDelegate |
| text: "SwipeDelegate" |
| width: parent.width |
| height: checked ? implicitHeight * 2 : implicitHeight |
| checkable: true |
| |
| swipe.enabled: false |
| swipe.right: Label { |
| text: swipeDelegate.checked ? qsTr("Expanded") : qsTr("Collapsed") |
| width: parent.width |
| height: parent.height |
| padding: 12 |
| color: "white" |
| verticalAlignment: Label.AlignVCenter |
| horizontalAlignment: Label.AlignRight |
| } |
| } |
| } |
| |
| function test_swipeEnabled() { |
| var control = createTemporaryObject(swipeDelegateDisabledComponent, testCase); |
| |
| mousePress(control, control.width / 2, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, 0.0); |
| verify(!control.swipe.complete); |
| verify(!control.swipe.leftItem); |
| verify(!control.swipe.rightItem); |
| |
| // It shouldn't be possible to swipe. |
| var overDragDistance = Math.round(dragDistance * 1.1); |
| mouseMove(control, control.width / 2 - overDragDistance, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, 0.0); |
| verify(!control.swipe.complete); |
| verify(!control.swipe.leftItem); |
| verify(!control.swipe.rightItem); |
| |
| // Now move outside the right edge of the control and release. |
| mouseMove(control, control.width * 1.1, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, 0.0); |
| verify(!control.swipe.complete); |
| verify(!control.swipe.leftItem); |
| verify(!control.swipe.rightItem); |
| |
| mouseRelease(control, control.width / 2, control.height / 2); |
| verify(!control.pressed); |
| compare(control.swipe.position, 0.0); |
| verify(!control.swipe.complete); |
| verify(!control.swipe.leftItem); |
| verify(!control.swipe.rightItem); |
| |
| // Now enabled swiping so that we can swipe to the left. |
| control.swipe.enabled = true; |
| swipe(control, 0, -1); |
| verify(control.swipe.complete); |
| |
| // Now that the swipe is complete, disable swiping and then try to swipe again. |
| // It should stay at its position of -1. |
| control.swipe.enabled = false; |
| |
| mousePress(control, control.width / 2, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, -1.0); |
| |
| mouseMove(control, control.width / 2 + overDragDistance, control.height / 2); |
| verify(control.pressed); |
| compare(control.swipe.position, -1.0); |
| verify(control.swipe.complete); |
| |
| mouseRelease(control, control.width / 2 + overDragDistance, control.height / 2); |
| verify(!control.pressed); |
| compare(control.swipe.position, -1.0); |
| verify(control.swipe.complete); |
| } |
| |
| function test_side() { |
| compare(SwipeDelegate.Left, 1.0); |
| compare(SwipeDelegate.Right, -1.0); |
| } |
| |
| function test_open_side_data() { |
| return [ |
| { tag: "left", side: SwipeDelegate.Left, position: 1, complete: true, left: greenLeftComponent, right: null, behind: null }, |
| { tag: "right", side: SwipeDelegate.Right, position: -1, complete: true, left: null, right: redRightComponent, behind: null }, |
| { tag: "behind,left", side: SwipeDelegate.Left, position: 1, complete: true, left: null, right: null, behind: greenLeftComponent }, |
| { tag: "behind,right", side: SwipeDelegate.Right, position: -1, complete: true, left: null, right: null, behind: redRightComponent }, |
| { tag: "left,behind", side: SwipeDelegate.Left, position: 1, complete: true, left: null, right: null, behind: greenLeftComponent }, |
| { tag: "right,behind", side: SwipeDelegate.Right, position: -1, complete: true, left: null, right: null, behind: redRightComponent }, |
| { tag: "left,null", side: SwipeDelegate.Left, position: 0, complete: false, left: null, right: null, behind: null }, |
| { tag: "right,null", side: SwipeDelegate.Right, position: 0, complete: false, left: null, right: null, behind: null }, |
| { tag: "invalid", side: 0, position: 0, complete: false, left: greenLeftComponent, right: null, behind: null } |
| ] |
| } |
| |
| function test_open_side(data) { |
| var control = createTemporaryObject(emptySwipeDelegateComponent, testCase, |
| {"swipe.left": data.left, "swipe.right": data.right, "swipe.behind": data.behind}); |
| verify(control); |
| |
| control.swipe.open(data.side); |
| tryCompare(control.swipe, "position", data.position); |
| tryCompare(control.swipe, "complete", data.complete); |
| } |
| |
| Component { |
| id: openSwipeDelegateComponent |
| |
| SwipeDelegate { |
| text: "SwipeDelegate" |
| width: 150 |
| |
| onClicked: swipe.open(SwipeDelegate.Right) |
| |
| swipe.right: Item { |
| width: parent.width |
| height: parent.height |
| } |
| } |
| } |
| |
| function test_open() { |
| var control = createTemporaryObject(openSwipeDelegateComponent, testCase); |
| verify(control); |
| |
| mouseClick(control); |
| tryCompare(control.swipe, "position", SwipeDelegate.Right); |
| tryCompare(control.background, "x", -control.background.width); |
| |
| // Swiping after opening should work as normal. |
| swipe(control, SwipeDelegate.Right, 0.0); |
| tryCompare(control.swipe, "position", 0.0); |
| tryCompare(control.background, "x", 0); |
| } |
| |
| Component { |
| id: animationSwipeDelegateComponent |
| |
| SwipeDelegate { |
| id: control |
| text: "SwipeDelegate" |
| width: 150 |
| swipe.left: greenLeftComponent |
| swipe.right: redRightComponent |
| swipe.transition: null |
| |
| property alias behavior: xBehavior |
| property alias animation: numberAnimation |
| |
| background: Rectangle { |
| color: control.down ? "#ccc" : "#fff" |
| |
| Behavior on x { |
| id: xBehavior |
| enabled: !control.down |
| |
| NumberAnimation { |
| id: numberAnimation |
| easing.type: Easing.InOutCubic |
| duration: 400 |
| } |
| } |
| } |
| } |
| } |
| |
| function test_animations() { |
| // Test that animations are run when releasing from a drag. |
| var control = createTemporaryObject(animationSwipeDelegateComponent, testCase); |
| verify(control); |
| |
| mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton); |
| mouseMove(control, control.width - 1, control.height / 2); |
| verify(control.down); |
| verify(!control.behavior.enabled); |
| verify(!control.animation.running); |
| |
| mouseRelease(control, control.width - 1, control.height / 2, Qt.LeftButton); |
| verify(!control.down); |
| verify(control.behavior.enabled); |
| verify(control.animation.running); |
| } |
| |
| function test_spacing() { |
| var control = createTemporaryObject(swipeDelegateComponent, testCase, { text: "Some long, long, long text" }) |
| verify(control) |
| verify(control.contentItem.implicitWidth + control.leftPadding + control.rightPadding > control.background.implicitWidth) |
| |
| var textLabel = findChild(control.contentItem, "label") |
| verify(textLabel) |
| |
| // The implicitWidth of the IconLabel that all buttons use as their contentItem |
| // should be equal to the implicitWidth of the Text while no icon is set. |
| compare(control.contentItem.implicitWidth, textLabel.implicitWidth) |
| |
| // That means that spacing shouldn't affect it. |
| control.spacing += 100 |
| compare(control.contentItem.implicitWidth, textLabel.implicitWidth) |
| |
| // The implicitWidth of the SwipeDelegate itself should, therefore, also never include spacing while no icon is set. |
| compare(control.implicitWidth, textLabel.implicitWidth + control.leftPadding + control.rightPadding) |
| } |
| |
| function test_display_data() { |
| return [ |
| { "tag": "IconOnly", display: SwipeDelegate.IconOnly }, |
| { "tag": "TextOnly", display: SwipeDelegate.TextOnly }, |
| { "tag": "TextUnderIcon", display: SwipeDelegate.TextUnderIcon }, |
| { "tag": "TextBesideIcon", display: SwipeDelegate.TextBesideIcon }, |
| { "tag": "IconOnly, mirrored", display: SwipeDelegate.IconOnly, mirrored: true }, |
| { "tag": "TextOnly, mirrored", display: SwipeDelegate.TextOnly, mirrored: true }, |
| { "tag": "TextUnderIcon, mirrored", display: SwipeDelegate.TextUnderIcon, mirrored: true }, |
| { "tag": "TextBesideIcon, mirrored", display: SwipeDelegate.TextBesideIcon, mirrored: true } |
| ] |
| } |
| |
| function test_display(data) { |
| var control = createTemporaryObject(swipeDelegateComponent, testCase, { |
| text: "SwipeDelegate", |
| display: data.display, |
| width: 400, |
| "icon.source": "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/check.png", |
| "LayoutMirroring.enabled": !!data.mirrored |
| }) |
| verify(control) |
| compare(control.icon.source, "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/check.png") |
| |
| var iconImage = findChild(control.contentItem, "image") |
| var textLabel = findChild(control.contentItem, "label") |
| |
| switch (control.display) { |
| case SwipeDelegate.IconOnly: |
| verify(iconImage) |
| verify(!textLabel) |
| compare(iconImage.x, (control.availableWidth - iconImage.width) / 2) |
| compare(iconImage.y, (control.availableHeight - iconImage.height) / 2) |
| break; |
| case SwipeDelegate.TextOnly: |
| verify(!iconImage) |
| verify(textLabel) |
| compare(textLabel.x, control.mirrored ? control.availableWidth - textLabel.width : 0) |
| compare(textLabel.y, (control.availableHeight - textLabel.height) / 2) |
| break; |
| case SwipeDelegate.TextUnderIcon: |
| verify(iconImage) |
| verify(textLabel) |
| compare(iconImage.x, (control.availableWidth - iconImage.width) / 2) |
| compare(textLabel.x, (control.availableWidth - textLabel.width) / 2) |
| verify(iconImage.y < textLabel.y) |
| break; |
| case SwipeDelegate.TextBesideIcon: |
| verify(iconImage) |
| verify(textLabel) |
| if (control.mirrored) |
| verify(textLabel.x < iconImage.x) |
| else |
| verify(iconImage.x < textLabel.x) |
| compare(iconImage.y, (control.availableHeight - iconImage.height) / 2) |
| compare(textLabel.y, (control.availableHeight - textLabel.height) / 2) |
| break; |
| } |
| } |
| } |