Add dragging properties to Flickable

This allows the user to determine when a movement is due to the
user dragging the view directly, i.e. excluding any flick that
occurs after the touch is released.

Change-Id: Idf4b699946f808da6fa34ec21a3d2cb2f0ec9de6
Fixes: QTBUG-19685
Reviewed-on: http://codereview.qt.nokia.com/2310
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Bea Lam <bea.lam@nokia.com>
This commit is contained in:
Martin Jones 2011-07-28 10:49:37 +10:00 committed by Qt by Nokia
parent 02bf8f421d
commit 68ef13d791
5 changed files with 226 additions and 3 deletions

View File

@ -43,6 +43,8 @@ set binding when its \e when clause becomes false.
QDeclarativeExpression can now be directly (and more efficiently) constructed from a QDeclarativeExpression can now be directly (and more efficiently) constructed from a
QDeclarativeScriptString. QDeclarativeScriptString.
Flickable: added dragging, draggingHorizontally and draggingVerically properties.
\section2 QtQuick 1 is now a separate library and module \section2 QtQuick 1 is now a separate library and module
Writing C++ applications using QtQuick 1 specific API, i.e. QDeclarativeView or QDeclarativeItem Writing C++ applications using QtQuick 1 specific API, i.e. QDeclarativeView or QDeclarativeItem

View File

@ -428,6 +428,34 @@ void QSGFlickablePrivate::updateBeginningEnd()
visibleArea->updateVisible(); visibleArea->updateVisible();
} }
/*
XXXTODO add docs describing moving, dragging, flicking properties, e.g.
When the user starts dragging the Flickable, the dragging and moving properties
will be true.
If the velocity is sufficient when the drag is ended, flicking may begin.
The moving properties will remain true until all dragging and flicking
is finished.
*/
/*!
\qmlsignal QtQuick2::Flickable::onDragStarted()
This handler is called when the view starts to be dragged due to user
interaction.
*/
/*!
\qmlsignal QtQuick2::Flickable::onDragEnded()
This handler is called when the user stops dragging the view.
If the velocity of the drag is suffient at the time the
touch/mouse button is released then a flick will start.
*/
QSGFlickable::QSGFlickable(QSGItem *parent) QSGFlickable::QSGFlickable(QSGItem *parent)
: QSGItem(*(new QSGFlickablePrivate), parent) : QSGItem(*(new QSGFlickablePrivate), parent)
{ {
@ -698,6 +726,7 @@ void QSGFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
} }
if (hMoved || vMoved) { if (hMoved || vMoved) {
draggingStarting();
q->movementStarting(); q->movementStarting();
q->viewportMoved(); q->viewportMoved();
} }
@ -724,8 +753,6 @@ void QSGFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *even
stealMouse = false; stealMouse = false;
q->setKeepMouseGrab(false); q->setKeepMouseGrab(false);
pressed = false; pressed = false;
if (!lastPosTime.isValid())
return;
// if we drag then pause before release we should not cause a flick. // if we drag then pause before release we should not cause a flick.
if (QSGItemPrivate::elapsed(lastPosTime) < 100) { if (QSGItemPrivate::elapsed(lastPosTime) < 100) {
@ -736,6 +763,11 @@ void QSGFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *even
vData.velocity = 0.0; vData.velocity = 0.0;
} }
draggingEnding();
if (!lastPosTime.isValid())
return;
vTime = timeline.time(); vTime = timeline.time();
qreal velocity = vData.velocity; qreal velocity = vData.velocity;
@ -1231,6 +1263,7 @@ void QSGFlickable::mouseUngrabEvent()
// if our mouse grab has been removed (probably by another Flickable), // if our mouse grab has been removed (probably by another Flickable),
// fix our state // fix our state
d->pressed = false; d->pressed = false;
d->draggingEnding();
d->stealMouse = false; d->stealMouse = false;
setKeepMouseGrab(false); setKeepMouseGrab(false);
} }
@ -1375,6 +1408,68 @@ bool QSGFlickable::isFlickingVertically() const
return d->flickingVertically; return d->flickingVertically;
} }
/*!
\qmlproperty bool QtQuick2::Flickable::dragging
\qmlproperty bool QtQuick2::Flickable::draggingHorizontally
\qmlproperty bool QtQuick2::Flickable::draggingVertically
These properties describe whether the view is currently moving horizontally,
vertically or in either direction, due to the user dragging the view.
*/
bool QSGFlickable::isDragging() const
{
Q_D(const QSGFlickable);
return d->hData.dragging || d->vData.dragging;
}
bool QSGFlickable::isDraggingHorizontally() const
{
Q_D(const QSGFlickable);
return d->hData.dragging;
}
bool QSGFlickable::isDraggingVertically() const
{
Q_D(const QSGFlickable);
return d->vData.dragging;
}
void QSGFlickablePrivate::draggingStarting()
{
Q_Q(QSGFlickable);
bool wasDragging = hData.dragging || vData.dragging;
if (hMoved && !hData.dragging) {
hData.dragging = true;
emit q->draggingHorizontallyChanged();
}
if (vMoved && !vData.dragging) {
vData.dragging = true;
emit q->draggingVerticallyChanged();
}
if (!wasDragging && (hData.dragging || vData.dragging)) {
emit q->draggingChanged();
emit q->dragStarted();
}
}
void QSGFlickablePrivate::draggingEnding()
{
Q_Q(QSGFlickable);
bool wasDragging = hData.dragging || vData.dragging;
if (hData.dragging) {
hData.dragging = false;
emit q->draggingHorizontallyChanged();
}
if (vData.dragging) {
vData.dragging = false;
emit q->draggingVerticallyChanged();
}
if (wasDragging && !hData.dragging && !vData.dragging) {
emit q->draggingChanged();
emit q->dragEnded();
}
}
int QSGFlickable::pressDelay() const int QSGFlickable::pressDelay() const
{ {
Q_D(const QSGFlickable); Q_D(const QSGFlickable);

View File

@ -75,6 +75,9 @@ class Q_AUTOTEST_EXPORT QSGFlickable : public QSGItem
Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged) Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged)
Q_PROPERTY(bool flickingHorizontally READ isFlickingHorizontally NOTIFY flickingHorizontallyChanged) Q_PROPERTY(bool flickingHorizontally READ isFlickingHorizontally NOTIFY flickingHorizontallyChanged)
Q_PROPERTY(bool flickingVertically READ isFlickingVertically NOTIFY flickingVerticallyChanged) Q_PROPERTY(bool flickingVertically READ isFlickingVertically NOTIFY flickingVerticallyChanged)
Q_PROPERTY(bool dragging READ isDragging NOTIFY draggingChanged)
Q_PROPERTY(bool draggingHorizontally READ isDraggingHorizontally NOTIFY draggingHorizontallyChanged)
Q_PROPERTY(bool draggingVertically READ isDraggingVertically NOTIFY draggingVerticallyChanged)
Q_PROPERTY(FlickableDirection flickableDirection READ flickableDirection WRITE setFlickableDirection NOTIFY flickableDirectionChanged) Q_PROPERTY(FlickableDirection flickableDirection READ flickableDirection WRITE setFlickableDirection NOTIFY flickableDirectionChanged)
Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged) Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged)
@ -123,6 +126,9 @@ public:
bool isFlicking() const; bool isFlicking() const;
bool isFlickingHorizontally() const; bool isFlickingHorizontally() const;
bool isFlickingVertically() const; bool isFlickingVertically() const;
bool isDragging() const;
bool isDraggingHorizontally() const;
bool isDraggingVertically() const;
int pressDelay() const; int pressDelay() const;
void setPressDelay(int delay); void setPressDelay(int delay);
@ -164,6 +170,9 @@ Q_SIGNALS:
void flickingChanged(); void flickingChanged();
void flickingHorizontallyChanged(); void flickingHorizontallyChanged();
void flickingVerticallyChanged(); void flickingVerticallyChanged();
void draggingChanged();
void draggingHorizontallyChanged();
void draggingVerticallyChanged();
void horizontalVelocityChanged(); void horizontalVelocityChanged();
void verticalVelocityChanged(); void verticalVelocityChanged();
void isAtBoundaryChanged(); void isAtBoundaryChanged();
@ -177,6 +186,8 @@ Q_SIGNALS:
void movementEnded(); void movementEnded();
void flickStarted(); void flickStarted();
void flickEnded(); void flickEnded();
void dragStarted();
void dragEnded();
protected: protected:
virtual bool childMouseEventFilter(QSGItem *, QEvent *); virtual bool childMouseEventFilter(QSGItem *, QEvent *);

View File

@ -96,7 +96,7 @@ public:
struct AxisData { struct AxisData {
AxisData(QSGFlickablePrivate *fp, void (QSGFlickablePrivate::*func)(qreal)) AxisData(QSGFlickablePrivate *fp, void (QSGFlickablePrivate::*func)(qreal))
: move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true) : move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true)
, fixingUp(false), inOvershoot(false) , fixingUp(false), inOvershoot(false), dragging(false)
{} {}
void reset() { void reset() {
@ -123,6 +123,7 @@ public:
bool atBeginning : 1; bool atBeginning : 1;
bool fixingUp : 1; bool fixingUp : 1;
bool inOvershoot : 1; bool inOvershoot : 1;
bool dragging : 1;
}; };
void flickX(qreal velocity); void flickX(qreal velocity);
@ -147,6 +148,9 @@ public:
void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &); void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &);
void draggingStarting();
void draggingEnding();
public: public:
QSGItem *contentItem; QSGItem *contentItem;

View File

@ -77,6 +77,7 @@ private slots:
void resizeContent(); void resizeContent();
void returnToBounds(); void returnToBounds();
void wheel(); void wheel();
void movingAndDragging();
private: private:
QDeclarativeEngine engine; QDeclarativeEngine engine;
@ -400,6 +401,116 @@ void tst_qsgflickable::wheel()
delete canvas; delete canvas;
} }
void tst_qsgflickable::movingAndDragging()
{
QSGView *canvas = new QSGView;
canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/flickable03.qml"));
canvas->show();
canvas->setFocus();
QVERIFY(canvas->rootObject() != 0);
QSGFlickable *flickable = qobject_cast<QSGFlickable*>(canvas->rootObject());
QVERIFY(flickable != 0);
QSignalSpy vDragSpy(flickable, SIGNAL(draggingVerticallyChanged()));
QSignalSpy hDragSpy(flickable, SIGNAL(draggingHorizontallyChanged()));
QSignalSpy dragSpy(flickable, SIGNAL(draggingChanged()));
QSignalSpy vMoveSpy(flickable, SIGNAL(movingVerticallyChanged()));
QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged()));
QSignalSpy moveSpy(flickable, SIGNAL(movingChanged()));
QSignalSpy dragStartSpy(flickable, SIGNAL(dragStarted()));
QSignalSpy dragEndSpy(flickable, SIGNAL(dragEnded()));
//Vertical
QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50, 90));
QMouseEvent moveEvent(QEvent::MouseMove, QPoint(50, 80), Qt::LeftButton, Qt::LeftButton, 0);
QApplication::sendEvent(canvas, &moveEvent);
moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(50, 70), Qt::LeftButton, Qt::LeftButton, 0);
QApplication::sendEvent(canvas, &moveEvent);
moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(50, 60), Qt::LeftButton, Qt::LeftButton, 0);
QApplication::sendEvent(canvas, &moveEvent);
QVERIFY(!flickable->isDraggingHorizontally());
QVERIFY(flickable->isDraggingVertically());
QVERIFY(flickable->isDragging());
QCOMPARE(vDragSpy.count(), 1);
QCOMPARE(dragSpy.count(), 1);
QCOMPARE(hDragSpy.count(), 0);
QCOMPARE(dragStartSpy.count(), 1);
QCOMPARE(dragEndSpy.count(), 0);
QVERIFY(!flickable->isMovingHorizontally());
QVERIFY(flickable->isMovingVertically());
QVERIFY(flickable->isMoving());
QCOMPARE(vMoveSpy.count(), 1);
QCOMPARE(moveSpy.count(), 1);
QCOMPARE(hMoveSpy.count(), 0);
QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50, 60));
QVERIFY(!flickable->isDraggingVertically());
QVERIFY(!flickable->isDragging());
QCOMPARE(vDragSpy.count(), 2);
QCOMPARE(dragSpy.count(), 2);
QCOMPARE(hDragSpy.count(), 0);
QCOMPARE(dragStartSpy.count(), 1);
QCOMPARE(dragEndSpy.count(), 1);
// Don't test moving because a flick could occur
//Horizontal
vDragSpy.clear();
hDragSpy.clear();
dragSpy.clear();
vMoveSpy.clear();
hMoveSpy.clear();
moveSpy.clear();
dragStartSpy.clear();
dragEndSpy.clear();
QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(90, 50));
moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(80, 50), Qt::LeftButton, Qt::LeftButton, 0);
QApplication::sendEvent(canvas, &moveEvent);
moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(70, 50), Qt::LeftButton, Qt::LeftButton, 0);
QApplication::sendEvent(canvas, &moveEvent);
moveEvent = QMouseEvent(QEvent::MouseMove, QPoint(60, 50), Qt::LeftButton, Qt::LeftButton, 0);
QApplication::sendEvent(canvas, &moveEvent);
QVERIFY(flickable->isDraggingHorizontally());
QVERIFY(flickable->isDragging());
QCOMPARE(vDragSpy.count(), 0);
QCOMPARE(dragSpy.count(), 1);
QCOMPARE(hDragSpy.count(), 1);
QCOMPARE(dragStartSpy.count(), 1);
QCOMPARE(dragEndSpy.count(), 0);
QVERIFY(!flickable->isMovingVertically());
QVERIFY(flickable->isMovingHorizontally());
QVERIFY(flickable->isMoving());
QCOMPARE(vMoveSpy.count(), 0);
QCOMPARE(moveSpy.count(), 1);
QCOMPARE(hMoveSpy.count(), 1);
QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(60, 50));
QVERIFY(!flickable->isDraggingHorizontally());
QVERIFY(!flickable->isDragging());
QCOMPARE(vDragSpy.count(), 0);
QCOMPARE(dragSpy.count(), 2);
QCOMPARE(hDragSpy.count(), 2);
QCOMPARE(dragStartSpy.count(), 1);
QCOMPARE(dragEndSpy.count(), 1);
// Don't test moving because a flick could occur
delete canvas;
}
template<typename T> template<typename T>
T *tst_qsgflickable::findItem(QSGItem *parent, const QString &objectName) T *tst_qsgflickable::findItem(QSGItem *parent, const QString &objectName)