summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Griebl <rgriebl@trolltech.com>2010-04-19 18:23:37 +0200
committerRobert Griebl <rgriebl@trolltech.com>2010-04-19 18:23:37 +0200
commitfe45b9c462461d3923b7eef8708a4112e95704ed (patch)
treebca7b1a2f6c32363bdd16282367040161606d6ae
parent668581db5b42ad69397749bf2055e8c3747f38b0 (diff)
touch test
-rw-r--r--kineticscroller.pro2
-rw-r--r--touchy/main.cpp33
-rw-r--r--touchy/plotwidget.cpp171
-rw-r--r--touchy/plotwidget.h81
-rw-r--r--touchy/qscrollareakineticscroller.cpp329
-rw-r--r--touchy/qscrollareakineticscroller.h92
-rw-r--r--touchy/settingswidget.cpp362
-rw-r--r--touchy/settingswidget.h70
-rw-r--r--touchy/touchy.pro16
9 files changed, 1155 insertions, 1 deletions
diff --git a/kineticscroller.pro b/kineticscroller.pro
index 0d4e45f..1506497 100644
--- a/kineticscroller.pro
+++ b/kineticscroller.pro
@@ -1,2 +1,2 @@
TEMPLATE = subdirs
-SUBDIRS = scroller testapp
+SUBDIRS = scroller testapp touchy
diff --git a/touchy/main.cpp b/touchy/main.cpp
new file mode 100644
index 0000000..698ec64
--- /dev/null
+++ b/touchy/main.cpp
@@ -0,0 +1,33 @@
+#include <QtGui>
+#include "qscrollareakineticscroller.h"
+
+#include "settingswidget.h"
+#include "plotwidget.h"
+
+int main(int argc, char **argv)
+{
+ QApplication a(argc, argv);
+
+ QListWidget *lw = new QListWidget();
+
+ for (int i = 0; i < 1000; ++i)
+ new QListWidgetItem(QString("Oa dsjfhdk jhdsjk hfdskj k %1").arg(i), lw);
+
+ QScrollAreaKineticScroller *s = new QScrollAreaKineticScroller();
+ s->setWidget(lw);
+
+ QWidget *settings = new SettingsWidget(s);
+ QWidget *plot = new PlotWidget(s);
+
+ QSplitter *split = new QSplitter();
+ split->addWidget(lw);
+ QTabWidget *tab = new QTabWidget();
+ tab->addTab(settings, settings->windowTitle());
+ tab->addTab(plot, plot->windowTitle());
+ split->addWidget(tab);
+
+ split->show();
+ split->raise();
+
+ return a.exec();
+}
diff --git a/touchy/plotwidget.cpp b/touchy/plotwidget.cpp
new file mode 100644
index 0000000..019c3c8
--- /dev/null
+++ b/touchy/plotwidget.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** 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 <QtGui>
+
+#include "plotwidget.h"
+#include "qkineticscroller.h"
+
+PlotWidget::PlotWidget(QKineticScroller *scroller)
+ : QWidget(), m_scroller(scroller)
+{
+ setWindowTitle(QLatin1String("Plot"));
+ m_scroller->registerDebugHook(debugHook, this);
+
+ m_clear = new QPushButton(QLatin1String("Clear"), this);
+ connect(m_clear, SIGNAL(clicked()), this, SLOT(reset()));
+ m_legend = new QLabel(this);
+ QString legend;
+ QTextStream ts(&legend);
+ ts << "<table border=\"0\">";
+ ts << "<tr><td width=\"30\" bgcolor=\"" << QColor(Qt::red).light().name() << "\" /><td>Velocity X</td></tr>";
+ ts << "<tr><td width=\"30\" bgcolor=\"" << QColor(Qt::red).dark().name() << "\" /><td>Velocity Y</td></tr>";
+ ts << "<tr><td width=\"30\" bgcolor=\"" << QColor(Qt::green).light().name() << "\" /><td>Content Position X</td></tr>";
+ ts << "<tr><td width=\"30\" bgcolor=\"" << QColor(Qt::green).dark().name() << "\" /><td>Content Position Y</td></tr>";
+ ts << "<tr><td width=\"30\" bgcolor=\"" << QColor(Qt::blue).light().name() << "\" /><td>Overshoot Position X</td></tr>";
+ ts << "<tr><td width=\"30\" bgcolor=\"" << QColor(Qt::blue).dark().name() << "\" /><td>Overshoot Position Y</td></tr>";
+ ts << "</table>";
+ m_legend->setText(legend);
+}
+
+void PlotWidget::debugHook(void *user, const QPointF &releaseVelocity, const QPointF &contentPosition, const QPointF &overshootPosition)
+{
+ PlotItem pi = { releaseVelocity, contentPosition, overshootPosition };
+ static_cast<PlotWidget *>(user)->addPlotItem(pi);
+}
+
+static inline void doMaxMin(const QPointF &v, qreal &minmaxv)
+{
+ minmaxv = qMax(minmaxv, qMax(qAbs(v.x()), qAbs(v.y())));
+}
+
+void PlotWidget::addPlotItem(const PlotItem &pi)
+{
+ m_plotitems.append(pi);
+ minMaxVelocity = minMaxPosition = 0;
+
+ while (m_plotitems.size() > 500)
+ m_plotitems.removeFirst();
+
+ foreach (const PlotItem &pi, m_plotitems) {
+ doMaxMin(pi.releaseVelocity, minMaxVelocity);
+ doMaxMin(pi.contentPosition, minMaxPosition);
+ doMaxMin(pi.overshootPosition, minMaxPosition);
+ }
+ update();
+}
+
+void PlotWidget::reset()
+{
+ m_plotitems.clear();
+ minMaxVelocity = minMaxPosition = 0;
+ update();
+}
+
+void PlotWidget::resizeEvent(QResizeEvent *)
+{
+ QSize cs = m_clear->sizeHint();
+ QSize ls = m_legend->sizeHint();
+ m_clear->setGeometry(4, 4, cs.width(), cs.height());
+ m_legend->setGeometry(4, height() - ls.height() - 4, ls.width(), ls.height());
+}
+
+void PlotWidget::paintEvent(QPaintEvent *)
+{
+#define SCALE(v, mm) ((qreal(1) - (v / mm)) * qreal(0.5) * height())
+
+ QColor rvColor = Qt::red;
+ QColor cpColor = Qt::green;
+ QColor opColor = Qt::blue;
+
+
+ QPainter p(this);
+ p.setRenderHints(QPainter::Antialiasing);
+ p.fillRect(rect(), Qt::white);
+
+ p.setPen(Qt::black);
+ p.drawLine(0, SCALE(0, 1), width(), SCALE(0, 1));
+
+ if (m_plotitems.isEmpty())
+ return;
+
+ int x = 2;
+ int offset = m_plotitems.size() - width() / 2;
+ QList<PlotItem>::const_iterator it = m_plotitems.constBegin();
+ if (offset > 0)
+ it += (offset - 1);
+
+ const PlotItem *last = &(*it++);
+
+ while (it != m_plotitems.constEnd()) {
+ p.setPen(rvColor.light());
+ p.drawLine(qreal(x - 2), SCALE(last->releaseVelocity.x(), minMaxVelocity),
+ qreal(x), SCALE(it->releaseVelocity.x(), minMaxVelocity));
+ p.setPen(rvColor.dark());
+ p.drawLine(qreal(x - 2), SCALE(last->releaseVelocity.y(), minMaxVelocity),
+ qreal(x), SCALE(it->releaseVelocity.y(), minMaxVelocity));
+
+ p.setPen(cpColor.light());
+ p.drawLine(qreal(x - 2), SCALE(last->contentPosition.x(), minMaxPosition),
+ qreal(x), SCALE(it->contentPosition.x(), minMaxPosition));
+ p.setPen(cpColor.dark());
+ p.drawLine(qreal(x - 2), SCALE(last->contentPosition.y(), minMaxPosition),
+ qreal(x), SCALE(it->contentPosition.y(), minMaxPosition));
+
+ p.setPen(opColor.light());
+ p.drawLine(qreal(x - 2), SCALE(last->overshootPosition.x(), minMaxPosition),
+ qreal(x), SCALE(it->overshootPosition.x(), minMaxPosition));
+ p.setPen(opColor.dark());
+ p.drawLine(qreal(x - 2), SCALE(last->overshootPosition.y(), minMaxPosition),
+ qreal(x), SCALE(it->overshootPosition.y(), minMaxPosition));
+
+ last = &(*it++);
+ x += 2;
+ }
+
+ QString toptext = QString("%1 [m/s] / %2 [pix]").arg(minMaxVelocity, 0, 'f', 2).arg(minMaxPosition, 0, 'f', 2);
+ QString bottomtext = QString("-%1 [m/s] / -%2 [pix]").arg(minMaxVelocity, 0, 'f', 2).arg(minMaxPosition, 0, 'f', 2);
+
+ p.setPen(Qt::black);
+ p.drawText(rect(), Qt::AlignTop | Qt::AlignHCenter, toptext);
+ p.drawText(rect(), Qt::AlignBottom | Qt::AlignHCenter, bottomtext);
+#undef SCALE
+}
diff --git a/touchy/plotwidget.h b/touchy/plotwidget.h
new file mode 100644
index 0000000..80e76ad
--- /dev/null
+++ b/touchy/plotwidget.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef PLOTWIDGET_H
+#define PLOTWIDGET_H
+
+#include <QtGui>
+
+class QKineticScroller;
+
+class PlotWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ PlotWidget(QKineticScroller *scroller);
+
+public slots:
+ void reset();
+
+protected:
+ void resizeEvent(QResizeEvent *);
+ void paintEvent(QPaintEvent *);
+
+private:
+ static void debugHook(void *user, const QPointF &releaseVelocity, const QPointF &contentPosition, const QPointF &overshootPosition);
+
+ struct PlotItem {
+ QPointF releaseVelocity;
+ QPointF contentPosition;
+ QPointF overshootPosition;
+ };
+
+ void addPlotItem(const PlotItem &pi);
+
+ QKineticScroller *m_scroller;
+ QList<PlotItem> m_plotitems;
+ qreal minMaxVelocity, minMaxPosition;
+ QPushButton *m_clear;
+ QLabel *m_legend;
+};
+
+#endif
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
diff --git a/touchy/qscrollareakineticscroller.h b/touchy/qscrollareakineticscroller.h
new file mode 100644
index 0000000..77b0caf
--- /dev/null
+++ b/touchy/qscrollareakineticscroller.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QSCROLLAREAKINETICSCROLLER_H
+#define QSCROLLAREAKINETICSCROLLER_H
+
+#include <qkineticscroller.h>
+#include <QAbstractScrollArea>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QScrollAreaKineticScrollerPrivate;
+
+class Q_GUI_EXPORT QScrollAreaKineticScroller : public QObject, public QKineticScroller
+{
+ Q_OBJECT
+public:
+ QScrollAreaKineticScroller();
+ // QScrollAreaKineticScroller(QScrollAreaKineticScrollerPrivate &dd);
+ ~QScrollAreaKineticScroller();
+
+ void setWidget(QAbstractScrollArea *widget);
+
+ bool eventFilter(QObject *o, QEvent *e);
+
+protected:
+
+ virtual QSizeF viewportSize() const;
+ virtual QPointF maximumContentPosition() const;
+ virtual QPointF contentPosition() const;
+ virtual void setContentPosition(const QPointF &pos, const QPointF &overshootDelta);
+
+ virtual void stateChanged(State oldState);
+ virtual bool canStartScrollingAt(const QPointF &pos) const;
+ virtual void cancelPress(const QPointF &pressPos);
+
+ QScopedPointer<QScrollAreaKineticScrollerPrivate> d_ptr;
+
+private:
+ Q_DISABLE_COPY(QScrollAreaKineticScroller)
+ Q_DECLARE_PRIVATE(QScrollAreaKineticScroller)
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QScrollAreaKineticScroller *)
+
+QT_END_HEADER
+
+#endif // QKINETICSCROLLER_H
diff --git a/touchy/settingswidget.cpp b/touchy/settingswidget.cpp
new file mode 100644
index 0000000..7f41a7b
--- /dev/null
+++ b/touchy/settingswidget.cpp
@@ -0,0 +1,362 @@
+/****************************************************************************
+**
+** 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 <QtGui>
+
+#include "settingswidget.h"
+#include "qkineticscroller.h"
+
+struct MetricItem
+{
+ QKineticScroller::ScrollMetric metric;
+ const char *name;
+ int scaling;
+ const char *unit;
+ QVariant min, max;
+ QVariant step;
+};
+
+class MetricItemUpdater : public QObject
+{
+ Q_OBJECT
+public:
+ MetricItemUpdater(MetricItem *item, QKineticScroller *scroller)
+ : m_item(item), m_scroller(scroller)
+ {
+ if (m_item->min.type() == QVariant::PointF) {
+ m_slider = new QSlider(Qt::Horizontal);
+ m_slider->setSingleStep(1);
+ m_slider->setMinimum(-1);
+ m_slider->setMaximum(int((m_item->max.toPointF().x() - m_item->min.toPointF().x()) / m_item->step.toReal()));
+ m_slider->setValue(-1);
+ m_slider2 = new QSlider(Qt::Horizontal);
+ m_slider2->setSingleStep(1);
+ m_slider2->setMinimum(-1);
+ m_slider2->setMaximum(int((m_item->max.toPointF().y() - m_item->min.toPointF().y()) / m_item->step.toReal()));
+ m_slider2->setValue(-1);
+ m_control = new QWidget();
+ QHBoxLayout *lay = new QHBoxLayout(m_control);
+ lay->setContentsMargins(0, 0, 0, 0);
+ lay->addWidget(m_slider);
+ lay->addWidget(m_slider2);
+ } else {
+ m_slider = new QSlider(Qt::Horizontal);
+ m_slider->setSingleStep(1);
+ m_slider->setMinimum(-1);
+ m_slider->setMaximum(int((m_item->max.toReal() - m_item->min.toReal()) / m_item->step.toReal()));
+ m_slider->setValue(-1);
+ m_slider2 = 0;
+ m_control = m_slider;
+ }
+ m_unitLabel = new QLabel(QLatin1String(m_item->unit));
+ m_valueLabel = new QLabel();
+ m_nameLabel = new QLabel(QLatin1String(m_item->name));
+ m_resetButton = new QToolButton();
+ m_resetButton->setText(QLatin1String("Reset"));
+ m_resetButton->setEnabled(false);
+
+ connect(m_resetButton, SIGNAL(clicked()), this, SLOT(reset()));
+ connect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int)));
+ if (m_slider2)
+ connect(m_slider2, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int)));
+
+ m_default_value = m_scroller->scrollMetric(m_item->metric);
+ valueChanged(m_default_value);
+
+ m_slider->setMinimum(0);
+ if (m_slider2)
+ m_slider2->setMinimum(0);
+ }
+
+ QWidget *nameLabel() { return m_nameLabel; }
+ QWidget *unitLabel() { return m_unitLabel; }
+ QWidget *valueLabel() { return m_valueLabel; }
+ QWidget *valueControl() { return m_control; }
+ QWidget *resetButton() { return m_resetButton; }
+
+private slots:
+ void valueChanged(const QVariant v)
+ {
+ m_value = v;
+ switch (m_item->min.type()) {
+ case QVariant::Double: {
+ m_slider->setValue(int((m_value.toReal() * m_item->scaling - m_item->min.toReal()) / m_item->step.toReal()));
+ break;
+ }
+ case QVariant::Int: {
+ m_slider->setValue(int((m_value.toInt() * m_item->scaling - m_item->min.toInt()) / m_item->step.toInt()));
+ break;
+ }
+ case QVariant::PointF: {
+ m_slider->setValue(int((m_value.toPointF().x() * m_item->scaling - m_item->min.toPointF().x()) / m_item->step.toReal()));
+ m_slider2->setValue(int((m_value.toPointF().y() * m_item->scaling - m_item->min.toPointF().y()) / m_item->step.toReal()));
+ break;
+ }
+ default: break;
+ }
+ }
+
+ void sliderChanged(int value)
+ {
+ bool second = (m_slider2 && (sender() == m_slider2));
+ QString text;
+
+ switch (m_item->min.type()) {
+ case QVariant::Double: {
+ qreal d = m_item->min.toReal() + qreal(value) * m_item->step.toReal();
+ text = QString::number(d);
+ m_value = d / qreal(m_item->scaling);
+ break;
+ }
+ case QVariant::Int: {
+ int i = m_item->min.toInt() + int(qreal(value) * m_item->step.toReal());
+ text = QString::number(i);
+ m_value = i / m_item->scaling;
+ break;
+ }
+ case QVariant::PointF: {
+ qreal p = (second ? m_item->min.toPointF().y() : m_item->min.toPointF().x()) + qreal(value) * m_item->step.toReal();
+ text = QString("%1, %2").arg(second ? m_value.toPointF().x() * m_item->scaling : p).arg(second ? p : m_value.toPointF().y() * m_item->scaling);
+ m_value = QPointF(second ? m_value.toPointF().x() : p / m_item->scaling, second ? p / m_item->scaling : m_value.toPointF().y());
+ break;
+ }
+ default: break;
+ }
+ m_valueLabel->setText(text);
+ m_scroller->setScrollMetric(m_item->metric, m_value);
+
+ m_resetButton->setEnabled(m_value != m_default_value);
+ }
+
+ void reset()
+ {
+ m_scroller->setScrollMetric(m_item->metric, m_value);
+ valueChanged(m_default_value);
+ }
+
+private:
+ MetricItem *m_item;
+ QKineticScroller *m_scroller;
+
+ QSlider *m_slider, *m_slider2;
+ QLabel *m_unitLabel, *m_nameLabel, *m_valueLabel;
+ QToolButton *m_resetButton;
+ QWidget *m_control;
+
+ QVariant m_value, m_default_value;
+};
+
+#define METRIC(x) QKineticScroller::x, #x
+
+MetricItem items[QKineticScroller::ScrollMetricCount] = {
+ { METRIC(DragVelocitySmoothingFactor), 1, "", qreal(0), qreal(1), qreal(0.01) },
+
+ { METRIC(LinearDecelerationFactor), 1, "m/s^2", qreal(0), qreal(3), qreal(0.01) },
+ { METRIC(ExponentialDecelerationBase), 1, "", qreal(0), qreal(1), qreal(0.01) },
+ { METRIC(OvershootSpringConstant), 1, "kg/s^2", qreal(1), qreal(500), qreal(1) },
+ { METRIC(OvershootDragResistanceFactor), 1, "", qreal(0), qreal(1), qreal(0.01) },
+ { METRIC(OvershootMaximumDistance), 1000, "(mm, mm)", QPointF(0, 0), QPointF(500, 500), qreal(1) },
+
+ { METRIC(DragStartDistance), 1000, "mm", qreal(1), qreal(20), qreal(0.1) },
+ { METRIC(DragStartDirectionErrorMargin), 1000, "mm", qreal(1), qreal(20), qreal(0.1) },
+
+ { METRIC(MinimumVelocity), 1, "m/s", qreal(0), qreal(7), qreal(0.01) },
+ { METRIC(MaximumVelocity), 1, "m/s", qreal(0), qreal(7), qreal(0.01) },
+ { METRIC(MaximumNonAcceleratedVelocity), 1, "m/s", qreal(0), qreal(7), qreal(0.01) },
+
+ { METRIC(MaximumClickThroughVelocity), 1, "m/s", qreal(0), qreal(7), qreal(0.01) },
+ { METRIC(AxisLockThreshold), 1, "", qreal(0), qreal(1), qreal(0.01) },
+
+ { METRIC(FramesPerSecond), 1, "frames/s", int(10), int(100), int(1) },
+
+ { METRIC(FastSwipeMaximumTime), 1000, "ms", qreal(10), qreal(1000), qreal(1) },
+ { METRIC(FastSwipeMinimumVelocity), 1, "m/s", qreal(0), qreal(7), qreal(0.01) },
+ { METRIC(FastSwipeBaseVelocity), 1, "m/s", qreal(0), qreal(7), qreal(0.01) }
+};
+
+#undef METRIC
+
+QGridLayout *SettingsWidget::createMetricsItemGrid()
+{
+ QGridLayout *grid = new QGridLayout();
+ grid->setVerticalSpacing(2);
+ int row = 0;
+
+ for (int i = 0; i < QKineticScroller::ScrollMetricCount; i++) {
+ MetricItemUpdater *u = new MetricItemUpdater(items + i, m_scroller);
+ u->setParent(this);
+
+ grid->addWidget(u->nameLabel(), row, 0);
+ grid->addWidget(u->valueControl(), row, 1);
+ grid->addWidget(u->valueLabel(), row, 2);
+ grid->addWidget(u->unitLabel(), row, 3);
+ grid->addWidget(u->resetButton(), row, 4);
+
+ row++;
+ }
+ return grid;
+}
+
+class HackScroller : public QKineticScroller {
+public:
+ using QKineticScroller::viewportSize;
+ using QKineticScroller::maximumContentPosition;
+};
+
+class HackSpinBox : public QSpinBox {
+public:
+ using QAbstractSpinBox::lineEdit;
+};
+
+SettingsWidget::SettingsWidget(QKineticScroller *scroller)
+ : QWidget(), m_scroller(scroller)
+{
+ setWindowTitle(QLatin1String("Settings"));
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ QGroupBox *grp;
+ QGridLayout *grid;
+
+ grp = new QGroupBox(QLatin1String("General"));
+ grid = new QGridLayout();
+ grid->setVerticalSpacing(2);
+ QCheckBox *onoff = new QCheckBox(QLatin1String("Enabled"));
+ onoff->setChecked(m_scroller->isEnabled());
+ connect(onoff, SIGNAL(toggled(bool)), this, SLOT(enabledChanged(bool)));
+ grid->addWidget(onoff, 0, 0, 1, 2);
+ grid->addWidget(new QLabel("DPI"), 1, 0);
+ QSpinBox *dpi = new QSpinBox();
+ dpi->setRange(10, 1000);
+ dpi->setSuffix(QLatin1String(" dpi"));
+ dpi->setValue(m_scroller->dpi());
+ connect(dpi, SIGNAL(valueChanged(int)), this, SLOT(dpiChanged(int)));
+ grid->addWidget(dpi, 1, 1);
+
+ grid->addWidget(new QLabel("Horizontal Overshoot Policy"), 2, 0);
+ m_hospolicy = new QComboBox();
+ m_hospolicy->addItem("When Scrollable", QKineticScroller::OvershootWhenScrollable);
+ m_hospolicy->addItem("Always On", QKineticScroller::OvershootAlwaysOn);
+ m_hospolicy->addItem("Always Off", QKineticScroller::OvershootAlwaysOff);
+ m_hospolicy->setCurrentIndex(m_hospolicy->findData(m_scroller->horizontalOvershootPolicy()));
+ connect(m_hospolicy, SIGNAL(currentIndexChanged(int)), this, SLOT(overshootPolicyChanged(int)));
+ grid->addWidget(m_hospolicy, 2, 1);
+
+ grid->addWidget(new QLabel("Vertical Overshoot Policy"), 3, 0);
+ m_vospolicy = new QComboBox();
+ m_vospolicy->addItem("When Scrollable", QKineticScroller::OvershootWhenScrollable);
+ m_vospolicy->addItem("Always On", QKineticScroller::OvershootAlwaysOn);
+ m_vospolicy->addItem("Always Off", QKineticScroller::OvershootAlwaysOff);
+ m_vospolicy->setCurrentIndex(m_vospolicy->findData(m_scroller->verticalOvershootPolicy()));
+ connect(m_vospolicy, SIGNAL(currentIndexChanged(int)), this, SLOT(overshootPolicyChanged(int)));
+ grid->addWidget(m_vospolicy, 3, 1);
+ grp->setLayout(grid);
+ layout->addWidget(grp);
+
+ grp = new QGroupBox(QLatin1String("Scroll Metrics"));
+ grid = createMetricsItemGrid();
+ grp->setLayout(grid);
+ layout->addWidget(grp);
+
+ grp = new QGroupBox(QLatin1String("Scroll To"));
+ grid = new QGridLayout();
+
+ QSizeF vp = static_cast<HackScroller *>(m_scroller)->viewportSize();
+ QPointF maxc = static_cast<HackScroller *>(m_scroller)->maximumContentPosition();
+
+ m_scrollx = new QSpinBox();
+ m_scrollx->setRange(int(-vp.width()), int(maxc.x() + vp.width()));
+ m_scrolly = new QSpinBox();
+ m_scrolly->setRange(int(-vp.height()), int(maxc.y() + vp.height()));
+ m_scrolltime = new QSpinBox();
+ m_scrolltime->setRange(0, 10000);
+ m_scrolltime->setValue(1000);
+ m_scrolltime->setSuffix(QLatin1String(" ms"));
+ QPushButton *go = new QPushButton(QLatin1String("Go"));
+ connect(go, SIGNAL(clicked()), this, SLOT(scrollTo()));
+ connect(m_scrollx, SIGNAL(editingFinished()), this, SLOT(scrollTo()));
+ connect(m_scrolly, SIGNAL(editingFinished()), this, SLOT(scrollTo()));
+ connect(m_scrolltime, SIGNAL(editingFinished()), this, SLOT(scrollTo()));
+ grid->addWidget(new QLabel(QLatin1String("X:")), 0, 0);
+ grid->addWidget(m_scrollx, 0, 1);
+ grid->addWidget(new QLabel(QLatin1String("Y:")), 0, 2);
+ grid->addWidget(m_scrolly, 0, 3);
+ grid->addWidget(new QLabel(QLatin1String("in")), 0, 4);
+ grid->addWidget(m_scrolltime, 0, 5);
+ grid->addWidget(go, 0, 6);
+ grid->setColumnStretch(1, 1);
+ grid->setColumnStretch(3, 1);
+ grid->setColumnStretch(5, 1);
+ grid->setColumnStretch(6, 1);
+ grp->setLayout(grid);
+ layout->addWidget(grp);
+ layout->addStretch(100);
+}
+
+void SettingsWidget::enabledChanged(bool on)
+{
+ m_scroller->setEnabled(on);
+}
+
+void SettingsWidget::dpiChanged(int dpi)
+{
+ m_scroller->setDpi(dpi);
+}
+
+void SettingsWidget::overshootPolicyChanged(int index)
+{
+ if (sender() == m_hospolicy)
+ m_scroller->setHorizontalOvershootPolicy(static_cast<QKineticScroller::OvershootPolicy>(m_hospolicy->itemData(index).toInt()));
+ else if (sender() == m_vospolicy)
+ m_scroller->setVerticalOvershootPolicy(static_cast<QKineticScroller::OvershootPolicy>(m_hospolicy->itemData(index).toInt()));
+}
+
+void SettingsWidget::scrollTo()
+{
+ if ((sender() == m_scrollx) && !m_scrollx->hasFocus())
+ return;
+ if ((sender() == m_scrolly) && !m_scrolly->hasFocus())
+ return;
+ if ((sender() == m_scrolltime) && !m_scrolltime->hasFocus())
+ return;
+
+ m_scroller->scrollTo(QPointF(m_scrollx->value(), m_scrolly->value()), m_scrolltime->value());
+}
+
+#include "settingswidget.moc"
diff --git a/touchy/settingswidget.h b/touchy/settingswidget.h
new file mode 100644
index 0000000..22ec6aa
--- /dev/null
+++ b/touchy/settingswidget.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef SETTINGSWIDGET_H
+#define SETTINGSWIDGET_H
+
+#include <QtGui>
+
+class QKineticScroller;
+
+class SettingsWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ SettingsWidget(QKineticScroller *scroller);
+
+private slots:
+ void enabledChanged(bool on);
+ void dpiChanged(int dpi);
+ void overshootPolicyChanged(int index);
+ void scrollTo();
+
+private:
+ QGridLayout *createMetricsItemGrid();
+
+ QKineticScroller *m_scroller;
+ QSpinBox *m_scrollx, *m_scrolly, *m_scrolltime;
+ QComboBox *m_hospolicy, *m_vospolicy;
+};
+
+#endif
diff --git a/touchy/touchy.pro b/touchy/touchy.pro
new file mode 100644
index 0000000..03da8ec
--- /dev/null
+++ b/touchy/touchy.pro
@@ -0,0 +1,16 @@
+######################################################################
+# Automatically generated by qmake (2.01a) Mon Apr 19 14:02:09 2010
+######################################################################
+
+TEMPLATE = app
+TARGET =
+DEPENDPATH += . ../scroller
+INCLUDEPATH += . ../scroller
+LIBS += -L../scroller -lscroller
+
+# Input
+HEADERS += plotwidget.h qscrollareakineticscroller.h settingswidget.h
+SOURCES += main.cpp \
+ plotwidget.cpp \
+ qscrollareakineticscroller.cpp \
+ settingswidget.cpp