]> git.nothing2do.fr Git - diary-mobile.git/commitdiff
added QsKineticScroller and use of it
authorNorbert Moutarde <norbert.moutarde@nothing2do.eu>
Wed, 30 Apr 2014 07:58:43 +0000 (09:58 +0200)
committerNorbert Moutarde <norbert.moutarde@nothing2do.eu>
Wed, 30 Apr 2014 07:58:43 +0000 (09:58 +0200)
QsKineticScroller.cpp [new file with mode: 0644]
QsKineticScroller.h [new file with mode: 0644]
diary-mobile-android.pro
mainwindow.cpp
mainwindow.h

diff --git a/QsKineticScroller.cpp b/QsKineticScroller.cpp
new file mode 100644 (file)
index 0000000..129d5e5
--- /dev/null
@@ -0,0 +1,213 @@
+// Copyright (c) 2010, Razvan Petru
+// All rights reserved.
+
+// 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.
+// * The name of the contributors may not 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 HOLDER 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.
+
+#include "QsKineticScroller.h"
+#include <QApplication>
+#include <QScrollBar>
+#include <QAbstractScrollArea>
+#include <QMouseEvent>
+#include <QEvent>
+#include <QTimer>
+#include <cstddef> // for NULL
+
+// A number of mouse moves are ignored after a press to differentiate
+// it from a press & drag.
+static const int gMaxIgnoredMouseMoves = 4;
+// The timer measures the drag speed & handles kinetic scrolling. Adjusting
+// the timer interval will change the scrolling speed and smoothness.
+static const int gTimerInterval = 30;
+// The speed measurement is imprecise, limit it so that the scrolling is not
+// too fast.
+static const int gMaxDecelerationSpeed = 30;
+// influences how fast the scroller decelerates
+static const int gFriction = 1;
+
+class QsKineticScrollerImpl
+{
+public:
+   QsKineticScrollerImpl()
+      : scrollArea(NULL)
+      , isPressed(false)
+      , isMoving(false)
+      , lastMouseYPos(0)
+      , lastScrollBarPosition(0)
+      , velocity(0)
+      , ignoredMouseMoves(0)
+      , ignoredMouseActions(0) {}
+
+   void stopMotion()
+   {
+      isMoving = false;
+      velocity = 0;
+      kineticTimer.stop();
+   }
+
+   QAbstractScrollArea* scrollArea;
+   bool isPressed;
+   bool isMoving;
+   QPoint lastPressPoint;
+   int lastMouseYPos;
+   int lastScrollBarPosition;
+   int velocity;
+   int ignoredMouseMoves;
+   int ignoredMouseActions;
+   QTimer kineticTimer;
+};
+
+QsKineticScroller::QsKineticScroller(QObject *parent)
+   : QObject(parent)
+   , d(new QsKineticScrollerImpl)
+{
+   connect(&d->kineticTimer, SIGNAL(timeout()), SLOT(onKineticTimerElapsed()));
+}
+
+// needed by smart pointer
+QsKineticScroller::~QsKineticScroller()
+{
+}
+
+void QsKineticScroller::enableKineticScrollFor(QAbstractScrollArea* scrollArea)
+{
+   if( !scrollArea )
+   {
+      Q_ASSERT_X(0, "kinetic scroller", "missing scroll area");
+      return;
+   }
+
+   // remove existing association
+   if( d->scrollArea )
+   {
+      d->scrollArea->viewport()->removeEventFilter(this);
+      d->scrollArea->removeEventFilter(this);
+      d->scrollArea = NULL;
+   }
+
+   // associate
+   scrollArea->installEventFilter(this);
+   scrollArea->viewport()->installEventFilter(this);
+   d->scrollArea = scrollArea;
+}
+
+//! intercepts mouse events to make the scrolling work
+bool QsKineticScroller::eventFilter(QObject* object, QEvent* event)
+{
+   const QEvent::Type eventType = event->type();
+   const bool isMouseAction = QEvent::MouseButtonPress == eventType
+      || QEvent::MouseButtonRelease == eventType;
+   const bool isMouseEvent = isMouseAction || QEvent::MouseMove == eventType;
+   if( !isMouseEvent || !d->scrollArea )
+     return false;
+   if( isMouseAction && d->ignoredMouseActions-- > 0 ) // don't filter simulated click
+     return false;
+
+   QMouseEvent* const mouseEvent = static_cast<QMouseEvent*>(event);
+   switch( eventType )
+   {
+   case QEvent::MouseButtonPress:
+      {
+         d->isPressed = true;
+         d->lastPressPoint = mouseEvent->pos();
+         d->lastScrollBarPosition = d->scrollArea->verticalScrollBar()->value();
+         if( d->isMoving ) // press while kinetic scrolling, so stop
+            d->stopMotion();
+      }
+      break;
+   case QEvent::MouseMove:
+      {
+         if( !d->isMoving )
+         {
+            // A few move events are ignored as "click jitter", but after that we
+            // assume that the user is doing a click & drag
+            if( d->ignoredMouseMoves < gMaxIgnoredMouseMoves )
+               ++d->ignoredMouseMoves;
+            else
+            {
+               d->ignoredMouseMoves = 0;
+               d->isMoving = true;
+               d->lastMouseYPos = mouseEvent->pos().y();
+               if( !d->kineticTimer.isActive() )
+                  d->kineticTimer.start(gTimerInterval);
+            }
+         }
+         else
+         {
+            // manual scroll
+            const int dragDistance = mouseEvent->pos().y() - d->lastPressPoint.y();
+            d->scrollArea->verticalScrollBar()->setValue(
+               d->lastScrollBarPosition - dragDistance);
+         }
+      }
+      break;
+   case QEvent::MouseButtonRelease:
+      {
+         d->isPressed = false;
+         // Looks like the user wanted a single click. Simulate the click,
+         // as the events were already consumed
+         if( !d->isMoving )
+         {
+            QMouseEvent* mousePress = new QMouseEvent(QEvent::MouseButtonPress,
+               d->lastPressPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+            QMouseEvent* mouseRelease = new QMouseEvent(QEvent::MouseButtonRelease,
+               d->lastPressPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+
+            d->ignoredMouseActions = 2;
+            QApplication::postEvent(object, mousePress);
+            QApplication::postEvent(object, mouseRelease);
+         }
+      }
+      break;
+   default:
+      break;
+   }
+
+   return true; // filter event
+}
+
+void QsKineticScroller::onKineticTimerElapsed()
+{
+   if( d->isPressed && d->isMoving )
+   {
+      // the speed is measured between two timer ticks
+      const int cursorYPos = d->scrollArea->mapFromGlobal(QCursor::pos()).y();
+      d->velocity = cursorYPos - d->lastMouseYPos;
+      d->lastMouseYPos = cursorYPos;
+   }
+   else if( !d->isPressed && d->isMoving )
+   {
+      // use the previously recorded speed and gradually decelerate
+      d->velocity = qBound(-gMaxDecelerationSpeed, d->velocity, gMaxDecelerationSpeed);
+      if( d->velocity > 0 )
+         d->velocity -= gFriction;
+      else if( d->velocity < 0 )
+         d->velocity += gFriction;
+      if( qAbs(d->velocity) < qAbs(gFriction) )
+         d->stopMotion();
+
+      const int scrollBarYPos = d->scrollArea->verticalScrollBar()->value();
+      d->scrollArea->verticalScrollBar()->setValue(scrollBarYPos - d->velocity);
+   }
+   else
+      d->stopMotion();
+}
diff --git a/QsKineticScroller.h b/QsKineticScroller.h
new file mode 100644 (file)
index 0000000..41c0da7
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright (c) 2010, Razvan Petru
+// All rights reserved.
+
+// 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.
+// * The name of the contributors may not 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 HOLDER 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.
+
+#ifndef QSKINETICSCROLLER_H
+#define QSKINETICSCROLLER_H
+
+#include <QObject>
+#include <QScopedPointer>
+class QsKineticScrollerImpl;
+class QAbstractScrollArea;
+class QEvent;
+
+//! Vertical kinetic scroller implementation without overshoot and bouncing.
+//! A temporary solution to get kinetic-like scrolling on Symbian.
+class QsKineticScroller: public QObject
+{
+   Q_OBJECT
+public:
+   QsKineticScroller(QObject* parent = 0);
+   ~QsKineticScroller();
+   //! enabled for one widget only, new calls remove previous association
+   void enableKineticScrollFor(QAbstractScrollArea* scrollArea);
+
+protected:
+   bool eventFilter(QObject* object, QEvent* event);
+
+private slots:
+   void onKineticTimerElapsed();
+
+private:
+   QScopedPointer<QsKineticScrollerImpl> d;
+};
+
+#endif // QSKINETICSCROLLER_H
index 01e4a308df3ea73371c1b0449f6f6a1a70eb2f7e..a91eca3fb2678aa8d5f33b66b940c9cb1491a4fe 100644 (file)
@@ -13,9 +13,11 @@ TEMPLATE = app
 
 
 SOURCES += main.cpp\
-        mainwindow.cpp
+        mainwindow.cpp \
+    QsKineticScroller.cpp
 
-HEADERS  += mainwindow.h
+HEADERS  += mainwindow.h \
+    QsKineticScroller.h
 
 #FORMS    += mainwindow.ui
 
index fe3217d59b287b440983a4d53b2a0d91a8841448..65e7a3065c979f239c03915d7d81e3595d3fc314 100644 (file)
 #include <QDateTime>
 #include <QFileDialog>
 #include <QScrollArea>
+
 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
 {
     qDebug()<<"begin of MainWindows";
-    conf=new QSettings(QString("FwF"), QString("diary-mobile"), this);
+    conf=new QSettings(QString("FwF"), QString("diary-mobile-android"), this);
     if(conf->allKeys().size()==0){
         qDebug()<<"first";
         firstrun();
         conf->setValue(QString("first"), 1);
     };
     screen=new QWidget(this);
-    screen->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-    /*scroll=new QScrollArea(screen);
-    scroll->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-    scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
-    scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);*/
-    grid=new QGridLayout(screen);
-    //scroll->show();
+    screen->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+    scroll=new QScrollArea(screen);
+    scroll->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+    scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+    scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+    scroll->setWidgetResizable(1);
+    grid=new QGridLayout(scroll);
+    scroll->show();
     grid->setSizeConstraint(QLayout::SetMinimumSize);
-
+    ks=new QsKineticScroller(scroll);
+    ks->enableKineticScrollFor(scroll);
 
     signalmap=new QSignalMapper(this);
     connect(signalmap, SIGNAL(mapped(const QString &)), this, SLOT(action(const QString &)));
@@ -286,6 +289,7 @@ void MainWindow::firstrun(){
     conf->setValue(QString("editbuttons"), QVariant("select id,keyword,label,action from ui where id=?;"));
     conf->setValue(QString("updatebuttons"), QVariant("update ui set keyword=?, label=?, action=? where id=?;"));
     conf->setValue(QString("deletebuttons"), QVariant("delete from ui where id=?"));
+    qDebug()<<"firstrun() finished";
 }
 QString &MainWindow::get(int row, int column){
     q->seek(row);
@@ -379,9 +383,9 @@ void MainWindow::getButtons(const QString &b){
         addWidget(q->value(actCol).toString(), new CLabel(q->value(labCol).toString(), conf->value(QString("button")).toString()));
         i++;
     };
-    /*grid->update();
+    grid->update();
     scroll->updateGeometry();
-    screen->updateGeometry();*/
+    screen->updateGeometry();
 
     qDebug()<<"end getButtons : "<<i<<" button(s) way.size()"<<way.size();
 }
@@ -473,7 +477,7 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event)
     else if(k==Qt::Key_VolumeUp){
         action(conf->value(QString("volumup")).toString());
     }
-    //Qt::Key_VolumeDown Qt::Key_VolumeUp
+
 
 }
 void CLabel::SetTextToLabel(const QString& text)
index 2eb4b9782abefea183032e030b0f8d0c1c1e0134..86d4f85eed1555b523e6dc99dd5ea5a8df1fbf5f 100644 (file)
@@ -28,7 +28,7 @@
 #include <QNetworkAccessManager>
 #include <QNetworkRequest>
 #include <QNetworkReply>
-
+#include <QsKineticScroller.h>
 class FileDownloader : public QObject
 {
     Q_OBJECT
@@ -135,7 +135,8 @@ public slots:
 signals:
     void datafilled();
 private:
-    //QScrollArea * scroll;
+    QsKineticScroller * ks;
+    QScrollArea * scroll;
     QMutex sql,updatelocker;
     QWidget *screen;
     QGridLayout *grid;