summaryrefslogtreecommitdiffstats
path: root/touchy/qscrollareakineticscroller.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'touchy/qscrollareakineticscroller.cpp')
-rw-r--r--touchy/qscrollareakineticscroller.cpp329
1 files changed, 329 insertions, 0 deletions
diff --git a/touchy/qscrollareakineticscroller.cpp b/touchy/qscrollareakineticscroller.cpp
new file mode 100644
index 0000000..6702325
--- /dev/null
+++ b/touchy/qscrollareakineticscroller.cpp
@@ -0,0 +1,329 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module 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 <qscrollareakineticscroller.h>
+#include <QApplication>
+#include <QGraphicsView>
+#include <QAbstractScrollArea>
+#include <QScrollBar>
+#include <QItemSelection>
+#include <QPointF>
+#include <QPointer>
+#include <QMouseEvent>
+#if QT_VERSION < 0x040700
+# include <QTime>
+#else
+# include <QElapsedTimer>
+#endif
+
+#include "qabstractitemview.h"
+#include "qgraphicsview.h"
+#include "qgraphicsitem.h"
+
+
+
+#include <QtDebug>
+
+QT_BEGIN_NAMESPACE
+
+
+bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event)
+{
+ return QCoreApplication::sendSpontaneousEvent(receiver, event);
+}
+
+
+
+class Q_GUI_EXPORT QScrollAreaKineticScrollerPrivate
+{
+public:
+ QScrollAreaKineticScroller *q_ptr;
+
+ QAbstractScrollArea *area;
+ QItemSelection oldSelection; // the selection before the first mouse down
+ bool ignoreEvents;
+ QPoint lastOvershoot; // don't change the type to QPointF or we might never shoot completely back.
+ QPointer<QWidget> childWidget; // the widget where the mouse was pressed
+ QPointF fractionalPosition;
+
+#if QT_VERSION < 0x040700
+ QTime timer;
+#else
+ QElapsedTimer timer;
+#endif
+
+ QScrollAreaKineticScrollerPrivate()
+ : q_ptr(0)
+ , area(0)
+ , ignoreEvents(false)
+ {}
+
+ void init()
+ {
+ timer.start();
+ }
+
+ static QWidget *mouseTransparentChildAtGlobalPos(QWidget *parent, const QPoint &gp)
+ {
+ foreach (QWidget *w, parent->findChildren<QWidget *>()) {
+ if (w && !w->isWindow() && w->isVisible() && (w->rect().contains(w->mapFromGlobal(gp)))) {
+ if (QWidget *t = mouseTransparentChildAtGlobalPos(w, gp))
+ return t;
+ else
+ return w;
+ }
+ }
+ return 0;
+ }
+
+ void sendEvent(QWidget *w, QEvent *e)
+ {
+ ignoreEvents = true;
+ qt_sendSpontaneousEvent(w, e);
+ ignoreEvents = false;
+ }
+};
+
+/*!
+ * The QScrollAreaKineticScroller class implements the QKineticScroller for the AbstractScrollArea
+ */
+QScrollAreaKineticScroller::QScrollAreaKineticScroller()
+ : QObject()
+ , QKineticScroller()
+ , d_ptr(new QScrollAreaKineticScrollerPrivate())
+{
+ Q_D(QScrollAreaKineticScroller);
+ d->q_ptr = this;
+ d->init();
+}
+
+/*!
+ Destroys the scroller.
+*/
+QScrollAreaKineticScroller::~QScrollAreaKineticScroller()
+{}
+
+void QScrollAreaKineticScroller::setWidget(QAbstractScrollArea *widget)
+{
+ Q_D(QScrollAreaKineticScroller);
+
+ if (d->area) {
+ d->area->viewport()->removeEventFilter(this);
+ d->area->viewport()->move(d->area->viewport()->pos() + d->lastOvershoot);
+ d->area->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, false);
+ }
+
+ reset();
+ d->area = widget;
+ d->lastOvershoot = QPoint();
+ d->fractionalPosition = QPointF();
+
+ setParent(d->area);
+ if (d->area) {
+ d->area->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true);
+ d->area->viewport()->installEventFilter(this);
+ if (QAbstractItemView *view = qobject_cast<QAbstractItemView *>(d->area)) {
+ view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
+ }
+ }
+}
+
+bool QScrollAreaKineticScroller::eventFilter(QObject *o, QEvent *e)
+{
+ Q_D(QScrollAreaKineticScroller);
+
+ qint64 timestamp = d->timer.elapsed();
+ bool res = false;
+ bool isMouseEvent = false;
+ bool isMouseDown = false;
+
+ if (d->area && (o == d->area->viewport()) &&
+ !d->ignoreEvents && d->area->isEnabled() && isEnabled()) {
+ switch (e->type()) {
+ case QEvent::TouchBegin: {
+ QTouchEvent *te = static_cast<QTouchEvent *>(e);
+ if (te->touchPoints().count() == 1)
+ res = handleInput(InputPress, te->touchPoints().at(0).pos() - d->lastOvershoot, timestamp);
+ break;
+ }
+ case QEvent::TouchUpdate: {
+ QTouchEvent *te = static_cast<QTouchEvent *>(e);
+ if (te->touchPoints().count() == 1)
+ res = handleInput(InputMove, te->touchPoints().at(0).pos() - d->lastOvershoot, timestamp);
+ break;
+ }
+ case QEvent::TouchEnd: {
+ QTouchEvent *te = static_cast<QTouchEvent *>(e);
+ if (te->touchPoints().count() == 1)
+ res = handleInput(InputRelease, te->touchPoints().at(0).pos() - d->lastOvershoot, timestamp);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (res)
+ e->accept();
+ res |= QObject::eventFilter(o, e);
+
+ return res;
+}
+
+void QScrollAreaKineticScroller::stateChanged(State oldState)
+{
+ Q_D(QScrollAreaKineticScroller);
+
+ // hack to remove the current selection as soon as we start to scroll
+ if (QAbstractItemView *view = qobject_cast<QAbstractItemView *>(d->area)) {
+ if (oldState == StateInactive)
+ d->oldSelection = view->selectionModel()->selection(); // store the selection
+ }
+}
+
+bool QScrollAreaKineticScroller::canStartScrollingAt(const QPointF &pos) const
+{
+ Q_D(const QScrollAreaKineticScroller);
+
+ // don't start scrolling when a drag mode has been set.
+ // don't start scrolling on a movable item.
+ if (QGraphicsView *view = qobject_cast<QGraphicsView *>(d->area)) {
+ if (view->dragMode() != QGraphicsView::NoDrag)
+ return false;
+
+ QGraphicsItem *childItem = view->itemAt(pos.toPoint());
+
+ if (childItem && (childItem->flags() & QGraphicsItem::ItemIsMovable))
+ return false;
+ }
+
+ // don't start scrolling on a QSlider
+ if (qobject_cast<QSlider *>(d->mouseTransparentChildAtGlobalPos(d->area->viewport(), d->area->viewport()->mapToGlobal(pos.toPoint())))) {
+ return false;
+ }
+
+ return true;
+}
+
+void QScrollAreaKineticScroller::cancelPress(const QPointF &pressPos)
+{
+ Q_D(QScrollAreaKineticScroller);
+
+ QPoint pos = pressPos.toPoint();
+
+ /*
+ if (QWidget *childWidget = d->mouseTransparentChildAtGlobalPos(d->area->viewport(), d->view->mapToGlobal(pos))) {
+ */
+ if (d->childWidget) {
+ // simulate a mouse-move and mouse-release far, far away
+ QPoint faraway(-1000, -1000);
+ QMouseEvent cmem(QEvent::MouseMove, faraway, d->childWidget->mapToGlobal(faraway),
+ Qt::LeftButton, QApplication::mouseButtons() | Qt::LeftButton,
+ QApplication::keyboardModifiers());
+ d->sendEvent(d->childWidget, &cmem);
+
+ QMouseEvent cmer(QEvent::MouseButtonRelease, faraway, d->childWidget->mapToGlobal(faraway),
+ Qt::LeftButton, QApplication::mouseButtons() & ~Qt::LeftButton,
+ QApplication::keyboardModifiers());
+ d->sendEvent(d->childWidget, &cmer);
+ }
+
+ if (QAbstractItemView *view = qobject_cast<QAbstractItemView *>(d->area))
+ view->selectionModel()->select(d->oldSelection, QItemSelectionModel::ClearAndSelect);
+}
+
+QSizeF QScrollAreaKineticScroller::viewportSize() const
+{
+ Q_D(const QScrollAreaKineticScroller);
+
+ return d->area ? QSizeF(d->area->viewport()->size()) : QSizeF();
+}
+
+QPointF QScrollAreaKineticScroller::maximumContentPosition() const
+{
+ Q_D(const QScrollAreaKineticScroller);
+
+ QPointF p;
+ if (d->area) {
+ if (QScrollBar *s = d->area->horizontalScrollBar())
+ p.setX(s->maximum());
+ if (QScrollBar *s = d->area->verticalScrollBar())
+ p.setY(s->maximum());
+ }
+ return p;
+}
+
+QPointF QScrollAreaKineticScroller::contentPosition() const
+{
+ Q_D(const QScrollAreaKineticScroller);
+
+ QPointF p;
+ if (d->area) {
+ if (QScrollBar *s = d->area->horizontalScrollBar())
+ p.setX(s->value());
+ if (QScrollBar *s = d->area->verticalScrollBar())
+ p.setY(s->value());
+ p += d->fractionalPosition;
+ }
+ return p;
+}
+
+void QScrollAreaKineticScroller::setContentPosition(const QPointF &p, const QPointF &overshootDelta)
+{
+ Q_D(QScrollAreaKineticScroller);
+
+ if (d->area) {
+ if (QScrollBar *s = d->area->horizontalScrollBar())
+ s->setValue(p.x());
+ if (QScrollBar *s = d->area->verticalScrollBar())
+ s->setValue(p.y());
+
+ QPoint delta = d->lastOvershoot - overshootDelta.toPoint();
+ if (!delta.isNull())
+ d->area->viewport()->move(d->area->viewport()->pos() + delta);
+ d->lastOvershoot -= delta;
+
+ d->fractionalPosition = QPointF(p.x() - int(p.x()), p.y() - int(p.y()));
+ }
+}
+
+QT_END_NAMESPACE