Fix kinetic scrolling on Symbian. Fix timer handling.
authorAkos Polster <akos@pipacs.com>
Sun, 17 Oct 2010 00:01:17 +0000 (02:01 +0200)
committerAkos Polster <akos@pipacs.com>
Sun, 17 Oct 2010 00:01:17 +0000 (02:01 +0200)
bookview.cpp
bookview.h
dorian.pro
mainwindow.cpp
pkg/changelog
widgets/flickable.cpp [deleted file]
widgets/flickable.h [deleted file]
widgets/flickcharm.cpp [new file with mode: 0755]
widgets/flickcharm.h [new file with mode: 0755]
widgets/progress.cpp

index 121fa50..2eda697 100644 (file)
@@ -5,8 +5,10 @@
 #include <QDir>
 #include <QTimer>
 
-#ifdef Q_WS_MAEMO_5
+#if defined(Q_WS_MAEMO_5)
 #   include <QAbstractKineticScroller>
+#elif defined(Q_OS_SYMBIAN)
+#   include "flickcharm.h"
 #endif
 
 #include "book.h"
@@ -72,9 +74,12 @@ BookView::BookView(QWidget *parent):
     s->setValue("scheme", s->value("scheme", "default"));
     setBook(0);
 
-#ifdef Q_WS_MAEMO_5
+#if defined(Q_WS_MAEMO_5)
     scrollerMonitor = 0;
     scroller = property("kineticScroller").value<QAbstractKineticScroller *>();
+#elif defined(Q_OS_SYMBIAN)
+    FlickCharm *charm = new FlickCharm(this);
+    charm->activateOn(this);
 #endif
 }
 
@@ -400,9 +405,8 @@ void BookView::timerEvent(QTimerEvent *e)
             killTimer(scrollerMonitor);
         }
     }
-#else
-    Q_UNUSED(e);
-#endif // Q_WS_MAEMO_5
+#endif
+    QWebView::timerEvent(e);
 }
 
 void BookView::keyPressEvent(QKeyEvent* event)
index 994fe5d..f96d599 100644 (file)
@@ -16,9 +16,6 @@ class QAbstractKineticScroller;
 
 /** Visual representation of a book. */
 class BookView: public QWebView
-#ifdef Q_OS_SYMBIAN
-        , public Flickable
-#endif
 {
     Q_OBJECT
 
@@ -99,9 +96,10 @@ private:
     bool mousePressed;
     int contentsHeight; /**< Last know height of the frame. */
 
-#if def(Q_WS_MAEMO_5)
+#if defined(Q_WS_MAEMO_5)
     int scrollerMonitor;
     QAbstractKineticScroller *scroller;
+#endif
 };
 
 #endif // BOOKVIEW_H
index fe08054..cac489f 100644 (file)
@@ -114,20 +114,23 @@ unix {
         LIBS += -lz\r
     }\r
 }\r
+\r
 win32 {\r
     DEFINES += ZLIB_WINAPI\r
     INCLUDEPATH += $$PWD/model/zlib\r
     LIBS += pkg/win32/zlibstat.lib\r
 }\r
+\r
 symbian {\r
     ICON = $$PWD/pkg/symbian/book.svgt\r
     TARGET.UID3 = 0xEA633557\r
     # FIXME: TARGET.CAPABILITY = ...\r
     # FIXME: Include path to OpenC ZLIB?\r
     INCLUDEPATH += c:/Qt/4.7.0/src/3rdparty/zlib\r
-    SOURCES += widgets/flickable.cpp\r
-    HEADERS += widgets/flickable.h\r
+    HEADERS += widgets/flickcharm.h\r
+    SOURCES += widgets/flickcharm.cpp\r
 }\r
+\r
 maemo5 {\r
     QT += maemo5 dbus\r
     isEmpty(PREFIX) {\r
index 5d2389f..3350754 100755 (executable)
@@ -403,6 +403,7 @@ void MainWindow::timerEvent(QTimerEvent *event)
 #endif // Q_WS_MAEMO_5
         qDebug() << "MainWindow::timerEvent: Prevent display blanking";
     }
+    AdopterWindow::timerEvent(event);
 }
 
 void MainWindow::resizeEvent(QResizeEvent *e)
index 4311462..d535473 100644 (file)
@@ -1,6 +1,7 @@
 dorian (0.3.1-1) unstable; urgency=low
 
   * Fix sorting of library
+  * Fix book view kinetic scrolling on Symbian
 
  -- Akos Polster <akos@pipacs.com>  Sat, 10 Oct 2010 02:00:00 +0200
 
diff --git a/widgets/flickable.cpp b/widgets/flickable.cpp
deleted file mode 100755 (executable)
index 7841d7d..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-/****************************************************************************\r
-**\r
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).\r
-** All rights reserved.\r
-** Contact: Nokia Corporation (qt-info@nokia.com)\r
-**\r
-** This file is part of the demonstration applications of the Qt Toolkit.\r
-**\r
-** $QT_BEGIN_LICENSE:LGPL$\r
-** Commercial Usage\r
-** Licensees holding valid Qt Commercial licenses may use this file in\r
-** accordance with the Qt Commercial License Agreement provided with the\r
-** Software or, alternatively, in accordance with the terms contained in\r
-** a written agreement between you and Nokia.\r
-**\r
-** GNU Lesser General Public License Usage\r
-** Alternatively, this file may be used under the terms of the GNU Lesser\r
-** General Public License version 2.1 as published by the Free Software\r
-** Foundation and appearing in the file LICENSE.LGPL included in the\r
-** packaging of this file.  Please review the following information to\r
-** ensure the GNU Lesser General Public License version 2.1 requirements\r
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\r
-**\r
-** In addition, as a special exception, Nokia gives you certain additional\r
-** rights.  These rights are described in the Nokia Qt LGPL Exception\r
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.\r
-**\r
-** GNU General Public License Usage\r
-** Alternatively, this file may be used under the terms of the GNU\r
-** General Public License version 3.0 as published by the Free Software\r
-** Foundation and appearing in the file LICENSE.GPL included in the\r
-** packaging of this file.  Please review the following information to\r
-** ensure the GNU General Public License version 3.0 requirements will be\r
-** met: http://www.gnu.org/copyleft/gpl.html.\r
-**\r
-** If you have questions regarding the use of this file, please contact\r
-** Nokia at qt-info@nokia.com.\r
-** $QT_END_LICENSE$\r
-**\r
-****************************************************************************/\r
-\r
-#include "flickable.h"\r
-\r
-#include <QtCore>\r
-#include <QtGui>\r
-\r
-class FlickableTicker: QObject\r
-{\r
-public:\r
-    FlickableTicker(Flickable *scroller) {\r
-        m_scroller = scroller;\r
-    }\r
-\r
-    void start(int interval) {\r
-        if (!m_timer.isActive())\r
-            m_timer.start(interval, this);\r
-    }\r
-\r
-    void stop() {\r
-        m_timer.stop();\r
-    }\r
-\r
-protected:\r
-    void timerEvent(QTimerEvent *event) {\r
-        Q_UNUSED(event);\r
-        m_scroller->tick();\r
-    }\r
-\r
-private:\r
-    Flickable *m_scroller;\r
-    QBasicTimer m_timer;\r
-};\r
-\r
-class FlickablePrivate\r
-{\r
-public:\r
-    typedef enum {\r
-        Steady,\r
-        Pressed,\r
-        ManualScroll,\r
-        AutoScroll,\r
-        Stop\r
-    } State;\r
-\r
-    State state;\r
-    int threshold;\r
-    QPoint pressPos;\r
-    QPoint offset;\r
-    QPoint delta;\r
-    QPoint speed;\r
-    FlickableTicker *ticker;\r
-    QTime timeStamp;\r
-    QWidget *target;\r
-    QList<QEvent*> ignoreList;\r
-};\r
-\r
-Flickable::Flickable()\r
-{\r
-    d = new FlickablePrivate;\r
-    d->state = FlickablePrivate::Steady;\r
-    d->threshold = 10;\r
-    d->ticker = new FlickableTicker(this);\r
-    d->timeStamp = QTime::currentTime();\r
-    d->target = 0;\r
-}\r
-\r
-Flickable::~Flickable()\r
-{\r
-    delete d;\r
-}\r
-\r
-void Flickable::setThreshold(int th)\r
-{\r
-    if (th >= 0)\r
-        d->threshold = th;\r
-}\r
-\r
-int Flickable::threshold() const\r
-{\r
-    return d->threshold;\r
-}\r
-\r
-void Flickable::setAcceptMouseClick(QWidget *target)\r
-{\r
-    d->target = target;\r
-}\r
-\r
-static QPoint deaccelerate(const QPoint &speed, int a = 1, int max = 64)\r
-{\r
-    int x = qBound(-max, speed.x(), max);\r
-    int y = qBound(-max, speed.y(), max);\r
-    x = (x == 0) ? x : (x > 0) ? qMax(0, x - a) : qMin(0, x + a);\r
-    y = (y == 0) ? y : (y > 0) ? qMax(0, y - a) : qMin(0, y + a);\r
-    return QPoint(x, y);\r
-}\r
-\r
-void Flickable::handleMousePress(QMouseEvent *event)\r
-{\r
-    event->ignore();\r
-\r
-    if (event->button() != Qt::LeftButton)\r
-        return;\r
-\r
-    if (d->ignoreList.removeAll(event))\r
-        return;\r
-\r
-    switch (d->state) {\r
-\r
-    case FlickablePrivate::Steady:\r
-        event->accept();\r
-        d->state = FlickablePrivate::Pressed;\r
-        d->pressPos = event->pos();\r
-        break;\r
-\r
-    case FlickablePrivate::AutoScroll:\r
-        event->accept();\r
-        d->state = FlickablePrivate::Stop;\r
-        d->speed = QPoint(0, 0);\r
-        d->pressPos = event->pos();\r
-        d->offset = scrollOffset();\r
-        d->ticker->stop();\r
-        break;\r
-\r
-    default:\r
-        break;\r
-    }\r
-}\r
-\r
-void Flickable::handleMouseRelease(QMouseEvent *event)\r
-{\r
-    event->ignore();\r
-\r
-    if (event->button() != Qt::LeftButton)\r
-        return;\r
-\r
-    if (d->ignoreList.removeAll(event))\r
-        return;\r
-\r
-    QPoint delta;\r
-\r
-    switch (d->state) {\r
-\r
-    case FlickablePrivate::Pressed:\r
-        event->accept();\r
-        d->state = FlickablePrivate::Steady;\r
-        if (d->target) {\r
-            QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,\r
-                                                  d->pressPos, Qt::LeftButton,\r
-                                                  Qt::LeftButton, Qt::NoModifier);\r
-            QMouseEvent *event2 = new QMouseEvent(*event);\r
-            d->ignoreList << event1;\r
-            d->ignoreList << event2;\r
-            QApplication::postEvent(d->target, event1);\r
-            QApplication::postEvent(d->target, event2);\r
-        }\r
-        break;\r
-\r
-    case FlickablePrivate::ManualScroll:\r
-        event->accept();\r
-        delta = event->pos() - d->pressPos;\r
-        if (d->timeStamp.elapsed() > 100) {\r
-            d->timeStamp = QTime::currentTime();\r
-            d->speed = delta - d->delta;\r
-            d->delta = delta;\r
-        }\r
-        d->offset = scrollOffset();\r
-        d->pressPos = event->pos();\r
-        if (d->speed == QPoint(0, 0)) {\r
-            d->state = FlickablePrivate::Steady;\r
-        } else {\r
-            d->speed /= 4;\r
-            d->state = FlickablePrivate::AutoScroll;\r
-            d->ticker->start(20);\r
-        }\r
-        break;\r
-\r
-    case FlickablePrivate::Stop:\r
-        event->accept();\r
-        d->state = FlickablePrivate::Steady;\r
-        d->offset = scrollOffset();\r
-        break;\r
-\r
-    default:\r
-        break;\r
-    }\r
-}\r
-\r
-void Flickable::handleMouseMove(QMouseEvent *event)\r
-{\r
-    event->ignore();\r
-\r
-    if (!(event->buttons() & Qt::LeftButton))\r
-        return;\r
-\r
-    if (d->ignoreList.removeAll(event))\r
-        return;\r
-\r
-    QPoint delta;\r
-\r
-    switch (d->state) {\r
-\r
-    case FlickablePrivate::Pressed:\r
-    case FlickablePrivate::Stop:\r
-        delta = event->pos() - d->pressPos;\r
-        if (delta.x() > d->threshold || delta.x() < -d->threshold ||\r
-                delta.y() > d->threshold || delta.y() < -d->threshold) {\r
-            d->timeStamp = QTime::currentTime();\r
-            d->state = FlickablePrivate::ManualScroll;\r
-            d->delta = QPoint(0, 0);\r
-            d->pressPos = event->pos();\r
-            event->accept();\r
-        }\r
-        break;\r
-\r
-    case FlickablePrivate::ManualScroll:\r
-        event->accept();\r
-        delta = event->pos() - d->pressPos;\r
-        setScrollOffset(d->offset - delta);\r
-        if (d->timeStamp.elapsed() > 100) {\r
-            d->timeStamp = QTime::currentTime();\r
-            d->speed = delta - d->delta;\r
-            d->delta = delta;\r
-        }\r
-        break;\r
-\r
-    default:\r
-        break;\r
-    }\r
-}\r
-\r
-void Flickable::tick()\r
-{\r
-    if (d->state == FlickablePrivate:: AutoScroll) {\r
-        d->speed = deaccelerate(d->speed);\r
-        setScrollOffset(d->offset - d->speed);\r
-        d->offset = scrollOffset();\r
-        if (d->speed == QPoint(0, 0)) {\r
-            d->state = FlickablePrivate::Steady;\r
-            d->ticker->stop();\r
-        }\r
-    } else {\r
-        d->ticker->stop();\r
-    }\r
-}\r
diff --git a/widgets/flickable.h b/widgets/flickable.h
deleted file mode 100755 (executable)
index 4211a2f..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/****************************************************************************\r
-**\r
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).\r
-** All rights reserved.\r
-** Contact: Nokia Corporation (qt-info@nokia.com)\r
-**\r
-** This file is part of the demonstration applications of the Qt Toolkit.\r
-**\r
-** $QT_BEGIN_LICENSE:LGPL$\r
-** Commercial Usage\r
-** Licensees holding valid Qt Commercial licenses may use this file in\r
-** accordance with the Qt Commercial License Agreement provided with the\r
-** Software or, alternatively, in accordance with the terms contained in\r
-** a written agreement between you and Nokia.\r
-**\r
-** GNU Lesser General Public License Usage\r
-** Alternatively, this file may be used under the terms of the GNU Lesser\r
-** General Public License version 2.1 as published by the Free Software\r
-** Foundation and appearing in the file LICENSE.LGPL included in the\r
-** packaging of this file.  Please review the following information to\r
-** ensure the GNU Lesser General Public License version 2.1 requirements\r
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\r
-**\r
-** In addition, as a special exception, Nokia gives you certain additional\r
-** rights.  These rights are described in the Nokia Qt LGPL Exception\r
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.\r
-**\r
-** GNU General Public License Usage\r
-** Alternatively, this file may be used under the terms of the GNU\r
-** General Public License version 3.0 as published by the Free Software\r
-** Foundation and appearing in the file LICENSE.GPL included in the\r
-** packaging of this file.  Please review the following information to\r
-** ensure the GNU General Public License version 3.0 requirements will be\r
-** met: http://www.gnu.org/copyleft/gpl.html.\r
-**\r
-** If you have questions regarding the use of this file, please contact\r
-** Nokia at qt-info@nokia.com.\r
-** $QT_END_LICENSE$\r
-**\r
-****************************************************************************/\r
-\r
-#ifndef FLICKABLE_H\r
-#define FLICKABLE_H\r
-\r
-class QMouseEvent;\r
-class QPoint;\r
-class QWidget;\r
-\r
-class FlickableTicker;\r
-class FlickablePrivate;\r
-\r
-class Flickable\r
-{\r
-public:\r
-\r
-    Flickable();\r
-    virtual ~Flickable();\r
-\r
-    void setThreshold(int threshold);\r
-    int threshold() const;\r
-\r
-    void setAcceptMouseClick(QWidget *target);\r
-\r
-    void handleMousePress(QMouseEvent *event);\r
-    void handleMouseMove(QMouseEvent *event);\r
-    void handleMouseRelease(QMouseEvent *event);\r
-\r
-protected:\r
-    virtual QPoint scrollOffset() const = 0;\r
-    virtual void setScrollOffset(const QPoint &offset) = 0;\r
-\r
-private:\r
-    void tick();\r
-\r
-private:\r
-    FlickablePrivate *d;\r
-    friend class FlickableTicker;\r
-};\r
-\r
-#endif // FLICKABLE_H\r
diff --git a/widgets/flickcharm.cpp b/widgets/flickcharm.cpp
new file mode 100755 (executable)
index 0000000..062a6f4
--- /dev/null
@@ -0,0 +1,402 @@
+/****************************************************************************\r
+**\r
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).\r
+** All rights reserved.\r
+** Contact: Nokia Corporation (qt-info@nokia.com)\r
+**\r
+** This file is part of the demos of the Qt Toolkit.\r
+**\r
+** $QT_BEGIN_LICENSE:LGPL$\r
+** Commercial Usage\r
+** Licensees holding valid Qt Commercial licenses may use this file in\r
+** accordance with the Qt Commercial License Agreement provided with the\r
+** Software or, alternatively, in accordance with the terms contained in\r
+** a written agreement between you and Nokia.\r
+**\r
+** GNU Lesser General Public License Usage\r
+** Alternatively, this file may be used under the terms of the GNU Lesser\r
+** General Public License version 2.1 as published by the Free Software\r
+** Foundation and appearing in the file LICENSE.LGPL included in the\r
+** packaging of this file.  Please review the following information to\r
+** ensure the GNU Lesser General Public License version 2.1 requirements\r
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\r
+**\r
+** In addition, as a special exception, Nokia gives you certain additional\r
+** rights.  These rights are described in the Nokia Qt LGPL Exception\r
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.\r
+**\r
+** GNU General Public License Usage\r
+** Alternatively, this file may be used under the terms of the GNU\r
+** General Public License version 3.0 as published by the Free Software\r
+** Foundation and appearing in the file LICENSE.GPL included in the\r
+** packaging of this file.  Please review the following information to\r
+** ensure the GNU General Public License version 3.0 requirements will be\r
+** met: http://www.gnu.org/copyleft/gpl.html.\r
+**\r
+** If you have questions regarding the use of this file, please contact\r
+** Nokia at qt-info@nokia.com.\r
+** $QT_END_LICENSE$\r
+**\r
+****************************************************************************/\r
+\r
+#include "flickcharm.h"\r
+\r
+#include <QAbstractScrollArea>\r
+#include <QApplication>\r
+#include <QBasicTimer>\r
+#include <QEvent>\r
+#include <QHash>\r
+#include <QList>\r
+#include <QMouseEvent>\r
+#include <QScrollBar>\r
+#include <QTime>\r
+#include <QWebFrame>\r
+#include <QWebView>\r
+\r
+#include <QDebug>\r
+\r
+const int fingerAccuracyThreshold = 3;\r
+\r
+struct FlickData {\r
+    typedef enum {\r
+        Steady, // Interaction without scrolling\r
+        ManualScroll, // Scrolling manually with the finger on the screen\r
+        AutoScroll, // Scrolling automatically\r
+        AutoScrollAcceleration // Scrolling automatically but a finger is on the screen\r
+    } State;\r
+    State state;\r
+    QWidget *widget;\r
+    QPoint pressPos;\r
+    QPoint lastPos;\r
+    QPoint speed;\r
+    QTime speedTimer;\r
+    QList<QEvent*> ignored;\r
+    QTime accelerationTimer;\r
+    bool lastPosValid:1;\r
+    bool waitingAcceleration:1;\r
+\r
+    FlickData()\r
+        : lastPosValid(false)\r
+        , waitingAcceleration(false)\r
+    {}\r
+\r
+    void resetSpeed()\r
+    {\r
+        speed = QPoint();\r
+        lastPosValid = false;\r
+    }\r
+    void updateSpeed(const QPoint &newPosition)\r
+    {\r
+        if (lastPosValid) {\r
+            const int timeElapsed = speedTimer.elapsed();\r
+            if (timeElapsed) {\r
+                const QPoint newPixelDiff = (newPosition - lastPos);\r
+                const QPoint pixelsPerSecond = newPixelDiff * (1000 / timeElapsed);\r
+                // fingers are inacurates, we ignore small changes to avoid stopping the autoscroll because\r
+                // of a small horizontal offset when scrolling vertically\r
+                const int newSpeedY = (qAbs(pixelsPerSecond.y()) > fingerAccuracyThreshold) ? pixelsPerSecond.y() : 0;\r
+                const int newSpeedX = (qAbs(pixelsPerSecond.x()) > fingerAccuracyThreshold) ? pixelsPerSecond.x() : 0;\r
+                if (state == AutoScrollAcceleration) {\r
+                    const int max = 4000; // px by seconds\r
+                    const int oldSpeedY = speed.y();\r
+                    const int oldSpeedX = speed.x();\r
+                    if ((oldSpeedY <= 0 && newSpeedY <= 0) ||  (oldSpeedY >= 0 && newSpeedY >= 0)\r
+                        && (oldSpeedX <= 0 && newSpeedX <= 0) ||  (oldSpeedX >= 0 && newSpeedX >= 0)) {\r
+                        speed.setY(qBound(-max, (oldSpeedY + (newSpeedY / 4)), max));\r
+                        speed.setX(qBound(-max, (oldSpeedX + (newSpeedX / 4)), max));\r
+                    } else {\r
+                        speed = QPoint();\r
+                    }\r
+                } else {\r
+                    const int max = 2500; // px by seconds\r
+                    // we average the speed to avoid strange effects with the last delta\r
+                    if (!speed.isNull()) {\r
+                        speed.setX(qBound(-max, (speed.x() / 4) + (newSpeedX * 3 / 4), max));\r
+                        speed.setY(qBound(-max, (speed.y() / 4) + (newSpeedY * 3 / 4), max));\r
+                    } else {\r
+                        speed = QPoint(newSpeedX, newSpeedY);\r
+                    }\r
+                }\r
+            }\r
+        } else {\r
+            lastPosValid = true;\r
+        }\r
+        speedTimer.start();\r
+        lastPos = newPosition;\r
+    }\r
+\r
+    // scroll by dx, dy\r
+    // return true if the widget was scrolled\r
+    bool scrollWidget(const int dx, const int dy)\r
+    {\r
+        QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);\r
+        if (scrollArea) {\r
+            const int x = scrollArea->horizontalScrollBar()->value();\r
+            const int y = scrollArea->verticalScrollBar()->value();\r
+            scrollArea->horizontalScrollBar()->setValue(x - dx);\r
+            scrollArea->verticalScrollBar()->setValue(y - dy);\r
+            return (scrollArea->horizontalScrollBar()->value() != x\r
+                    || scrollArea->verticalScrollBar()->value() != y);\r
+        }\r
+\r
+        QWebView *webView = qobject_cast<QWebView*>(widget);\r
+        if (webView) {\r
+            QWebFrame *frame = webView->page()->mainFrame();\r
+            const QPoint position = frame->scrollPosition();\r
+            frame->setScrollPosition(position - QPoint(dx, dy));\r
+            return frame->scrollPosition() != position;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    bool scrollTo(const QPoint &newPosition)\r
+    {\r
+        const QPoint delta = newPosition - lastPos;\r
+        updateSpeed(newPosition);\r
+        return scrollWidget(delta.x(), delta.y());\r
+    }\r
+};\r
+\r
+class FlickCharmPrivate\r
+{\r
+public:\r
+    QHash<QWidget*, FlickData*> flickData;\r
+    QBasicTimer ticker;\r
+    QTime timeCounter;\r
+    void startTicker(QObject *object)\r
+    {\r
+        if (!ticker.isActive())\r
+            ticker.start(15, object);\r
+        timeCounter.start();\r
+    }\r
+};\r
+\r
+FlickCharm::FlickCharm(QObject *parent): QObject(parent)\r
+{\r
+    d = new FlickCharmPrivate;\r
+}\r
+\r
+FlickCharm::~FlickCharm()\r
+{\r
+    delete d;\r
+}\r
+\r
+void FlickCharm::activateOn(QWidget *widget)\r
+{\r
+    QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);\r
+    if (scrollArea) {\r
+        scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\r
+        scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\r
+\r
+        QWidget *viewport = scrollArea->viewport();\r
+\r
+        viewport->installEventFilter(this);\r
+        scrollArea->installEventFilter(this);\r
+\r
+        d->flickData.remove(viewport);\r
+        d->flickData[viewport] = new FlickData;\r
+        d->flickData[viewport]->widget = widget;\r
+        d->flickData[viewport]->state = FlickData::Steady;\r
+\r
+        return;\r
+    }\r
+\r
+    QWebView *webView = qobject_cast<QWebView*>(widget);\r
+    if (webView) {\r
+        QWebFrame *frame = webView->page()->mainFrame();\r
+        frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);\r
+        frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);\r
+\r
+        webView->installEventFilter(this);\r
+\r
+        d->flickData.remove(webView);\r
+        d->flickData[webView] = new FlickData;\r
+        d->flickData[webView]->widget = webView;\r
+        d->flickData[webView]->state = FlickData::Steady;\r
+\r
+        return;\r
+    }\r
+\r
+    qWarning() << "FlickCharm only works on QAbstractScrollArea (and derived classes)";\r
+    qWarning() << "or QWebView (and derived classes)";\r
+}\r
+\r
+void FlickCharm::deactivateFrom(QWidget *widget)\r
+{\r
+    QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);\r
+    if (scrollArea) {\r
+        QWidget *viewport = scrollArea->viewport();\r
+\r
+        viewport->removeEventFilter(this);\r
+        scrollArea->removeEventFilter(this);\r
+\r
+        delete d->flickData[viewport];\r
+        d->flickData.remove(viewport);\r
+\r
+        return;\r
+    }\r
+\r
+    QWebView *webView = qobject_cast<QWebView*>(widget);\r
+    if (webView) {\r
+        webView->removeEventFilter(this);\r
+\r
+        delete d->flickData[webView];\r
+        d->flickData.remove(webView);\r
+\r
+        return;\r
+    }\r
+}\r
+\r
+static QPoint deaccelerate(const QPoint &speed, const int deltatime)\r
+{\r
+    const int deltaSpeed = deltatime;\r
+\r
+    int x = speed.x();\r
+    int y = speed.y();\r
+    x = (x == 0) ? x : (x > 0) ? qMax(0, x - deltaSpeed) : qMin(0, x + deltaSpeed);\r
+    y = (y == 0) ? y : (y > 0) ? qMax(0, y - deltaSpeed) : qMin(0, y + deltaSpeed);\r
+    return QPoint(x, y);\r
+}\r
+\r
+bool FlickCharm::eventFilter(QObject *object, QEvent *event)\r
+{\r
+    if (!object->isWidgetType())\r
+        return false;\r
+\r
+    const QEvent::Type type = event->type();\r
+\r
+    switch (type) {\r
+    case QEvent::MouseButtonPress:\r
+    case QEvent::MouseMove:\r
+    case QEvent::MouseButtonRelease:\r
+        break;\r
+    case QEvent::MouseButtonDblClick: // skip double click\r
+        return true;\r
+    default:\r
+        return false;\r
+    }\r
+\r
+    QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);\r
+    if (type == QEvent::MouseMove && mouseEvent->buttons() != Qt::LeftButton)\r
+        return false;\r
+\r
+    if (mouseEvent->modifiers() != Qt::NoModifier)\r
+        return false;\r
+\r
+    QWidget *viewport = qobject_cast<QWidget*>(object);\r
+    FlickData *data = d->flickData.value(viewport);\r
+    if (!viewport || !data || data->ignored.removeAll(event))\r
+        return false;\r
+\r
+    const QPoint mousePos = mouseEvent->pos();\r
+    bool consumed = false;\r
+    switch (data->state) {\r
+\r
+    case FlickData::Steady:\r
+        if (type == QEvent::MouseButtonPress) {\r
+            consumed = true;\r
+            data->pressPos = mousePos;\r
+        } else if (type == QEvent::MouseButtonRelease) {\r
+            consumed = true;\r
+            QMouseEvent *event1 = new QMouseEvent(QEvent::MouseButtonPress,\r
+                                                  data->pressPos, Qt::LeftButton,\r
+                                                  Qt::LeftButton, Qt::NoModifier);\r
+            QMouseEvent *event2 = new QMouseEvent(QEvent::MouseButtonRelease,\r
+                                                  data->pressPos, Qt::LeftButton,\r
+                                                  Qt::LeftButton, Qt::NoModifier);\r
+\r
+            data->ignored << event1;\r
+            data->ignored << event2;\r
+            QApplication::postEvent(object, event1);\r
+            QApplication::postEvent(object, event2);\r
+        } else if (type == QEvent::MouseMove) {\r
+            consumed = true;\r
+            data->scrollTo(mousePos);\r
+\r
+            const QPoint delta = mousePos - data->pressPos;\r
+            if (delta.x() > fingerAccuracyThreshold || delta.y() > fingerAccuracyThreshold)\r
+                data->state = FlickData::ManualScroll;\r
+        }\r
+        break;\r
+\r
+    case FlickData::ManualScroll:\r
+        if (type == QEvent::MouseMove) {\r
+            consumed = true;\r
+            data->scrollTo(mousePos);\r
+        } else if (type == QEvent::MouseButtonRelease) {\r
+            consumed = true;\r
+            data->state = FlickData::AutoScroll;\r
+            data->lastPosValid = false;\r
+            d->startTicker(this);\r
+        }\r
+        break;\r
+\r
+    case FlickData::AutoScroll:\r
+        if (type == QEvent::MouseButtonPress) {\r
+            consumed = true;\r
+            data->state = FlickData::AutoScrollAcceleration;\r
+            data->waitingAcceleration = true;\r
+            data->accelerationTimer.start();\r
+            data->updateSpeed(mousePos);\r
+            data->pressPos = mousePos;\r
+        } else if (type == QEvent::MouseButtonRelease) {\r
+            consumed = true;\r
+            data->state = FlickData::Steady;\r
+            data->resetSpeed();\r
+        }\r
+        break;\r
+\r
+    case FlickData::AutoScrollAcceleration:\r
+        if (type == QEvent::MouseMove) {\r
+            consumed = true;\r
+            data->updateSpeed(mousePos);\r
+            data->accelerationTimer.start();\r
+            if (data->speed.isNull())\r
+                data->state = FlickData::ManualScroll;\r
+        } else if (type == QEvent::MouseButtonRelease) {\r
+            consumed = true;\r
+            data->state = FlickData::AutoScroll;\r
+            data->waitingAcceleration = false;\r
+            data->lastPosValid = false;\r
+        }\r
+        break;\r
+    default:\r
+        break;\r
+    }\r
+    data->lastPos = mousePos;\r
+    return true;\r
+}\r
+\r
+void FlickCharm::timerEvent(QTimerEvent *event)\r
+{\r
+    int count = 0;\r
+    QHashIterator<QWidget*, FlickData*> item(d->flickData);\r
+    while (item.hasNext()) {\r
+        item.next();\r
+        FlickData *data = item.value();\r
+        if (data->state == FlickData::AutoScrollAcceleration\r
+            && data->waitingAcceleration\r
+            && data->accelerationTimer.elapsed() > 40) {\r
+            data->state = FlickData::ManualScroll;\r
+            data->resetSpeed();\r
+        }\r
+        if (data->state == FlickData::AutoScroll || data->state == FlickData::AutoScrollAcceleration) {\r
+            const int timeElapsed = d->timeCounter.elapsed();\r
+            const QPoint delta = (data->speed) * timeElapsed / 1000;\r
+            bool hasScrolled = data->scrollWidget(delta.x(), delta.y());\r
+\r
+            if (data->speed.isNull() || !hasScrolled)\r
+                data->state = FlickData::Steady;\r
+            else\r
+                count++;\r
+            data->speed = deaccelerate(data->speed, timeElapsed);\r
+        }\r
+    }\r
+\r
+    if (!count)\r
+        d->ticker.stop();\r
+    else\r
+        d->timeCounter.start();\r
+\r
+    QObject::timerEvent(event);\r
+}\r
diff --git a/widgets/flickcharm.h b/widgets/flickcharm.h
new file mode 100755 (executable)
index 0000000..83d7ac9
--- /dev/null
@@ -0,0 +1,67 @@
+/****************************************************************************\r
+**\r
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).\r
+** All rights reserved.\r
+** Contact: Nokia Corporation (qt-info@nokia.com)\r
+**\r
+** This file is part of the demos of the Qt Toolkit.\r
+**\r
+** $QT_BEGIN_LICENSE:LGPL$\r
+** Commercial Usage\r
+** Licensees holding valid Qt Commercial licenses may use this file in\r
+** accordance with the Qt Commercial License Agreement provided with the\r
+** Software or, alternatively, in accordance with the terms contained in\r
+** a written agreement between you and Nokia.\r
+**\r
+** GNU Lesser General Public License Usage\r
+** Alternatively, this file may be used under the terms of the GNU Lesser\r
+** General Public License version 2.1 as published by the Free Software\r
+** Foundation and appearing in the file LICENSE.LGPL included in the\r
+** packaging of this file.  Please review the following information to\r
+** ensure the GNU Lesser General Public License version 2.1 requirements\r
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.\r
+**\r
+** In addition, as a special exception, Nokia gives you certain additional\r
+** rights.  These rights are described in the Nokia Qt LGPL Exception\r
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.\r
+**\r
+** GNU General Public License Usage\r
+** Alternatively, this file may be used under the terms of the GNU\r
+** General Public License version 3.0 as published by the Free Software\r
+** Foundation and appearing in the file LICENSE.GPL included in the\r
+** packaging of this file.  Please review the following information to\r
+** ensure the GNU General Public License version 3.0 requirements will be\r
+** met: http://www.gnu.org/copyleft/gpl.html.\r
+**\r
+** If you have questions regarding the use of this file, please contact\r
+** Nokia at qt-info@nokia.com.\r
+** $QT_END_LICENSE$\r
+**\r
+****************************************************************************/\r
+\r
+#ifndef FLICKCHARM_H\r
+#define FLICKCHARM_H\r
+\r
+#include <QObject>\r
+\r
+class FlickCharmPrivate;\r
+class QWidget;\r
+\r
+class FlickCharm: public QObject\r
+{\r
+    Q_OBJECT\r
+public:\r
+    FlickCharm(QObject *parent = 0);\r
+    ~FlickCharm();\r
+    void activateOn(QWidget *widget);\r
+    void deactivateFrom(QWidget *widget);\r
+    bool eventFilter(QObject *object, QEvent *event);\r
+\r
+protected:\r
+    void timerEvent(QTimerEvent *event);\r
+\r
+private:\r
+    FlickCharmPrivate *d;\r
+};\r
+\r
+#endif // FLICKCHARM_H\r
index d0a1409..e3eb3b8 100644 (file)
@@ -45,4 +45,5 @@ void Progress::timerEvent(QTimerEvent *e)
         killTimer(timer);
         hide();
     }
+    QLabel::timerEvent(e);
 }