diff options
author | Zeno Albisser <zeno.albisser@nokia.com> | 2010-09-07 08:14:45 -0400 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@nokia.com> | 2010-09-08 09:02:33 -0400 |
commit | 4a9a0c09f3cfb9262f929620fd86609a9b1b3a76 (patch) | |
tree | 1f7dc0864578addf117a94ef5144a91ea4aaf98a |
initial import of GestureArea sources including Recognizers
These sources have been extracted from a previous repository
of the QMLEnablers project. The purpose of these files is to
provide GestureRecognizers loadable as a module in QML.
Reviewed-by: Denis Dzyubenko
-rw-r--r-- | gestureareaplugin_p.h | 71 | ||||
-rw-r--r-- | gestures.pro | 34 | ||||
-rw-r--r-- | plugin.cpp | 91 | ||||
-rw-r--r-- | qdeclarativegesturearea.cpp | 294 | ||||
-rw-r--r-- | qdeclarativegesturearea_p.h | 95 | ||||
-rw-r--r-- | qdeclarativegesturehandler.cpp | 170 | ||||
-rw-r--r-- | qdeclarativegesturehandler_p.h | 208 | ||||
-rw-r--r-- | qdeclarativegesturerecognizers.cpp | 874 | ||||
-rw-r--r-- | qdeclarativegesturerecognizers_p.h | 117 | ||||
-rw-r--r-- | qmldir | 1 |
10 files changed, 1955 insertions, 0 deletions
diff --git a/gestureareaplugin_p.h b/gestureareaplugin_p.h new file mode 100644 index 0000000..95ed762 --- /dev/null +++ b/gestureareaplugin_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** 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 QML Gesture Area plugin 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 GESTUREAREAPLUGIN_P_H +#define GESTUREAREAPLUGIN_P_H + +#include <QtDeclarative/qdeclarativeextensionplugin.h> +#include <QtCore/QSet> +#include <QtCore/QList> +#include <QtCore/QWeakPointer> + +QT_BEGIN_NAMESPACE + +class QDeclarativeGestureArea; +class GestureAreaQmlPlugin : public QDeclarativeExtensionPlugin +{ + Q_OBJECT +public: + GestureAreaQmlPlugin(QObject *parent = 0) : QDeclarativeExtensionPlugin(parent) { self = this; } + ~GestureAreaQmlPlugin() { self = 0; } + + virtual void registerTypes(const char *uri); + + static GestureAreaQmlPlugin *self; + + QSet<Qt::GestureType> allGestures; + // all gesture areas with default handlers + QList<QWeakPointer<QDeclarativeGestureArea> > allDefaultAreas; +}; + +QT_END_NAMESPACE + +#endif diff --git a/gestures.pro b/gestures.pro new file mode 100644 index 0000000..2da157d --- /dev/null +++ b/gestures.pro @@ -0,0 +1,34 @@ +TARGET = qmlgestureareaplugin +TARGETPATH = Qt/labs/gestures + +TEMPLATE = lib +CONFIG += qt plugin +QT += declarative + +SOURCES += qdeclarativegesturearea.cpp \ + qdeclarativegesturehandler.cpp \ + qdeclarativegesturerecognizers.cpp \ + plugin.cpp +HEADERS += qdeclarativegesturearea_p.h \ + gestureareaplugin_p.h \ + qdeclarativegesturehandler_p.h \ + qdeclarativegesturerecognizers_p.h + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/imports/$$TARGETPATH +else:DESTDIR = . +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +symbian:{ + TARGET.UID3 = 0x2002131F + include($$QT_SOURCE_TREE/demos/symbianpkgrules.pri) + + importFiles.sources = $$DESTDIR/qmlgestureareaplugin$${QT_LIBINFIX}.dll qmldir + importFiles.path = $$QT_IMPORTS_BASE_DIR/$$TARGETPATH + + DEPLOYMENT = importFiles +} + +INSTALLS += target qmldir diff --git a/plugin.cpp b/plugin.cpp new file mode 100644 index 0000000..e848868 --- /dev/null +++ b/plugin.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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 QML Gesture Area plugin 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 <QtDeclarative/qdeclarative.h> + +#include "gestureareaplugin_p.h" +#include "qdeclarativegesturearea_p.h" +#include "qdeclarativegesturerecognizers_p.h" + +QT_BEGIN_NAMESPACE + +void GestureAreaQmlPlugin::registerTypes(const char *uri) +{ + Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.gestures")); +#ifndef QT_NO_GESTURES + qmlRegisterType<QDeclarativeGestureArea>(uri, 2, 0, "GestureArea"); + + qmlRegisterUncreatableType<QDeclarativeGestureHandler>(uri, 2, 0, "GestureHandler", QLatin1String("Do not create objects of this type.")); + + qmlRegisterUncreatableType<QGesture>(uri, 2, 0, "Gesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QPanGesture>(uri, 2, 0, "PanGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QTapGesture>(uri, 2, 0, "TapGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QTapAndHoldGesture>(uri, 2, 0, "TapAndHoldGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QPinchGesture>(uri, 2, 0, "PinchGesture", QLatin1String("Do not create objects of this type.")); + qmlRegisterUncreatableType<QSwipeGesture>(uri, 2, 0, "SwipeGesture", QLatin1String("Do not create objects of this type.")); + + qmlRegisterType<QDeclarativeDefaultGestureHandler>(uri, 2, 0, "Default"); + qmlRegisterType<QDeclarativePanGestureHandler>(uri, 2, 0, "Pan"); + qmlRegisterType<QDeclarativeTapGestureHandler>(uri, 2, 0, "Tap"); + qmlRegisterType<QDeclarativeTapAndHoldGestureHandler>(uri, 2, 0, "TapAndHold"); + qmlRegisterType<QDeclarativePinchGestureHandler>(uri, 2, 0, "Pinch"); + qmlRegisterType<QDeclarativeSwipeGestureHandler>(uri, 2, 0, "Swipe"); + + QGestureRecognizer::unregisterRecognizer(Qt::TapGesture); + QGestureRecognizer::unregisterRecognizer(Qt::TapAndHoldGesture); + QGestureRecognizer::unregisterRecognizer(Qt::PanGesture); + QGestureRecognizer::unregisterRecognizer(Qt::PinchGesture); + QGestureRecognizer::unregisterRecognizer(Qt::SwipeGesture); + + QGestureRecognizer::registerRecognizer(new QTapGestureRecognizer()); + QGestureRecognizer::registerRecognizer(new QTapAndHoldGestureRecognizer()); + QGestureRecognizer::registerRecognizer(new QPanGestureRecognizer()); + QGestureRecognizer::registerRecognizer(new QPinchGestureRecognizer()); + QGestureRecognizer::registerRecognizer(new QSwipeGestureRecognizer()); +#endif +} + +GestureAreaQmlPlugin *GestureAreaQmlPlugin::self = 0; + +QT_END_NAMESPACE + + +Q_EXPORT_PLUGIN2(qmlgestureareaplugin, QT_PREPEND_NAMESPACE(GestureAreaQmlPlugin)); diff --git a/qdeclarativegesturearea.cpp b/qdeclarativegesturearea.cpp new file mode 100644 index 0000000..c5f621f --- /dev/null +++ b/qdeclarativegesturearea.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** 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 QML Gesture Area plugin 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 "qdeclarativegesturearea_p.h" + +#include <qdeclarativeexpression.h> +#include <qdeclarativecontext.h> +#include <qdeclarativeinfo.h> + +#include <private/qdeclarativeproperty_p.h> +#include <private/qdeclarativeitem_p.h> +#include <private/qdeclarativebinding_p.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qstringlist.h> + +#include <QtGui/qevent.h> +#include <QtGui/qgesture.h> + +#include <private/qobject_p.h> + +#include "qdeclarativegesturehandler_p.h" +#include "gestureareaplugin_p.h" + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +class QDeclarativeGestureAreaPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeGestureArea) +public: + QDeclarativeGestureAreaPrivate() : defaultHandler(0) { } + + typedef QMap<Qt::GestureType, QObject *> Handlers; + Handlers handlers; + QObject *defaultHandler; + + static void handlers_append(QDeclarativeListProperty<QObject> *prop, QObject *handler) { + QDeclarativeGestureAreaPrivate *d = static_cast<QDeclarativeGestureAreaPrivate *>(prop->data); + QDeclarativeGestureArea *q = d->q_func(); + handler->setParent(q); + int type = handler->property("gestureType").toInt(); + // check that all needed properties exist + if (!handler->property("gestureType").isValid() || !handler->property("gesture").isValid() + || !handler->property("onStarted").isValid() || !handler->property("onUpdated").isValid() + || !handler->property("onCanceled").isValid() || !handler->property("onFinished").isValid()) { + qmlInfo(handler) << "Invalid GestureArea handler.\n" + << "A GestureArea handler should provide the following properties: " + "'gestureType', 'onStarted', 'onUpdated', 'onCanceled', 'onFinished' and 'gesture'"; + return; + } + Qt::GestureType gestureType = Qt::GestureType(type); + if (d->handlers.contains(gestureType)) { + qmlInfo(handler) << "Duplicate gesture found, ignoring."; + return; + } + d->handlers.insert(gestureType, handler); + if (GestureAreaQmlPlugin::self) + GestureAreaQmlPlugin::self->allGestures << gestureType; + if (type == 0 && GestureAreaQmlPlugin::self) { + d->defaultHandler = handler; + GestureAreaQmlPlugin::self->allDefaultAreas << QWeakPointer<QDeclarativeGestureArea>(q); + foreach (Qt::GestureType gestureType, GestureAreaQmlPlugin::self->allGestures) { + foreach (QWeakPointer<QDeclarativeGestureArea> area, GestureAreaQmlPlugin::self->allDefaultAreas) { + if (area) + area.data()->grabGesture(gestureType); + } + } + } else { + q->grabGesture(gestureType); + } + } + static void handlers_clear(QDeclarativeListProperty<QObject> *prop) { + QDeclarativeGestureAreaPrivate *d = static_cast<QDeclarativeGestureAreaPrivate *>(prop->data); + d->handlers.clear(); + } + static int handlers_count(QDeclarativeListProperty<QObject> *prop) { + QDeclarativeGestureAreaPrivate *d = static_cast<QDeclarativeGestureAreaPrivate *>(prop->data); + return d->handlers.count(); + } + static QObject *handlers_at(QDeclarativeListProperty<QObject> *prop, int index) { + QDeclarativeGestureAreaPrivate *d = static_cast<QDeclarativeGestureAreaPrivate *>(prop->data); + return d->handlers.value(d->handlers.keys().at(index)); + } + + void evaluate(QGestureEvent *event, QGesture *gesture, QObject *handler); + bool gestureEvent(QGestureEvent *event); +}; + +/*! + \qmlclass GestureArea QDeclarativeGestureArea + \ingroup qml-basic-interaction-elements + + \brief The GestureArea item enables simple gesture handling. + \inherits Item + + A GestureArea is like a MouseArea, but it has signals for gesture events. + + \e {Elements in the Qt.labs module are not guaranteed to remain compatible + in future versions.} + + \qml + import Qt.labs.gestures 0.1 + + GestureArea { + anchors.fill: parent + + Pan { + when: Math.abs(gesture.delta.y) < Math.abs(gesture.delta.x) + onUpdated: { + // use gesture.delta + } + } + TapAndHold { + onFinished: { + // do something + } + } + } + \endqml + + Each gesture object has a \e when clause that will be evaluated on start of the + gesture. If the when clause evaluates to true the gesture is accepted and the + \e onStarted script is handled. If there is no \e when clause then it will + automatically be accepted. Notice that a gesture that is not accepted will be + offered to another gesture area that is also covering the area where the gesture + happened. + + After acceptance of a gesture updates in movement call \e unOpdated and finally either + the script from \e onCanceled or \e onFinished is handled when the gesture is canceled + or finished. + Each gesture object has a \e gesture parameter that has the properties of the gesture + which can be used in all of the script properties. + + The full list of supported gesture-types handled by a GestureArea is listed in the table + below. + \table + \header \o Type \o Description + \row \o Pan \o a movement of pressing and holding while moving the pointing device on the gesture area + \row \o Tap \o a touch and release without moving + \row \o TapAndHold \o a touch without releasing or moving for a longer time + \row \o Pinch \o is used to handle the moving using more than one finger + \endtable + + GestureArea is an invisible item: it is never painted. + + \sa MouseArea, {declarative/touchinteraction/gestures}{Gestures example} +*/ + +/*! + \internal + \class QDeclarativeGestureArea + \brief The QDeclarativeGestureArea class provides simple gesture handling. + +*/ +QDeclarativeGestureArea::QDeclarativeGestureArea(QDeclarativeItem *parent) : + QDeclarativeItem(*(new QDeclarativeGestureAreaPrivate), parent) +{ + setAcceptedMouseButtons(Qt::LeftButton); + setAcceptTouchEvents(true); +} + +QDeclarativeGestureArea::~QDeclarativeGestureArea() +{ +} + +QDeclarativeListProperty<QObject> QDeclarativeGestureArea::handlers() +{ + Q_D(QDeclarativeGestureArea); + return QDeclarativeListProperty<QObject>(this, d, QDeclarativeGestureAreaPrivate::handlers_append, + QDeclarativeGestureAreaPrivate::handlers_count, QDeclarativeGestureAreaPrivate::handlers_at, + QDeclarativeGestureAreaPrivate::handlers_clear); +} + +bool QDeclarativeGestureArea::sceneEvent(QEvent *event) +{ + Q_D(QDeclarativeGestureArea); + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::MouseButtonPress: + case QEvent::TouchBegin: + event->accept(); + return true; + case QEvent::Gesture: + return d->gestureEvent(static_cast<QGestureEvent *>(event)); + default: + break; + } + return QDeclarativeItem::sceneEvent(event); +} + +void QDeclarativeGestureAreaPrivate::evaluate(QGestureEvent *event, QGesture *gesture, QObject *handler) +{ + handler->setProperty("gesture", QVariant::fromValue<QObject *>(gesture)); + QDeclarativeScriptString when = handler->property("when").value<QDeclarativeScriptString>(); + if (!when.script().isEmpty()) { + QDeclarativeExpression expr(when.context(), when.scopeObject(), when.script()); + QVariant result = expr.evaluate(); + if (expr.hasError()) { + qmlInfo(q_func()) << expr.error(); + return; + } + if (!result.toBool()) + return; + } + + // those names map to the enum Qt::GestureState + static const char * scriptPropertyNames[] = { "0", "onStarted", "onUpdated", "onFinished", "onCanceled", "__ERROR" }; + QDeclarativeScriptString script = handler->property(scriptPropertyNames[gesture->state()]).value<QDeclarativeScriptString>(); + if (!script.script().isEmpty()) { + QDeclarativeExpression expr(script.context(), script.scopeObject(), script.script()); + expr.evaluate(); + if (expr.hasError()) + qmlInfo(q_func()) << expr.error(); + } + event->accept(gesture); + handler->setProperty("gesture", QVariant()); +} + +bool QDeclarativeGestureAreaPrivate::gestureEvent(QGestureEvent *event) +{ + event->accept(); + foreach(Qt::GestureType type, handlers.keys()) + event->ignore(type); + + if (handlers.isEmpty()) + return false; + + QSet<Qt::GestureType> handledGestures; + Handlers::Iterator it = handlers.end(); + do { + --it; + Qt::GestureType gestureType = it.key(); + if (!gestureType) + continue; + if (QGesture *gesture = event->gesture(gestureType)) { + handledGestures << gestureType; + QObject *handler = it.value(); + evaluate(event, gesture, handler); + } + } while (it != handlers.begin()); + + if (defaultHandler) { + // filter all gestures through the default handler + foreach (QGesture *gesture, event->gestures()) { + if (!handledGestures.contains(gesture->gestureType())) + evaluate(event, gesture, defaultHandler); + } + } + return false; +} + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES diff --git a/qdeclarativegesturearea_p.h b/qdeclarativegesturearea_p.h new file mode 100644 index 0000000..d91d695 --- /dev/null +++ b/qdeclarativegesturearea_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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 QML Gesture Area plugin 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 QDECLARATIVEGESTUREAREA_H +#define QDECLARATIVEGESTUREAREA_H + +#include <qdeclarativeitem.h> +#include <qdeclarativescriptstring.h> +#include <private/qdeclarativecustomparser_p.h> + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtGui/qgesture.h> + +#include "qdeclarativegesturehandler_p.h" + +#ifndef QT_NO_GESTURES + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeBoundSignal; +class QDeclarativeContext; +class QDeclarativeGestureAreaPrivate; +class QDeclarativeGestureArea : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeListProperty<QObject> handlers READ handlers) + Q_CLASSINFO("DefaultProperty", "handlers") + +public: + QDeclarativeGestureArea(QDeclarativeItem *parent=0); + ~QDeclarativeGestureArea(); + + QDeclarativeListProperty<QObject> handlers(); + +protected: + bool sceneEvent(QEvent *event); + +private: + Q_DISABLE_COPY(QDeclarativeGestureArea) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeGestureArea) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGestureArea) + +QT_END_HEADER + +#endif // QT_NO_GESTURES + +#endif diff --git a/qdeclarativegesturehandler.cpp b/qdeclarativegesturehandler.cpp new file mode 100644 index 0000000..05e13d7 --- /dev/null +++ b/qdeclarativegesturehandler.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** 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 QML Gesture Area plugin 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 "qdeclarativegesturehandler_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qstringlist.h> + +#include <QtGui/qevent.h> + +#include <private/qobject_p.h> +#include <private/qdeclarativebinding_p.h> + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +class QDeclarativeGestureHandlerPrivate : public QObjectPrivate +{ +public: + QDeclarativeGestureHandlerPrivate() + : gestureType((Qt::GestureType)0), gesture(0) { } + + int gestureType; + QDeclarativeScriptString when; + QDeclarativeScriptString onStarted; + QDeclarativeScriptString onUpdated; + QDeclarativeScriptString onCanceled; + QDeclarativeScriptString onFinished; + QObject *gesture; +}; + +QDeclarativeGestureHandler::QDeclarativeGestureHandler(QObject *parent) + : QObject(*new QDeclarativeGestureHandlerPrivate, parent) +{ +} + +QDeclarativeGestureHandler::QDeclarativeGestureHandler(QDeclarativeGestureHandlerPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +bool QDeclarativeGestureHandler::isWhenKnown() const +{ + Q_D(const QDeclarativeGestureHandler); + return !d->when.script().isEmpty(); +} + +QDeclarativeScriptString QDeclarativeGestureHandler::when() const +{ + Q_D(const QDeclarativeGestureHandler); + return d->when; +} + +void QDeclarativeGestureHandler::setWhen(const QDeclarativeScriptString &when) +{ + Q_D(QDeclarativeGestureHandler); + d->when = when; +} + +QDeclarativeScriptString QDeclarativeGestureHandler::onStarted() const +{ + Q_D(const QDeclarativeGestureHandler); + return d->onStarted; +} + +void QDeclarativeGestureHandler::setOnStarted(const QDeclarativeScriptString &script) +{ + Q_D(QDeclarativeGestureHandler); + d->onStarted = script; +} + +QDeclarativeScriptString QDeclarativeGestureHandler::onUpdated() const +{ + Q_D(const QDeclarativeGestureHandler); + return d->onUpdated; +} + +void QDeclarativeGestureHandler::setOnUpdated(const QDeclarativeScriptString &script) +{ + Q_D(QDeclarativeGestureHandler); + d->onUpdated = script; +} + +QDeclarativeScriptString QDeclarativeGestureHandler::onFinished() const +{ + Q_D(const QDeclarativeGestureHandler); + return d->onFinished; +} + +void QDeclarativeGestureHandler::setOnFinished(const QDeclarativeScriptString &script) +{ + Q_D(QDeclarativeGestureHandler); + d->onFinished = script; +} + +QDeclarativeScriptString QDeclarativeGestureHandler::onCanceled() const +{ + Q_D(const QDeclarativeGestureHandler); + return d->onCanceled; +} + +void QDeclarativeGestureHandler::setOnCanceled(const QDeclarativeScriptString &script) +{ + Q_D(QDeclarativeGestureHandler); + d->onCanceled = script; +} + +QObject *QDeclarativeGestureHandler::gesture() const +{ + return d_func()->gesture; +} + +void QDeclarativeGestureHandler::setGesture(QObject *gesture) +{ + d_func()->gesture = gesture; +} + +int QDeclarativeGestureHandler::gestureType() const +{ + return d_func()->gestureType; +} + +void QDeclarativeGestureHandler::setGestureType(int gestureType) +{ + d_func()->gestureType = gestureType; +} + + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES diff --git a/qdeclarativegesturehandler_p.h b/qdeclarativegesturehandler_p.h new file mode 100644 index 0000000..0e9aa38 --- /dev/null +++ b/qdeclarativegesturehandler_p.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** 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 QML Gesture Area plugin 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 QDECLARATIVEGESTUREHANDLER_H +#define QDECLARATIVEGESTUREHANDLER_H + +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativescriptstring.h> +#include <QtCore/qobject.h> +#include <QtCore/qstringlist.h> +#include <QtGui/qgesture.h> + +#ifndef QT_NO_GESTURES + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QGesture; +class QDeclarativeBinding; +class QDeclarativeGestureHandlerPrivate; +class QDeclarativeGestureHandler : public QObject +{ + Q_OBJECT + Q_PROPERTY(int gestureType READ gestureType WRITE setGestureType) + Q_PROPERTY(QObject *parent READ parent DESIGNABLE false FINAL) + Q_PROPERTY(QDeclarativeScriptString when READ when WRITE setWhen) + Q_PROPERTY(QDeclarativeScriptString onStarted READ onStarted WRITE setOnStarted) + Q_PROPERTY(QDeclarativeScriptString onUpdated READ onUpdated WRITE setOnUpdated) + Q_PROPERTY(QDeclarativeScriptString onCanceled READ onCanceled WRITE setOnCanceled) + Q_PROPERTY(QDeclarativeScriptString onFinished READ onFinished WRITE setOnFinished) + Q_PROPERTY(QObject *gesture READ gesture WRITE setGesture NOTIFY gestureChanged) + +public: + bool isWhenKnown() const; + QDeclarativeScriptString when() const; + void setWhen(const QDeclarativeScriptString &script); + + QDeclarativeScriptString onStarted() const; + void setOnStarted(const QDeclarativeScriptString &script); + + QDeclarativeScriptString onUpdated() const; + void setOnUpdated(const QDeclarativeScriptString &script); + + QDeclarativeScriptString onCanceled() const; + void setOnCanceled(const QDeclarativeScriptString &script); + + QDeclarativeScriptString onFinished() const; + void setOnFinished(const QDeclarativeScriptString &script); + + QObject *gesture() const; + void setGesture(QObject *gesture); + + int gestureType() const; + +Q_SIGNALS: + void gestureChanged(); + +protected: + QDeclarativeGestureHandler(QObject *parent = 0); + QDeclarativeGestureHandler(QDeclarativeGestureHandlerPrivate &dd, QObject *parent = 0); + + void setGestureType(int type); + +private: + friend class QDeclarativeGestureArea; + Q_DISABLE_COPY(QDeclarativeGestureHandler) + Q_DECLARE_PRIVATE(QDeclarativeGestureHandler) +}; + +class QDeclarativePanGestureHandler : public QDeclarativeGestureHandler +{ + Q_OBJECT +public: + QDeclarativePanGestureHandler(QObject *parent = 0) + : QDeclarativeGestureHandler(parent) + { + setGestureType(Qt::PanGesture); + } + +private: + Q_DISABLE_COPY(QDeclarativePanGestureHandler) +}; + +class QDeclarativeDefaultGestureHandler : public QDeclarativeGestureHandler +{ + Q_OBJECT +public: + QDeclarativeDefaultGestureHandler(QObject *parent = 0) + : QDeclarativeGestureHandler(parent) + { + } + +private: + Q_DISABLE_COPY(QDeclarativeDefaultGestureHandler) +}; + +class QDeclarativePinchGestureHandler : public QDeclarativeGestureHandler +{ + Q_OBJECT +public: + QDeclarativePinchGestureHandler(QObject *parent = 0) + : QDeclarativeGestureHandler(parent) + { + setGestureType(Qt::PinchGesture); + } + +private: + Q_DISABLE_COPY(QDeclarativePinchGestureHandler) +}; + +class QDeclarativeTapGestureHandler : public QDeclarativeGestureHandler +{ + Q_OBJECT +public: + QDeclarativeTapGestureHandler(QObject *parent = 0) + : QDeclarativeGestureHandler(parent) + { + setGestureType(Qt::TapGesture); + } + +private: + Q_DISABLE_COPY(QDeclarativeTapGestureHandler) +}; + +class QDeclarativeTapAndHoldGestureHandler : public QDeclarativeGestureHandler +{ + Q_OBJECT +public: + QDeclarativeTapAndHoldGestureHandler(QObject *parent = 0) + : QDeclarativeGestureHandler(parent) + { + setGestureType(Qt::TapAndHoldGesture); + } + +private: + Q_DISABLE_COPY(QDeclarativeTapAndHoldGestureHandler) +}; + +class QDeclarativeSwipeGestureHandler : public QDeclarativeGestureHandler +{ + Q_OBJECT +public: + QDeclarativeSwipeGestureHandler(QObject *parent = 0) + : QDeclarativeGestureHandler(parent) + { + setGestureType(Qt::SwipeGesture); + } + +private: + Q_DISABLE_COPY(QDeclarativeSwipeGestureHandler) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGestureHandler) +QML_DECLARE_TYPE(QDeclarativeDefaultGestureHandler) +QML_DECLARE_TYPE(QDeclarativePanGestureHandler) +QML_DECLARE_TYPE(QDeclarativePinchGestureHandler) +QML_DECLARE_TYPE(QDeclarativeTapGestureHandler) +QML_DECLARE_TYPE(QDeclarativeTapAndHoldGestureHandler) +QML_DECLARE_TYPE(QDeclarativeSwipeGestureHandler) + +QT_END_HEADER + +#endif // QT_NO_GESTURES + +#endif diff --git a/qdeclarativegesturerecognizers.cpp b/qdeclarativegesturerecognizers.cpp new file mode 100644 index 0000000..a7d260a --- /dev/null +++ b/qdeclarativegesturerecognizers.cpp @@ -0,0 +1,874 @@ +/**************************************************************************** +** +** 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 QML Gesture Area plugin 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 "qdeclarativegesturerecognizers_p.h" +#include "qgesture.h" +#include "qevent.h" +#include "qwidget.h" +#include "qabstractscrollarea.h" +#include <qgraphicssceneevent.h> +#include "qdebug.h" + +#include "qmath.h" + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +static const int PanGestureMovementThreshold = 30; + +QPanGestureRecognizer::QPanGestureRecognizer() +{ +} + +QGesture *QPanGestureRecognizer::create(QObject *target) +{ + if (target && target->isWidgetType()) { +#if defined(Q_OS_WIN) && !defined(QT_NO_NATIVE_GESTURES) + // for scroll areas on Windows we want to use native gestures instead + if (!qobject_cast<QAbstractScrollArea *>(target->parent())) + static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents); +#else + static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents); +#endif + } else if (target) { + return 0; + } + return new QPanGesture; +} + +QGestureRecognizer::Result QPanGestureRecognizer::recognize(QGesture *state, + QObject *, + QEvent *event) +{ + QPanGesture *q = static_cast<QPanGesture *>(state); + QPanGesturePrivate *d = q->d_func(); + + QGestureRecognizer::Result result; + switch (event->type()) { + case QEvent::TouchBegin: { + const QTouchEvent *ev = static_cast<const QTouchEvent *>(event); + d->gotTouched = true; + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + d->lastOffset = d->offset = QPointF(); + d->pressTime.start(); + result = QGestureRecognizer::MayBeGesture; + break; + } + case QEvent::TouchEnd: { + const QTouchEvent *ev = static_cast<const QTouchEvent *>(event); + if (q->state() != Qt::NoGesture) { + if (ev->deviceType() == QTouchEvent::TouchScreen && ev->touchPoints().size() == 1) { + QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0); + d->lastOffset = d->offset; + d->offset = QPointF(p1.pos().x() - p1.startPos().x(), p1.pos().y() - p1.startPos().y()); + } else if (ev->deviceType() == QTouchEvent::TouchPad && ev->touchPoints().size() == 2) { + QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0); + QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1); + d->lastOffset = d->offset; + d->offset = + QPointF(p1.pos().x() - p1.startPos().x() + p2.pos().x() - p2.startPos().x(), + p1.pos().y() - p1.startPos().y() + p2.pos().y() - p2.startPos().y()) / 2; + } + result = QGestureRecognizer::FinishGesture; + } else { + result = QGestureRecognizer::CancelGesture; + } + break; + } + case QEvent::TouchUpdate: { + bool deviceAndTouchPointsOK = false; + const QTouchEvent *ev = static_cast<const QTouchEvent *>(event); + QTouchEvent::TouchPoint p1; + if (ev->deviceType() == QTouchEvent::TouchScreen && ev->touchPoints().size() == 1) { + deviceAndTouchPointsOK = true; + p1 = ev->touchPoints().at(0); + d->lastOffset = d->offset; + d->offset = QPointF(p1.pos().x() - p1.startPos().x(), p1.pos().y() - p1.startPos().y()); + } else if (ev->deviceType() == QTouchEvent::TouchPad && ev->touchPoints().size() >= 2) { + deviceAndTouchPointsOK = true; + p1 = ev->touchPoints().at(0); + QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1); + d->lastOffset = d->offset; + d->offset = + QPointF(p1.pos().x() - p1.startPos().x() + p2.pos().x() - p2.startPos().x(), + p1.pos().y() - p1.startPos().y() + p2.pos().y() - p2.startPos().y()) / 2; + } + if (deviceAndTouchPointsOK && + (d->offset.x() > 10 || d->offset.y() > 10 || + d->offset.x() < -10 || d->offset.y() < -10)) { + q->setHotSpot(p1.startScreenPos()); + result = QGestureRecognizer::TriggerGesture; + } else { + result = QGestureRecognizer::MayBeGesture; + } + break; + } + + case QEvent::MouseButtonPress: + if (!d->gotTouched) { + QMouseEvent *ev = static_cast<QMouseEvent *>(event); + if (ev->buttons() == Qt::LeftButton && !(ev->modifiers() & Qt::ControlModifier)) { + d->startPosition = d->lastPos = ev->globalPos(); + state->setHotSpot(d->startPosition); + state->setHandled(false); + d->pressTime.start(); + d->lastPosTime.start(); + ev->accept(); + } + } + return QGestureRecognizer::MayBeGesture; + + case QEvent::MouseMove: + if (!d->gotTouched) { + QMouseEvent *ev = static_cast<QMouseEvent *>(event); + if (!d->startPosition.isNull()) { + QPoint delta = ev->globalPos() - d->startPosition; + if (state->state() != Qt::GestureStarted || state->state() != Qt::GestureUpdated) { + if (delta.manhattanLength() > PanGestureMovementThreshold || d->pressTime.elapsed() > 200) { + q->setLastOffset(q->offset()); + q->setOffset(delta); + + qreal elapsed = qreal(d->lastPosTime.restart()) / 1000.; + if (elapsed <= 0) + elapsed = 1; + int dx = ev->globalPos().x() - d->lastPos.x(); + d->xVelocity += dx / elapsed; + d->xVelocity /= 2; + int dy = ev->globalPos().y() - d->lastPos.y(); + d->yVelocity += dy / elapsed; + d->yVelocity /= 2; + + d->lastPos = ev->globalPos(); + return QGestureRecognizer::TriggerGesture; + } + } + } + } + return QGestureRecognizer::Ignore; + + case QEvent::MouseButtonRelease: + if (!d->gotTouched) { + if(q->state() != Qt::NoGesture) { + QMouseEvent *ev = static_cast<QMouseEvent *>(event); + if (d->lastPosTime.elapsed() > 100) { + // if we drag then pause before release we should not cause a flick. + d->xVelocity = d->yVelocity = 0; + } else { + // FlickThreshold determines how far the "mouse" must have moved + // before we perform a flick. + static const int FlickThreshold = 20; + + // Really slow flicks can be annoying. + static const int minimumFlickVelocity = 200; + + if (qAbs(d->xVelocity) > 10 && qAbs(ev->globalPos().x() - d->startPosition.x()) > FlickThreshold) { + if (qAbs(d->xVelocity) < minimumFlickVelocity) + d->xVelocity = d->xVelocity < 0 ? -minimumFlickVelocity : minimumFlickVelocity; + } + if (qAbs(d->yVelocity) > 10 && qAbs(ev->globalPos().y() - d->startPosition.y()) > FlickThreshold) { + if (qAbs(d->yVelocity) < minimumFlickVelocity) + d->yVelocity = d->yVelocity < 0 ? -minimumFlickVelocity : minimumFlickVelocity; + } + } + if (!d->startPosition.isNull() && !q->lastOffset().isNull()) { + return QGestureRecognizer::FinishGesture; + } + } + } + return QGestureRecognizer::CancelGesture; + + default: + result = QGestureRecognizer::Ignore; + break; + } + return result; +} + +void QPanGestureRecognizer::reset(QGesture *state) +{ + if (!state) + return; + QPanGesture *pan = static_cast<QPanGesture*>(state); + QPanGesturePrivate *d = pan->d_func(); + + d->startPosition = QPoint(); + d->lastOffset = d->offset = QPointF(); + d->acceleration = 0; + d->gotTouched = false; + d->pressTime.invalidate(); + d->lastPosTime.invalidate(); + d->lastPos = QPoint(); + d->xVelocity = d->yVelocity = 0; + + QGestureRecognizer::reset(state); +} + + +// +// QPinchGestureRecognizer +// + +QPinchGestureRecognizer::QPinchGestureRecognizer() +{ +} + +QGesture *QPinchGestureRecognizer::create(QObject *target) +{ + if (target) { + if (target->isWidgetType()) + static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents); + else + return 0; + } + return new QPinchGesture; +} + +QGestureRecognizer::Result QPinchGestureRecognizer::recognize(QGesture *state, + QObject *obj, + QEvent *event) +{ + QPinchGesture *q = static_cast<QPinchGesture *>(state); + QPinchGesturePrivate *d = q->d_func(); + + QGestureRecognizer::Result result; + + switch (event->type()) { + case QEvent::TouchBegin: { + d->gotTouched = true; + result = QGestureRecognizer::MayBeGesture; + break; + } + case QEvent::TouchEnd: { + if (q->state() != Qt::NoGesture) { + result = QGestureRecognizer::FinishGesture; + } else { + result = QGestureRecognizer::CancelGesture; + } + break; + } + case QEvent::TouchUpdate: { + const QTouchEvent *ev = static_cast<const QTouchEvent *>(event); + d->changeFlags = 0; + if (ev->touchPoints().size() == 2) { + QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0); + QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1); + + d->hotSpot = p1.screenPos(); + d->isHotSpotSet = true; + + if (d->isNewSequence) { + d->startPosition[0] = p1.screenPos(); + d->startPosition[1] = p2.screenPos(); + } + QLineF line(p1.screenPos(), p2.screenPos()); + QLineF lastLine(p1.lastScreenPos(), p2.lastScreenPos()); + QLineF tmp(line); + tmp.setLength(line.length() / 2.); + QPointF centerPoint = tmp.p2(); + + d->lastCenterPoint = d->centerPoint; + d->centerPoint = centerPoint; + d->changeFlags |= QPinchGesture::CenterPointChanged; + + const qreal scaleFactor = line.length() / lastLine.length(); + + if (d->isNewSequence) { + d->lastScaleFactor = scaleFactor; + d->lastCenterPoint = centerPoint; + } else { + d->lastScaleFactor = d->scaleFactor; + } + d->scaleFactor = scaleFactor; + d->totalScaleFactor = d->totalScaleFactor * scaleFactor; + d->changeFlags |= QPinchGesture::ScaleFactorChanged; + + qreal angle = QLineF(p1.screenPos(), p2.screenPos()).angle(); + if (angle > 180) + angle -= 360; + qreal startAngle = QLineF(p1.startScreenPos(), p2.startScreenPos()).angle(); + if (startAngle > 180) + startAngle -= 360; + const qreal rotationAngle = startAngle - angle; + if (d->isNewSequence) + d->lastRotationAngle = rotationAngle; + else + d->lastRotationAngle = d->rotationAngle; + d->rotationAngle = rotationAngle; + d->totalRotationAngle += d->rotationAngle - d->lastRotationAngle; + d->changeFlags |= QPinchGesture::RotationAngleChanged; + + d->totalChangeFlags |= d->changeFlags; + if(p1.state() == Qt::TouchPointReleased || p2.state() == Qt::TouchPointReleased) + d->isNewSequence = true; + else + d->isNewSequence = false; + result = QGestureRecognizer::TriggerGesture; + } else { + d->isNewSequence = true; + if (q->state() != Qt::NoGesture) + result = QGestureRecognizer::Ignore; + else + result = QGestureRecognizer::FinishGesture; + } + break; + } + + case QEvent::Wheel: { + QWheelEvent *ev = static_cast<QWheelEvent *>(event); + const Qt::KeyboardModifiers ctrlShiftMask = Qt::ControlModifier | Qt::ShiftModifier; + if (ev->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier)) { + q->setHotSpot(ev->globalPos()); + + bool isNewSequence = q->state() == Qt::NoGesture; + + d->lastCenterPoint = d->centerPoint; + d->centerPoint = ev->globalPos(); + + d->changeFlags = 0; + + if (ev->modifiers() & Qt::ControlModifier) { + d->lastScaleFactor = d->scaleFactor; + if (ev->delta() > 0) + d->scaleFactor = 1.1 * qAbs(ev->delta()) / 8. / 15.; + else + d->scaleFactor = 0.9 / qAbs(ev->delta() / 8. / 15.); + d->changeFlags |= QPinchGesture::ScaleFactorChanged; + } else if (ev->modifiers() & Qt::ShiftModifier) { + d->lastRotationAngle = d->rotationAngle; + d->rotationAngle += ev->delta() / 8. / 15.; + d->changeFlags |= QPinchGesture::RotationAngleChanged; + } + + if (isNewSequence) { + d->lastCenterPoint = d->centerPoint; + d->lastScaleFactor = d->scaleFactor; + d->lastRotationAngle = d->rotationAngle; + } + + int timerId = q->property("timerid").toInt(); + if (timerId) + q->killTimer(timerId); + q->setProperty("timerid", q->startTimer(400)); + return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; + } + return QGestureRecognizer::Ignore; + } + case QEvent::MouseButtonPress: + if (q->property("timerid").toInt()) { + return QGestureRecognizer::FinishGesture; + } + return QGestureRecognizer::Ignore; + + case QEvent::Timer: { + int timerId = state->property("timerid").toInt(); + if (obj == state && timerId) { + state->killTimer(timerId); + d->changeFlags = 0; + return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; + } + } + + default: + result = QGestureRecognizer::Ignore; + break; + } + return result; +} + +void QPinchGestureRecognizer::reset(QGesture *state) +{ + if (!state) + return; + QPinchGesture *pinch = static_cast<QPinchGesture *>(state); + QPinchGesturePrivate *d = pinch->d_func(); + + d->totalChangeFlags = d->changeFlags = 0; + + d->startCenterPoint = d->lastCenterPoint = d->centerPoint = QPointF(); + d->totalScaleFactor = d->lastScaleFactor = d->scaleFactor = 1; + d->totalRotationAngle = d->lastRotationAngle = d->rotationAngle = 0; + + d->isNewSequence = true; + d->startPosition[0] = d->startPosition[1] = QPointF(); + + d->gotTouched = false; + + int timerId = state->property("timerid").toInt(); + if (timerId) { + state->killTimer(timerId); + state->setProperty("timerid", QVariant()); + } + QGestureRecognizer::reset(state); +} + +// +// QSwipeGestureRecognizer +// + +QSwipeGestureRecognizer::QSwipeGestureRecognizer() +{ +} + +QGesture *QSwipeGestureRecognizer::create(QObject *target) +{ + if (target) { + if (target->isWidgetType()) + static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents); + else + return 0; + } + return new QSwipeGesture; +} + +QGestureRecognizer::Result QSwipeGestureRecognizer::recognize(QGesture *state, + QObject *, + QEvent *event) +{ + QSwipeGesture *q = static_cast<QSwipeGesture *>(state); + QSwipeGesturePrivate *d = q->d_func(); + + QGestureRecognizer::Result result; + + switch (event->type()) { + case QEvent::TouchBegin: { + d->gotTouched = true; + d->velocityValue = 1; + d->time.start(); + d->started = true; + result = QGestureRecognizer::MayBeGesture; + break; + } + case QEvent::TouchEnd: { + if (q->state() != Qt::NoGesture) { + result = QGestureRecognizer::FinishGesture; + } else { + result = QGestureRecognizer::CancelGesture; + } + break; + } + case QEvent::TouchUpdate: { + const QTouchEvent *ev = static_cast<const QTouchEvent *>(event); + if (!d->started) + result = QGestureRecognizer::CancelGesture; + else if (ev->touchPoints().size() == 2) { + QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0); + QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1); + + if (d->lastPositions[0].isNull()) { + d->lastPositions[0] = p1.startScreenPos().toPoint(); + d->lastPositions[1] = p2.startScreenPos().toPoint(); + } + d->hotSpot = p1.screenPos(); + d->isHotSpotSet = true; + + int xDistance = (p1.screenPos().x() - d->lastPositions[0].x() + + p2.screenPos().x() - d->lastPositions[1].x()) / 2; + int yDistance = (p1.screenPos().y() - d->lastPositions[0].y() + + p2.screenPos().y() - d->lastPositions[1].y()) / 2; + int absXDistance = qAbs(xDistance); + int absYDistance = qAbs(yDistance); + + const int distance = absXDistance >= absYDistance ? absXDistance : absYDistance; + int elapsedTime = d->time.restart(); + if (!elapsedTime) + elapsedTime = 1; + d->velocityValue = 0.9 * d->velocityValue + distance / elapsedTime; + d->swipeAngle = QLineF(p1.startScreenPos(), p1.screenPos()).angle(); + + static const int MoveThreshold = 50; + if (absXDistance > MoveThreshold || absYDistance > MoveThreshold) { + // measure the distance to check if the direction changed + d->lastPositions[0] = p1.screenPos().toPoint(); + d->lastPositions[1] = p2.screenPos().toPoint(); + QSwipeGesture::SwipeDirection horizontal = + xDistance > 0 ? QSwipeGesture::Right : QSwipeGesture::Left; + QSwipeGesture::SwipeDirection vertical = + yDistance > 0 ? QSwipeGesture::Down : QSwipeGesture::Up; + if (d->verticalDirection == QSwipeGesture::NoDirection) + d->verticalDirection = vertical; + if (d->horizontalDirection == QSwipeGesture::NoDirection) + d->horizontalDirection = horizontal; + if (d->verticalDirection != vertical || d->horizontalDirection != horizontal) { + // the user has changed the direction! + result = QGestureRecognizer::CancelGesture; + } + result = QGestureRecognizer::TriggerGesture; + } else { + if (q->state() != Qt::NoGesture) + result = QGestureRecognizer::TriggerGesture; + else + result = QGestureRecognizer::MayBeGesture; + } + } else if (ev->touchPoints().size() > 2) { + result = QGestureRecognizer::CancelGesture; + } else { // less than 2 touch points + if (d->started && (ev->touchPointStates() & Qt::TouchPointPressed)) + result = QGestureRecognizer::CancelGesture; + else if (d->started) + result = QGestureRecognizer::Ignore; + else + result = QGestureRecognizer::MayBeGesture; + } + break; + } + + case QEvent::MouseButtonPress: { + if(!d->gotTouched) { + const QMouseEvent *ev = static_cast<const QMouseEvent *>(event); + d->velocityValue = 1; + d->time.start(); + d->started = true; + state->setHotSpot(ev->globalPos()); + return QGestureRecognizer::MayBeGesture; + } + return QGestureRecognizer::Ignore; + } + case QEvent::MouseButtonRelease: { + if(!d->gotTouched) { + if (q->state() != Qt::NoGesture) { + result = QGestureRecognizer::FinishGesture; + } else { + result = QGestureRecognizer::CancelGesture; + } + } else + result = QGestureRecognizer::Ignore; + break; + } + + case QEvent::MouseMove: { + if(!d->gotTouched) { + const QMouseEvent *ev = static_cast<const QMouseEvent *>(event); + if (!d->started) + result = QGestureRecognizer::CancelGesture; + else { + if (d->lastPositions[0].isNull()) { + d->lastPositions[0] = ev->pos(); + } + + int xDistance = (ev->x() - d->lastPositions[0].x()); + int yDistance = (ev->y() - d->lastPositions[0].y()); + int absXDistance = qAbs(xDistance); + int absYDistance = qAbs(yDistance); + + const int distance = absXDistance >= absYDistance ? absXDistance : absYDistance; + int elapsedTime = d->time.restart(); + if (!elapsedTime) + elapsedTime = 1; + d->velocityValue = 0.9 * d->velocityValue + distance / elapsedTime; + d->swipeAngle = QLineF(d->lastPositions[0], ev->pos()).angle(); + + static const int MoveThreshold = 50; + if (absXDistance > MoveThreshold || absYDistance > MoveThreshold) { + // measure the distance to check if the direction changed + d->lastPositions[0] = ev->pos(); + QSwipeGesture::SwipeDirection horizontal = + xDistance > 0 ? QSwipeGesture::Right : QSwipeGesture::Left; + QSwipeGesture::SwipeDirection vertical = + yDistance > 0 ? QSwipeGesture::Down : QSwipeGesture::Up; + if (d->verticalDirection == QSwipeGesture::NoDirection) + d->verticalDirection = vertical; + if (d->horizontalDirection == QSwipeGesture::NoDirection) + d->horizontalDirection = horizontal; + if (d->verticalDirection != vertical || d->horizontalDirection != horizontal) { + // the user has changed the direction! + result = QGestureRecognizer::CancelGesture; + } + result = QGestureRecognizer::TriggerGesture; + } else { + if (q->state() != Qt::NoGesture) + result = QGestureRecognizer::TriggerGesture; + else + result = QGestureRecognizer::MayBeGesture; + } + } + } else + result = QGestureRecognizer::Ignore; + break; + } + default: + result = QGestureRecognizer::Ignore; + break; + } + return result; +} + +void QSwipeGestureRecognizer::reset(QGesture *state) +{ + if (!state) + return; + QSwipeGesture *q = static_cast<QSwipeGesture *>(state); + QSwipeGesturePrivate *d = q->d_func(); + + d->verticalDirection = d->horizontalDirection = QSwipeGesture::NoDirection; + d->swipeAngle = 0; + d->gotTouched = false; + + d->lastPositions[0] = d->lastPositions[1] = d->lastPositions[2] = QPoint(); + d->started = false; + d->velocityValue = 0; + d->time.invalidate(); + + QGestureRecognizer::reset(state); +} + +// +// QTapGestureRecognizer +// + +QTapGestureRecognizer::QTapGestureRecognizer() +{ +} + +QGesture *QTapGestureRecognizer::create(QObject *target) +{ + if (target) { + if (target->isWidgetType()) + static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents); + else + return 0; + } + return new QTapGesture; +} + +QGestureRecognizer::Result QTapGestureRecognizer::recognize(QGesture *state, + QObject *, + QEvent *event) +{ + QTapGesture *q = static_cast<QTapGesture *>(state); + QTapGesturePrivate *d = q->d_func(); + + QGestureRecognizer::Result result = QGestureRecognizer::CancelGesture; + + switch (event->type()) { + case QEvent::TouchBegin: { + d->gotTouched = true; + const QTouchEvent *ev = static_cast<const QTouchEvent *>(event); + d->position = ev->touchPoints().at(0).pos(); + q->setHotSpot(ev->touchPoints().at(0).screenPos()); + result = QGestureRecognizer::TriggerGesture; + break; + } + case QEvent::TouchUpdate: + case QEvent::TouchEnd: { + const QTouchEvent *ev = static_cast<const QTouchEvent *>(event); + if (q->state() != Qt::NoGesture && ev->touchPoints().size() == 1) { + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + QPoint delta = p.pos().toPoint() - p.startPos().toPoint(); + enum { TapRadius = 40 }; + if (delta.manhattanLength() <= TapRadius) { + if (event->type() == QEvent::TouchEnd) + result = QGestureRecognizer::FinishGesture; + else + result = QGestureRecognizer::TriggerGesture; + } + } + break; + } + + case QEvent::MouseButtonPress: + if (!d->gotTouched) { + QMouseEvent *ev = static_cast<QMouseEvent *>(event); + if (!(ev->modifiers() & Qt::ControlModifier)) { + state->setProperty("position", ev->globalPos()); + state->setHotSpot(ev->globalPos()); + state->setHandled(false); + event->accept(); + return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; + } + } + return QGestureRecognizer::Ignore; + + case QEvent::MouseMove: + return QGestureRecognizer::Ignore; + + case QEvent::MouseButtonRelease: + if (!d->gotTouched) { + if (state->state() == Qt::GestureStarted) { + return QGestureRecognizer::FinishGesture; + } + } + return QGestureRecognizer::Ignore; + + case QEvent::Gesture: { + // We check for other gesture events: TapAndHold and Pan both cancel the simple Tap + QGestureEvent *ge = static_cast<QGestureEvent *>(event); + if (ge->gesture(Qt::PanGesture) || ge->gesture(Qt::TapAndHoldGesture)) { + if (state->state() == Qt::GestureStarted || state->state() == Qt::GestureUpdated) + return QGestureRecognizer::CancelGesture; + } + return QGestureRecognizer::Ignore; + } + + default: + result = QGestureRecognizer::Ignore; + break; + } + return result; +} + +void QTapGestureRecognizer::reset(QGesture *state) +{ + if (!state) + return; + QTapGesture *q = static_cast<QTapGesture *>(state); + QTapGesturePrivate *d = q->d_func(); + + d->position = QPointF(); + + QGestureRecognizer::reset(state); +} + +// +// QTapAndHoldGestureRecognizer +// + +QTapAndHoldGestureRecognizer::QTapAndHoldGestureRecognizer() +{ +} + +QGesture *QTapAndHoldGestureRecognizer::create(QObject *target) +{ + if (target) { + if (target->isWidgetType()) + static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents); + else + return 0; + } + return new QTapAndHoldGesture; +} + +QGestureRecognizer::Result +QTapAndHoldGestureRecognizer::recognize(QGesture *state, QObject *object, + QEvent *event) +{ + QTapAndHoldGesture *q = static_cast<QTapAndHoldGesture *>(state); + QTapAndHoldGesturePrivate *d = q->d_func(); + + if (object == state && event->type() == QEvent::Timer) { + q->killTimer(d->timerId); + d->timerId = 0; + return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; + } + + const QTouchEvent *ev = static_cast<const QTouchEvent *>(event); +// const QMouseEvent *me = static_cast<const QMouseEvent *>(event); +#ifndef QT_NO_GRAPHICSVIEW + const QMouseEvent *gsme = static_cast<const QMouseEvent *>(event); +#endif + + enum { TapRadius = 40 }; + + switch (event->type()) { +#ifndef QT_NO_GRAPHICSVIEW + case QEvent::MouseButtonPress: + d->position = gsme->globalPos(); + q->setHotSpot(d->position); + if (d->timerId) + q->killTimer(d->timerId); + d->timerId = q->startTimer(QTapAndHoldGesturePrivate::Timeout); + return QGestureRecognizer::MayBeGesture; // we don't show a sign of life until the timeout +#endif +// case QEvent::MouseButtonPress: +// d->position = me->globalPos(); +// q->setHotSpot(d->position); +// if (d->timerId) +// q->killTimer(d->timerId); +// d->timerId = q->startTimer(QTapAndHoldGesturePrivate::Timeout); +// return QGestureRecognizer::MayBeGesture; // we don't show a sign of life until the timeout + case QEvent::TouchBegin: + d->position = ev->touchPoints().at(0).startScreenPos(); + q->setHotSpot(d->position); + if (d->timerId) + q->killTimer(d->timerId); + d->timerId = q->startTimer(QTapAndHoldGesturePrivate::Timeout); + return QGestureRecognizer::MayBeGesture; // we don't show a sign of life until the timeout +#ifndef QT_NO_GRAPHICSVIEW + case QEvent::MouseButtonRelease: +#endif +// case QEvent::MouseButtonRelease: + case QEvent::TouchEnd: + return QGestureRecognizer::CancelGesture; // get out of the MayBeGesture state + case QEvent::TouchUpdate: + if (d->timerId && ev->touchPoints().size() == 1) { + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + QPoint delta = p.pos().toPoint() - p.startPos().toPoint(); + if (delta.manhattanLength() <= TapRadius) + return QGestureRecognizer::MayBeGesture; + } + return QGestureRecognizer::CancelGesture; +// case QEvent::MouseMove: { +// QPoint delta = me->globalPos() - d->position.toPoint(); +// if (d->timerId && delta.manhattanLength() <= TapRadius) +// return QGestureRecognizer::MayBeGesture; +// return QGestureRecognizer::CancelGesture; +// } +#ifndef QT_NO_GRAPHICSVIEW + case QEvent::MouseMove: { + QPoint delta = gsme->globalPos() - d->position.toPoint(); + if (d->timerId && delta.manhattanLength() <= TapRadius) + return QGestureRecognizer::MayBeGesture; + return QGestureRecognizer::CancelGesture; + } +#endif + default: + return QGestureRecognizer::Ignore; + } +} + +void QTapAndHoldGestureRecognizer::reset(QGesture *state) +{ + if (!state) + return; + QTapAndHoldGesture *q = static_cast<QTapAndHoldGesture *>(state); + QTapAndHoldGesturePrivate *d = q->d_func(); + + d->position = QPointF(); + if (d->timerId) + q->killTimer(d->timerId); + d->timerId = 0; + + QGestureRecognizer::reset(state); +} + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES diff --git a/qdeclarativegesturerecognizers_p.h b/qdeclarativegesturerecognizers_p.h new file mode 100644 index 0000000..6ad7e4b --- /dev/null +++ b/qdeclarativegesturerecognizers_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** 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 QML Gesture Area plugin 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 QDECLARATIVEGESTURERECOGNIZERS_P_H +#define QDECLARATIVEGESTURERECOGNIZERS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgesturerecognizer.h" +//#include "private/qgesture_p.h" + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +class QPanGestureRecognizer : public QGestureRecognizer +{ +public: + QPanGestureRecognizer(); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event); + void reset(QGesture *state); +}; + +class QPinchGestureRecognizer : public QGestureRecognizer +{ +public: + QPinchGestureRecognizer(); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event); + void reset(QGesture *state); +}; + +class QSwipeGestureRecognizer : public QGestureRecognizer +{ +public: + QSwipeGestureRecognizer(); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event); + void reset(QGesture *state); +}; + +class QTapGestureRecognizer : public QGestureRecognizer +{ +public: + QTapGestureRecognizer(); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event); + void reset(QGesture *state); +}; + +class QTapAndHoldGestureRecognizer : public QGestureRecognizer +{ +public: + QTapAndHoldGestureRecognizer(); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event); + void reset(QGesture *state); +}; + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES + +#endif // QDECLARATIVEGESTURERECOGNIZERS_P_H @@ -0,0 +1 @@ +plugin qmlgestureareaplugin |