demos/embedded/flickable/flickable.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demos/embedded/flickable/flickable.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,284 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "flickable.h"
+
+#include <QtCore>
+#include <QtGui>
+
+class FlickableTicker: QObject
+{
+public:
+    FlickableTicker(Flickable *scroller) {
+        m_scroller = scroller;
+    }
+
+    void start(int interval) {
+        if (!m_timer.isActive())
+            m_timer.start(interval, this);
+    }
+
+    void stop() {
+        m_timer.stop();
+    }
+
+protected:
+    void timerEvent(QTimerEvent *event) {
+        Q_UNUSED(event);
+        m_scroller->tick();
+    }
+
+private:
+    Flickable *m_scroller;
+    QBasicTimer m_timer;
+};
+
+class FlickablePrivate
+{
+public:
+    typedef enum {
+        Steady,
+        Pressed,
+        ManualScroll,
+        AutoScroll,
+        Stop
+    } State;
+
+    State state;
+    int threshold;
+    QPoint pressPos;
+    QPoint offset;
+    QPoint delta;
+    QPoint speed;
+    FlickableTicker *ticker;
+    QTime timeStamp;
+    QWidget *target;
+    QList<QEvent*> ignoreList;
+};
+
+Flickable::Flickable()
+{
+    d = new FlickablePrivate;
+    d->state = FlickablePrivate::Steady;
+    d->threshold = 10;
+    d->ticker = new FlickableTicker(this);
+    d->timeStamp = QTime::currentTime();
+    d->target = 0;
+}
+
+Flickable::~Flickable()
+{
+    delete d;
+}
+
+void Flickable::setThreshold(int th)
+{
+    if (th >= 0)
+        d->threshold = th;
+}
+
+int Flickable::threshold() const
+{
+    return d->threshold;
+}
+
+void Flickable::setAcceptMouseClick(QWidget *target)
+{
+    d->target = target;
+}
+
+static QPoint deaccelerate(const QPoint &speed, int a = 1, int max = 64)
+{
+    int x = qBound(-max, speed.x(), max);
+    int y = qBound(-max, speed.y(), max);
+    x = (x == 0) ? x : (x > 0) ? qMax(0, x - a) : qMin(0, x + a);
+    y = (y == 0) ? y : (y > 0) ? qMax(0, y - a) : qMin(0, y + a);
+    return QPoint(x, y);
+}
+
+void Flickable::handleMousePress(QMouseEvent *event)
+{
+    event->ignore();
+
+    if (event->button() != Qt::LeftButton)
+        return;
+
+    if (d->ignoreList.removeAll(event))
+        return;
+
+    switch (d->state) {
+
+    case FlickablePrivate::Steady:
+        event->accept();
+        d->state = FlickablePrivate::Pressed;
+        d->pressPos = event->pos();
+        break;
+
+    case FlickablePrivate::AutoScroll:
+        event->accept();
+        d->state = FlickablePrivate::Stop;
+        d->speed = QPoint(0, 0);
+        d->pressPos = event->pos();
+        d->offset = scrollOffset();
+        d->ticker->stop();
+        break;
+
+    default:
+        break;
+    }
+}
+
+void Flickable::handleMouseRelease(QMouseEvent *event)
+{
+    event->ignore();
+
+    if (event->button() != Qt::LeftButton)
+        return;
+
+    if (d->ignoreList.removeAll(event))
+        return;
+
+    QPoint delta;
+
+    switch (d->state) {
+
+    case FlickablePrivate::Pressed:
+        event->accept();
+        d->state = FlickablePrivate::Steady;
+        if (d->target) {
+            QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,
+                                                  d->pressPos, Qt::LeftButton,
+                                                  Qt::LeftButton, Qt::NoModifier);
+            QMouseEvent *event2 = new QMouseEvent(*event);
+            d->ignoreList << event1;
+            d->ignoreList << event2;
+            QApplication::postEvent(d->target, event1);
+            QApplication::postEvent(d->target, event2);
+        }
+        break;
+
+    case FlickablePrivate::ManualScroll:
+        event->accept();
+        delta = event->pos() - d->pressPos;
+        if (d->timeStamp.elapsed() > 100) {
+            d->timeStamp = QTime::currentTime();
+            d->speed = delta - d->delta;
+            d->delta = delta;
+        }
+        d->offset = scrollOffset();
+        d->pressPos = event->pos();
+        if (d->speed == QPoint(0, 0)) {
+            d->state = FlickablePrivate::Steady;
+        } else {
+            d->speed /= 4;
+            d->state = FlickablePrivate::AutoScroll;
+            d->ticker->start(20);
+        }
+        break;
+
+    case FlickablePrivate::Stop:
+        event->accept();
+        d->state = FlickablePrivate::Steady;
+        d->offset = scrollOffset();
+        break;
+
+    default:
+        break;
+    }
+}
+
+void Flickable::handleMouseMove(QMouseEvent *event)
+{
+    event->ignore();
+
+    if (!(event->buttons() & Qt::LeftButton))
+        return;
+
+    if (d->ignoreList.removeAll(event))
+        return;
+
+    QPoint delta;
+
+    switch (d->state) {
+
+    case FlickablePrivate::Pressed:
+    case FlickablePrivate::Stop:
+        delta = event->pos() - d->pressPos;
+        if (delta.x() > d->threshold || delta.x() < -d->threshold ||
+                delta.y() > d->threshold || delta.y() < -d->threshold) {
+            d->timeStamp = QTime::currentTime();
+            d->state = FlickablePrivate::ManualScroll;
+            d->delta = QPoint(0, 0);
+            d->pressPos = event->pos();
+            event->accept();
+        }
+        break;
+
+    case FlickablePrivate::ManualScroll:
+        event->accept();
+        delta = event->pos() - d->pressPos;
+        setScrollOffset(d->offset - delta);
+        if (d->timeStamp.elapsed() > 100) {
+            d->timeStamp = QTime::currentTime();
+            d->speed = delta - d->delta;
+            d->delta = delta;
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+void Flickable::tick()
+{
+    if (d->state == FlickablePrivate:: AutoScroll) {
+        d->speed = deaccelerate(d->speed);
+        setScrollOffset(d->offset - d->speed);
+        d->offset = scrollOffset();
+        if (d->speed == QPoint(0, 0)) {
+            d->state = FlickablePrivate::Steady;
+            d->ticker->stop();
+        }
+    } else {
+        d->ticker->stop();
+    }
+}