aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@qt.io>2016-11-28 10:57:45 +0100
committerJ-P Nurmi <jpnurmi@qt.io>2016-12-14 19:46:05 +0000
commitd49ab00f2b9c99da48d9ddef2b0627a86d54b95e (patch)
treedde0f77d4353f3cc6adfef085ba19ab78ac7bc57
parentbc6414762e48f12f6680ee6bb667c49c64fbd32a (diff)
SwipeDelegate: transition swipe.position
Previously the value of swipe.position jumped on mouse/touch release, or when open() or close() was called. Transitioning the swipe.position value smoothly to the target value makes it possible to use it as opacity/scale/etc. for the side items. Notice that this is backwards compatible with the old behavior. If no transition is set, the value jumps the same way it did before. In that case, Behavior on x makes the movement smooth. Task-number: QTBUG-57242 Change-Id: Id9c06b5b08fa73f2f575787e16dc6e20e4ccb545 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r--src/imports/controls/SwipeDelegate.qml18
-rw-r--r--src/imports/controls/material/SwipeDelegate.qml18
-rw-r--r--src/imports/controls/universal/SwipeDelegate.qml18
-rw-r--r--src/quicktemplates2/qquickswipe_p.h8
-rw-r--r--src/quicktemplates2/qquickswipedelegate.cpp109
-rw-r--r--tests/auto/controls/data/tst_swipedelegate.qml31
6 files changed, 126 insertions, 76 deletions
diff --git a/src/imports/controls/SwipeDelegate.qml b/src/imports/controls/SwipeDelegate.qml
index 099ee117..1c900b52 100644
--- a/src/imports/controls/SwipeDelegate.qml
+++ b/src/imports/controls/SwipeDelegate.qml
@@ -52,6 +52,8 @@ T.SwipeDelegate {
padding: 12
spacing: 12
+ swipe.rebound: Transition { SmoothedAnimation { velocity: 3; easing.type: Easing.InOutCubic } }
+
contentItem: Text {
leftPadding: control.mirrored ? (control.indicator ? control.indicator.width : 0) + control.spacing : 0
rightPadding: !control.mirrored ? (control.indicator ? control.indicator.width : 0) + control.spacing : 0
@@ -63,27 +65,11 @@ T.SwipeDelegate {
visible: control.text
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
-
- Behavior on x {
- enabled: !control.down
- NumberAnimation {
- easing.type: Easing.InOutCubic
- duration: 400
- }
- }
}
background: Rectangle {
color: control.visualFocus
? (control.down ? Default.focusPressedColor : Default.delegateFocusColor)
: (control.down ? Default.delegatePressedColor : Default.backgroundColor)
-
- Behavior on x {
- enabled: !control.down
- NumberAnimation {
- easing.type: Easing.InOutCubic
- duration: 400
- }
- }
}
}
diff --git a/src/imports/controls/material/SwipeDelegate.qml b/src/imports/controls/material/SwipeDelegate.qml
index c6240910..4247ddf1 100644
--- a/src/imports/controls/material/SwipeDelegate.qml
+++ b/src/imports/controls/material/SwipeDelegate.qml
@@ -54,6 +54,8 @@ T.SwipeDelegate {
bottomPadding: 8
spacing: 16
+ swipe.rebound: Transition { SmoothedAnimation { velocity: 3; easing.type: Easing.InOutCubic } }
+
contentItem: Text {
leftPadding: !control.mirrored ? (control.indicator ? control.indicator.width + control.spacing : 0) : 0
rightPadding: control.mirrored ? (control.indicator ? control.indicator.width + control.spacing : 0) : 0
@@ -65,14 +67,6 @@ T.SwipeDelegate {
visible: control.text
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
-
- Behavior on x {
- enabled: !control.down
- NumberAnimation {
- easing.type: Easing.InOutCubic
- duration: 400
- }
- }
}
background: Rectangle {
@@ -98,13 +92,5 @@ T.SwipeDelegate {
color: control.Material.rippleColor
enabled: control.swipe.position === 0
}
-
- Behavior on x {
- enabled: !control.down
- NumberAnimation {
- easing.type: Easing.InOutCubic
- duration: 400
- }
- }
}
}
diff --git a/src/imports/controls/universal/SwipeDelegate.qml b/src/imports/controls/universal/SwipeDelegate.qml
index 0b888e70..193d1ab1 100644
--- a/src/imports/controls/universal/SwipeDelegate.qml
+++ b/src/imports/controls/universal/SwipeDelegate.qml
@@ -54,6 +54,8 @@ T.SwipeDelegate {
topPadding: padding - 1
bottomPadding: padding + 1
+ swipe.rebound: Transition { SmoothedAnimation { velocity: 3; easing.type: Easing.InOutCubic } }
+
contentItem: Text {
leftPadding: !control.mirrored ? (control.indicator ? control.indicator.width : 0) + control.spacing : 0
rightPadding: control.mirrored ? (control.indicator ? control.indicator.width : 0) + control.spacing : 0
@@ -68,14 +70,6 @@ T.SwipeDelegate {
opacity: enabled ? 1.0 : 0.2
color: control.Universal.foreground
-
- Behavior on x {
- enabled: !control.down
- NumberAnimation {
- easing.type: Easing.InOutCubic
- duration: 400
- }
- }
}
background: Rectangle {
@@ -94,13 +88,5 @@ T.SwipeDelegate {
opacity: control.Universal.theme === Universal.Light ? 0.4 : 0.6
}
}
-
- Behavior on x {
- enabled: !control.down
- NumberAnimation {
- easing.type: Easing.InOutCubic
- duration: 400
- }
- }
}
}
diff --git a/src/quicktemplates2/qquickswipe_p.h b/src/quicktemplates2/qquickswipe_p.h
index bedf5b1f..2201bb06 100644
--- a/src/quicktemplates2/qquickswipe_p.h
+++ b/src/quicktemplates2/qquickswipe_p.h
@@ -56,12 +56,13 @@ QT_BEGIN_NAMESPACE
class QQmlComponent;
class QQuickItem;
+class QQuickTransition;
class QQuickSwipePrivate;
class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipe : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal position READ position NOTIFY positionChanged FINAL)
+ Q_PROPERTY(qreal position READ position WRITE setPosition NOTIFY positionChanged FINAL)
Q_PROPERTY(bool complete READ isComplete NOTIFY completeChanged FINAL)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged FINAL) // REVISION 2
Q_PROPERTY(QQmlComponent *left READ left WRITE setLeft NOTIFY leftChanged FINAL)
@@ -70,6 +71,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSwipe : public QObject
Q_PROPERTY(QQuickItem *leftItem READ leftItem NOTIFY leftItemChanged FINAL)
Q_PROPERTY(QQuickItem *behindItem READ behindItem NOTIFY behindItemChanged FINAL)
Q_PROPERTY(QQuickItem *rightItem READ rightItem NOTIFY rightItemChanged FINAL)
+ Q_PROPERTY(QQuickTransition *rebound READ rebound WRITE setRebound NOTIFY reboundChanged FINAL) // REVISION 2
public:
explicit QQuickSwipe(QQuickSwipeDelegate *control);
@@ -101,6 +103,9 @@ public:
QQuickItem *rightItem() const;
void setRightItem(QQuickItem *item);
+ QQuickTransition *rebound() const;
+ void setRebound(QQuickTransition *rebound);
+
Q_REVISION(2) Q_INVOKABLE void open(QQuickSwipeDelegate::Side side);
Q_REVISION(1) Q_INVOKABLE void close();
@@ -115,6 +120,7 @@ Q_SIGNALS:
void leftItemChanged();
void behindItemChanged();
void rightItemChanged();
+ /*Q_REVISION(2)*/ void reboundChanged();
private:
Q_DISABLE_COPY(QQuickSwipe)
diff --git a/src/quicktemplates2/qquickswipedelegate.cpp b/src/quicktemplates2/qquickswipedelegate.cpp
index 2f7e7382..5504dfc8 100644
--- a/src/quicktemplates2/qquickswipedelegate.cpp
+++ b/src/quicktemplates2/qquickswipedelegate.cpp
@@ -44,6 +44,9 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtQml/qqmlinfo.h>
+#include <QtQuick/private/qquickanimation_p.h>
+#include <QtQuick/private/qquicktransition_p.h>
+#include <QtQuick/private/qquicktransitionmanager_p_p.h>
QT_BEGIN_NAMESPACE
@@ -113,6 +116,48 @@ namespace {
};
}
+class QQuickSwipeTransitionManager : public QQuickTransitionManager
+{
+public:
+ QQuickSwipeTransitionManager(QQuickSwipe *swipe);
+
+ void transition(QQuickTransition *rebound, qreal position);
+
+protected:
+ void finished() override;
+
+private:
+ QQuickSwipe *m_swipe;
+};
+
+QQuickSwipeTransitionManager::QQuickSwipeTransitionManager(QQuickSwipe *swipe)
+ : m_swipe(swipe)
+{
+}
+
+void QQuickSwipeTransitionManager::transition(QQuickTransition *rebound, qreal position)
+{
+ qmlExecuteDeferred(rebound);
+
+ QQmlProperty defaultTarget(m_swipe, QLatin1String("position"));
+ QQmlListProperty<QQuickAbstractAnimation> animations = rebound->animations();
+ const int count = animations.count(&animations);
+ for (int i = 0; i < count; ++i) {
+ QQuickAbstractAnimation *anim = animations.at(&animations, i);
+ anim->setDefaultTarget(defaultTarget);
+ }
+
+ QList<QQuickStateAction> actions;
+ actions << QQuickStateAction(m_swipe, QLatin1String("position"), position);
+ QQuickTransitionManager::transition(actions, rebound, m_swipe);
+}
+
+void QQuickSwipeTransitionManager::finished()
+{
+ const qreal pos = m_swipe->position();
+ m_swipe->setComplete(qFuzzyCompare(qAbs(pos), 1.0));
+}
+
class QQuickSwipePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickSwipe)
@@ -130,7 +175,9 @@ public:
right(nullptr),
leftItem(nullptr),
behindItem(nullptr),
- rightItem(nullptr)
+ rightItem(nullptr),
+ rebound(nullptr),
+ transitionManager(nullptr)
{
}
@@ -152,6 +199,9 @@ public:
bool hasDelegates() const;
+ bool isTransitioning() const;
+ void transition(qreal position);
+
QQuickSwipeDelegate *control;
// Same range as position, but is set before press events so that we can
// keep track of which direction the user must swipe when using left and right delegates.
@@ -169,6 +219,8 @@ public:
QQuickItem *leftItem;
QQuickItem *behindItem;
QQuickItem *rightItem;
+ QQuickTransition *rebound;
+ QScopedPointer<QQuickSwipeTransitionManager> transitionManager;
};
QQuickSwipePrivate *QQuickSwipePrivate::get(QQuickSwipe *swipe)
@@ -358,6 +410,26 @@ bool QQuickSwipePrivate::hasDelegates() const
return left || right || behind;
}
+bool QQuickSwipePrivate::isTransitioning() const
+{
+ return transitionManager && transitionManager->isRunning();
+}
+
+void QQuickSwipePrivate::transition(qreal position)
+{
+ Q_Q(QQuickSwipe);
+ if (!rebound) {
+ q->setPosition(position);
+ q->setComplete(qFuzzyCompare(qAbs(position), 1.0));
+ return;
+ }
+
+ if (!transitionManager)
+ transitionManager.reset(new QQuickSwipeTransitionManager(q));
+
+ transitionManager->transition(rebound, position);
+}
+
QQuickSwipe::QQuickSwipe(QQuickSwipeDelegate *control) :
QObject(*(new QQuickSwipePrivate(control)))
{
@@ -592,6 +664,22 @@ void QQuickSwipe::setEnabled(bool enabled)
emit enabledChanged();
}
+QQuickTransition *QQuickSwipe::rebound() const
+{
+ Q_D(const QQuickSwipe);
+ return d->rebound;
+}
+
+void QQuickSwipe::setRebound(QQuickTransition *rebound)
+{
+ Q_D(QQuickSwipe);
+ if (rebound == d->rebound)
+ return;
+
+ d->rebound = rebound;
+ emit reboundChanged();
+}
+
void QQuickSwipe::open(QQuickSwipeDelegate::Side side)
{
Q_D(QQuickSwipe);
@@ -600,8 +688,7 @@ void QQuickSwipe::open(QQuickSwipeDelegate::Side side)
|| (!d->right && !d->behind && side == QQuickSwipeDelegate::Right))
return;
- setPosition(side);
- setComplete(true);
+ d->transition(side);
d->wasComplete = true;
d->velocityCalculator.reset();
d->positionBeforePress = d->position;
@@ -610,8 +697,7 @@ void QQuickSwipe::open(QQuickSwipeDelegate::Side side)
void QQuickSwipe::close()
{
Q_D(QQuickSwipe);
- setPosition(0);
- setComplete(false);
+ d->transition(0.0);
d->wasComplete = false;
d->positionBeforePress = 0.0;
d->velocityCalculator.reset();
@@ -748,6 +834,8 @@ bool QQuickSwipeDelegatePrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEv
position = distance > 0 ? normalizedDistance - 1.0 : normalizedDistance + 1.0;
}
+ if (swipePrivate->isTransitioning())
+ swipePrivate->transitionManager->cancel();
swipe.setPosition(position);
}
} else {
@@ -794,17 +882,14 @@ bool QQuickSwipeDelegatePrivate::handleMouseReleaseEvent(QQuickItem *item, QMous
const qreal swipeVelocity = swipePrivate->velocityCalculator.velocity().x();
if (swipePrivate->position > 0.5 ||
(swipePrivate->position > 0.0 && swipeVelocity > exposeVelocityThreshold)) {
- swipe.setPosition(1.0);
- swipe.setComplete(true);
+ swipePrivate->transition(1.0);
swipePrivate->wasComplete = true;
} else if (swipePrivate->position < -0.5 ||
(swipePrivate->position < 0.0 && swipeVelocity < -exposeVelocityThreshold)) {
- swipe.setPosition(-1.0);
- swipe.setComplete(true);
+ swipePrivate->transition(-1.0);
swipePrivate->wasComplete = true;
- } else {
- swipe.setPosition(0.0);
- swipe.setComplete(false);
+ } else if (!swipePrivate->isTransitioning()) {
+ swipePrivate->transition(0.0);
swipePrivate->wasComplete = false;
}
diff --git a/tests/auto/controls/data/tst_swipedelegate.qml b/tests/auto/controls/data/tst_swipedelegate.qml
index a6e11246..27a1028b 100644
--- a/tests/auto/controls/data/tst_swipedelegate.qml
+++ b/tests/auto/controls/data/tst_swipedelegate.qml
@@ -40,7 +40,7 @@
import QtQuick 2.6
import QtTest 1.0
-import QtQuick.Controls 2.1
+import QtQuick.Controls 2.2
TestCase {
@@ -380,8 +380,8 @@ TestCase {
mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": false }], "canceled"];
mouseRelease(control, control.width / 2, control.height / 2);
verify(!control.pressed);
- compare(control.swipe.position, 1.0);
- verify(control.swipe.complete);
+ tryCompare(control.swipe, "position", 1.0);
+ tryCompare(control.swipe, "complete", true);
compare(completedSpy.count, 1);
verify(mouseSignalSequenceSpy.success);
verify(control.swipe.leftItem);
@@ -411,8 +411,8 @@ TestCase {
mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": false }], "canceled"];
mouseRelease(control, control.width * 0.4, control.height / 2);
verify(!control.pressed);
- compare(control.swipe.position, 1.0);
- verify(control.swipe.complete);
+ tryCompare(control.swipe, "position", 1.0);
+ tryCompare(control.swipe, "complete", true);
compare(completedSpy.count, 2);
verify(mouseSignalSequenceSpy.success);
tryCompare(control.contentItem, "x", control.width + control.leftPadding);
@@ -435,7 +435,7 @@ TestCase {
mouseSignalSequenceSpy.expectedSequence = [["pressedChanged", { "pressed": false }], "canceled"];
mouseRelease(control, control.width * -0.1, control.height / 2);
verify(!control.pressed);
- compare(control.swipe.position, 0.0);
+ tryCompare(control.swipe, "position", 0.0);
verify(!control.swipe.complete);
compare(completedSpy.count, 2);
verify(mouseSignalSequenceSpy.success);
@@ -572,7 +572,7 @@ TestCase {
// Returning back to a position of 0 and pressing on the control should
// result in the control being pressed.
mouseDrag(control, control.width / 2, control.height / 2, control.width * 0.6, 0);
- compare(control.swipe.position, 0);
+ tryCompare(control.swipe, "position", 0);
mousePress(control, control.width / 2, control.height / 2);
verify(control.pressed);
verify(!button.pressed);
@@ -730,8 +730,8 @@ TestCase {
mouseRelease(listView, firstItem.width / 2, firstItem.height / 2);
verify(!firstItem.pressed);
- compare(firstItem.swipe.position, 1.0);
- verify(firstItem.swipe.complete);
+ tryCompare(firstItem.swipe, "position", 1.0);
+ tryCompare(firstItem.swipe, "complete", true);
compare(listView.count, 3);
// Wait for it to settle down.
@@ -1032,7 +1032,7 @@ TestCase {
mouseMove(control, control.width / 2 + control.swipe.behindItem.width * 0.8, control.height / 2, Qt.LeftButton);
compare(control.swipe.position, -0.2);
mouseRelease(control, control.width / 2 + control.swipe.behindItem.width * 0.8, control.height / 2, Qt.LeftButton);
- compare(control.swipe.position, 0.0);
+ tryCompare(control.swipe, "position", 0.0);
// Try wrapping the other way.
swipe(control, 0.0, -1.0);
@@ -1043,7 +1043,7 @@ TestCase {
mouseMove(control, control.width / 2 - control.swipe.behindItem.width * 0.8, control.height / 2, Qt.LeftButton);
compare(control.swipe.position, 0.2);
mouseRelease(control, control.width / 2 - control.swipe.behindItem.width * 0.8, control.height / 2, Qt.LeftButton);
- compare(control.swipe.position, 0.0);
+ tryCompare(control.swipe, "position", 0.0);
control.destroy();
}
@@ -1475,8 +1475,8 @@ TestCase {
verify(control);
control.swipe.open(data.side);
- compare(control.swipe.position, data.position);
- compare(control.swipe.complete, data.complete);
+ tryCompare(control.swipe, "position", data.position);
+ tryCompare(control.swipe, "complete", data.complete);
control.destroy();
}
@@ -1502,12 +1502,12 @@ TestCase {
verify(control);
mouseClick(control);
- compare(control.swipe.position, SwipeDelegate.Right);
+ 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);
- compare(control.swipe.position, 0.0);
+ tryCompare(control.swipe, "position", 0.0);
tryCompare(control.background, "x", 0);
control.destroy();
@@ -1522,6 +1522,7 @@ TestCase {
width: 150
swipe.left: greenLeftComponent
swipe.right: redRightComponent
+ swipe.rebound: null
property alias behavior: xBehavior
property alias animation: numberAnimation