From fe45b9c462461d3923b7eef8708a4112e95704ed Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Mon, 19 Apr 2010 18:23:37 +0200 Subject: touch test --- kineticscroller.pro | 2 +- touchy/main.cpp | 33 ++++ touchy/plotwidget.cpp | 171 ++++++++++++++++ touchy/plotwidget.h | 81 ++++++++ touchy/qscrollareakineticscroller.cpp | 329 ++++++++++++++++++++++++++++++ touchy/qscrollareakineticscroller.h | 92 +++++++++ touchy/settingswidget.cpp | 362 ++++++++++++++++++++++++++++++++++ touchy/settingswidget.h | 70 +++++++ touchy/touchy.pro | 16 ++ 9 files changed, 1155 insertions(+), 1 deletion(-) create mode 100644 touchy/main.cpp create mode 100644 touchy/plotwidget.cpp create mode 100644 touchy/plotwidget.h create mode 100644 touchy/qscrollareakineticscroller.cpp create mode 100644 touchy/qscrollareakineticscroller.h create mode 100644 touchy/settingswidget.cpp create mode 100644 touchy/settingswidget.h create mode 100644 touchy/touchy.pro 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 +#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 + +#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 << ""; + ts << ""; + ts << ""; + ts << ""; + ts << ""; + ts << ""; + ts << ""; + ts << "
Velocity X
Velocity Y
Content Position X
Content Position Y
Overshoot Position X
Overshoot Position Y
"; + 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(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::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 + +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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION < 0x040700 +# include +#else +# include +#endif + +#include "qabstractitemview.h" +#include "qgraphicsview.h" +#include "qgraphicsitem.h" + + + +#include + +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 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()) { + 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(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(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(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(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(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(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(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(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 +#include + +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 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 + +#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(m_scroller)->viewportSize(); + QPointF maxc = static_cast(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(m_hospolicy->itemData(index).toInt())); + else if (sender() == m_vospolicy) + m_scroller->setVerticalOvershootPolicy(static_cast(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 + +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 -- cgit v1.2.3