diff options
Diffstat (limited to 'src/gui')
41 files changed, 5233 insertions, 75 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 555f7b0..1c062cc 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -574,6 +574,7 @@ #include <private/qtextcontrol_p.h> #include <private/qtextdocumentlayout_p.h> #include <private/qtextengine_p.h> +#include <private/qgesturemanager_p.h> #ifdef Q_WS_X11 #include <private/qt_x11_p.h> @@ -614,6 +615,8 @@ public: }; Q_GLOBAL_STATIC(QGraphicsItemCustomDataStore, qt_dataStore) +QString qt_getStandardGestureTypeName(Qt::GestureType type); + /*! \internal @@ -1127,6 +1130,7 @@ QGraphicsItem::~QGraphicsItem() { d_ptr->inDestructor = 1; d_ptr->removeExtraItemCache(); + d_ptr->removeExtraGestures(); clearFocus(); if (!d_ptr->children.isEmpty()) { @@ -2281,6 +2285,36 @@ void QGraphicsItem::setAcceptsHoverEvents(bool enabled) setAcceptHoverEvents(enabled); } +/*! \since 4.6 + + Returns true if an item accepts touch events (QTouchEvent); otherwise, returns false. By + default, items do not accept touch events. + + \sa setAcceptTouchEvents() +*/ +bool QGraphicsItem::acceptTouchEvents() const +{ + return d_ptr->acceptTouchEvents; +} + +/*! + \since 4.6 + + If \a enabled is true, this item will accept touch events; + otherwise, it will ignore them. By default, items do not accept + touch events. +*/ +void QGraphicsItem::setAcceptTouchEvents(bool enabled) +{ + if (d_ptr->acceptTouchEvents == quint32(enabled)) + return; + d_ptr->acceptTouchEvents = quint32(enabled); + if (d_ptr->acceptTouchEvents && d_ptr->scene && d_ptr->scene->d_func()->allItemsIgnoreTouchEvents) { + d_ptr->scene->d_func()->allItemsIgnoreTouchEvents = false; + d_ptr->scene->d_func()->enableTouchEventsOnViews(); + } +} + /*! Returns true if this item handles child events (i.e., all events intended for any of its children are instead sent to this item); @@ -6219,6 +6253,102 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const } /*! + \since 4.6 + + Subscribes the graphics item to the specified \a gesture type. + + Returns the id of the gesture. + + \sa releaseGesture(), setGestureEnabled() +*/ +int QGraphicsItem::grabGesture(Qt::GestureType gesture) +{ + /// TODO: if we are QGraphicsProxyWidget we should subscribe the widget to gesture as well. + return grabGesture(qt_getStandardGestureTypeName(gesture)); +} + +/*! + \since 4.6 + + Subscribes the graphics item to the specified \a gesture type. + + Returns the id of the gesture. + + \sa releaseGesture(), setGestureEnabled() +*/ +int QGraphicsItem::grabGesture(const QString &gesture) +{ + int id = QGestureManager::instance()->makeGestureId(gesture); + d_ptr->grabGesture(id); + return id; +} + +void QGraphicsItemPrivate::grabGesture(int id) +{ + Q_Q(QGraphicsItem); + extraGestures()->gestures << id; + if (scene) + scene->d_func()->grabGesture(q, id); +} + +bool QGraphicsItemPrivate::releaseGesture(int id) +{ + Q_Q(QGraphicsItem); + QGestureExtraData *extra = maybeExtraGestures(); + if (extra && extra->gestures.contains(id)) { + if (scene) + scene->d_func()->releaseGesture(q, id); + extra->gestures.remove(id); + return true; + } + return false; +} + +/*! + \since 4.6 + + Unsubscribes the graphics item from a gesture, which is specified + by the \a gestureId. + + \sa grabGesture(), setGestureEnabled() +*/ +void QGraphicsItem::releaseGesture(int gestureId) +{ + /// TODO: if we are QGraphicsProxyWidget we should unsubscribe the widget from gesture as well. + if (d_ptr->releaseGesture(gestureId)) + QGestureManager::instance()->releaseGestureId(gestureId); +} + +/*! + \since 4.6 + + If \a enable is true, the gesture with the given \a gestureId is + enabled; otherwise the gesture is disabled. + + The id of the gesture is returned by the grabGesture(). + + \sa grabGesture(), releaseGesture() +*/ +void QGraphicsItem::setGestureEnabled(int gestureId, bool enable) +{ + //### +} + +bool QGraphicsItemPrivate::hasGesture(const QString &name) const +{ + if (QGestureExtraData *extra = maybeExtraGestures()) { + QGestureManager *gm = QGestureManager::instance(); + QSet<int>::const_iterator it = extra->gestures.begin(), + e = extra->gestures.end(); + for (; it != e; ++it) { + if (gm->gestureNameFromId(*it) == name) + return true; + } + } + return false; +} + +/*! This virtual function is called by QGraphicsItem to notify custom items that some part of the item's state changes. By reimplementing this function, your can react to a change, and in some cases, (depending on \a @@ -6242,6 +6372,23 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const QVariant QGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value) { Q_UNUSED(change); + if (change == QGraphicsItem::ItemSceneChange) { + QGestureExtraData *extra = d_ptr->maybeExtraGestures(); + if (!qVariantValue<QGraphicsScene*>(value) && extra) { + // the item has been removed from a scene, unsubscribe gestures. + Q_ASSERT(d_ptr->scene); + foreach(int id, extra->gestures) + d_ptr->scene->d_func()->releaseGesture(this, id); + } + } else if (change == QGraphicsItem::ItemSceneHasChanged) { + QGraphicsScene *scene = qVariantValue<QGraphicsScene*>(value); + QGestureExtraData *extra = d_ptr->maybeExtraGestures(); + if (scene && extra) { + // item has been added to the scene + foreach(int id, extra->gestures) + scene->d_func()->grabGesture(this, id); + } + } return value; } diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index aeb4691..3a3d1a1 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -212,6 +212,8 @@ public: void setAcceptsHoverEvents(bool enabled); // obsolete bool acceptHoverEvents() const; void setAcceptHoverEvents(bool enabled); + bool acceptTouchEvents() const; + void setAcceptTouchEvents(bool enabled); bool handlesChildEvents() const; void setHandlesChildEvents(bool enabled); @@ -377,6 +379,11 @@ public: QVariant data(int key) const; void setData(int key, const QVariant &value); + int grabGesture(Qt::GestureType gesture); + int grabGesture(const QString &gesture); + void releaseGesture(int gestureId); + void setGestureEnabled(int gestureId, bool enable = true); + enum { Type = 1, UserType = 65536 diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index b9e7041..a0d061b 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -54,6 +54,7 @@ // #include "qgraphicsitem.h" +#include "qset.h" #include "qpixmapcache.h" #include "qgraphicsview_p.h" @@ -92,6 +93,12 @@ public: void purge(); }; +class QGestureExtraData +{ +public: + QSet<int> gestures; +}; + class Q_AUTOTEST_EXPORT QGraphicsItemPrivate { Q_DECLARE_PUBLIC(QGraphicsItem) @@ -101,7 +108,8 @@ public: ExtraCursor, ExtraCacheData, ExtraMaxDeviceCoordCacheSize, - ExtraBoundingRegionGranularity + ExtraBoundingRegionGranularity, + ExtraGestures }; enum AncestorFlag { @@ -153,6 +161,8 @@ public: isObject(0), ignoreVisible(0), ignoreOpacity(0), + acceptTouchEvents(0), + acceptedTouchBeginEvent(0), globalStackingOrder(-1), q_ptr(0) { @@ -174,7 +184,7 @@ public: void combineTransformToParent(QTransform *x, const QTransform *viewTransform = 0) const; void combineTransformFromParent(QTransform *x, const QTransform *viewTransform = 0) const; - + // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4. virtual QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const; static bool movableAncestorIsSelected(const QGraphicsItem *item); @@ -243,7 +253,7 @@ public: } } } - + struct ExtraStruct { ExtraStruct(Extra type, QVariant value) : type(type), value(value) @@ -255,7 +265,7 @@ public: bool operator<(Extra extra) const { return type < extra; } }; - + QList<ExtraStruct> extras; QGraphicsItemCache *maybeExtraItemCache() const; @@ -384,7 +394,31 @@ public: int index; int depth; - // Packed 32 bits + inline QGestureExtraData* extraGestures() const + { + QGestureExtraData *c = (QGestureExtraData *)qVariantValue<void *>(extra(ExtraGestures)); + if (!c) { + QGraphicsItemPrivate *that = const_cast<QGraphicsItemPrivate *>(this); + c = new QGestureExtraData; + that->setExtra(ExtraGestures, qVariantFromValue<void *>(c)); + } + return c; + } + QGestureExtraData* maybeExtraGestures() const + { + return (QGestureExtraData *)qVariantValue<void *>(extra(ExtraGestures)); + } + inline void removeExtraGestures() + { + QGestureExtraData *c = (QGestureExtraData *)qVariantValue<void *>(extra(ExtraGestures)); + delete c; + unsetExtra(ExtraGestures); + } + bool hasGesture(const QString &gesture) const; + void grabGesture(int id); + bool releaseGesture(int id); + + // Packed 32 bytes quint32 acceptedMouseButtons : 5; quint32 visible : 1; quint32 explicitlyHidden : 1; @@ -401,8 +435,8 @@ public: quint32 cacheMode : 2; quint32 hasBoundingRegionGranularity : 1; quint32 isWidget : 1; - quint32 dirty : 1; - quint32 dirtyChildren : 1; + quint32 dirty : 1; + quint32 dirtyChildren : 1; quint32 localCollisionHack : 1; quint32 dirtyClipPath : 1; quint32 emptyClipPath : 1; @@ -421,7 +455,9 @@ public: quint32 isObject : 1; quint32 ignoreVisible : 1; quint32 ignoreOpacity : 1; - quint32 unused : 12; // feel free to use + quint32 acceptTouchEvents : 1; + quint32 acceptedTouchBeginEvent : 1; + quint32 unused : 10; // feel free to use // Optional stacking order int globalStackingOrder; diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index 57269ce..1f3637d 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -48,6 +48,7 @@ #include "private/qgraphicsproxywidget_p.h" #include "private/qwidget_p.h" #include "private/qapplication_p.h" +#include "private/qgesturemanager_p.h" #include <QtCore/qdebug.h> #include <QtGui/qevent.h> @@ -275,7 +276,7 @@ void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneMouseEvent QWidget *embeddedMouseGrabberPtr = (QWidget *)embeddedMouseGrabber; QApplicationPrivate::sendMouseEvent(receiver, mouseEvent, alienWidget, widget, - &embeddedMouseGrabberPtr, lastWidgetUnderMouse); + &embeddedMouseGrabberPtr, lastWidgetUnderMouse, event->spontaneous()); embeddedMouseGrabber = embeddedMouseGrabberPtr; // Handle enter/leave events when last button is released from mouse @@ -648,6 +649,9 @@ void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool auto q->setAttribute(Qt::WA_OpaquePaintEvent); widget = newWidget; + foreach(int gestureId, widget->d_func()->gestures) { + grabGesture(gestureId); + } // Changes only go from the widget to the proxy. enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; @@ -871,6 +875,16 @@ bool QGraphicsProxyWidget::event(QEvent *event) } break; } + case QEvent::GraphicsSceneGesture: { + qDebug() << "QGraphicsProxyWidget: graphicsscenegesture"; + if (d->widget && d->widget->isVisible()) { + QGraphicsSceneGestureEvent *ge = static_cast<QGraphicsSceneGestureEvent*>(event); + //### TODO: widget->childAt(): decompose gesture event and find widget under hotspots. + //QGestureManager::instance()->sendGestureEvent(d->widget, ge->gestures().toSet(), ge->cancelledGestures()); + return true; + } + break; + } default: break; } diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index d9d31ef..7428d79 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -246,6 +246,8 @@ #ifdef Q_WS_X11 #include <private/qt_x11_p.h> #endif +#include <private/qgesturemanager_p.h> +#include <private/qgesture_p.h> QT_BEGIN_NAMESPACE @@ -358,7 +360,8 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() painterStateProtection(true), sortCacheEnabled(false), updatingSortCache(false), - style(0) + style(0), + allItemsIgnoreTouchEvents(true) { } @@ -1357,6 +1360,7 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou // event is converted to a press. Known limitation: // Triple-clicking will not generate a doubleclick, though. QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress); + mousePress.spont = mouseEvent->spont; mousePress.accept(); mousePress.setButton(mouseEvent->button()); mousePress.setButtons(mouseEvent->buttons()); @@ -2885,6 +2889,7 @@ void QGraphicsScene::clear() d->largestUntransformableItem = QRectF(); d->allItemsIgnoreHoverEvents = true; d->allItemsUseDefaultCursor = true; + d->allItemsIgnoreTouchEvents = true; } /*! @@ -3062,6 +3067,12 @@ void QGraphicsScene::addItem(QGraphicsItem *item) } #endif //QT_NO_CURSOR + // Enable touch events if the item accepts touch events. + if (d->allItemsIgnoreTouchEvents && item->acceptTouchEvents()) { + d->allItemsIgnoreTouchEvents = false; + d->enableTouchEventsOnViews(); + } + // Update selection lists if (item->isSelected()) d->selectedItems << item; @@ -3793,6 +3804,9 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::GraphicsSceneHoverEnter: case QEvent::GraphicsSceneHoverLeave: case QEvent::GraphicsSceneHoverMove: + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: // Reset the under-mouse list to ensure that this event gets fresh // item-under-mouse data. Be careful about this list; if people delete // items from inside event handlers, this list can quickly end up @@ -3946,6 +3960,52 @@ bool QGraphicsScene::event(QEvent *event) // geometries that do not have an explicit style set. update(); break; + case QEvent::GraphicsSceneGesture: { + QGraphicsSceneGestureEvent *ev = static_cast<QGraphicsSceneGestureEvent*>(event); + QGraphicsView *view = qobject_cast<QGraphicsView*>(ev->widget()); + if (!view) { + qWarning("QGraphicsScene::event: gesture event was received without a view"); + break; + } + + // get a list of gestures that just started. + QSet<QGesture*> startedGestures; + QList<QGesture*> gestures = ev->gestures(); + for(QList<QGesture*>::const_iterator it = gestures.begin(), e = gestures.end(); + it != e; ++it) { + QGesture *g = *it; + QGesturePrivate *gd = g->d_func(); + if (g->state() == Qt::GestureStarted || gd->singleshot) { + startedGestures.insert(g); + } + } + if (!startedGestures.isEmpty()) { + // find a target for each started gesture. + for(QSet<QGesture*>::const_iterator it = startedGestures.begin(), e = startedGestures.end(); + it != e; ++it) { + QGesture *g = *it; + QGesturePrivate *gd = g->d_func(); + gd->graphicsItem = 0; + QList<QGraphicsItem*> itemsInGestureArea = items(g->hotSpot()); + const QString gestureName = g->type(); + foreach(QGraphicsItem *item, itemsInGestureArea) { + if (item->d_func()->hasGesture(gestureName)) { + Q_ASSERT(gd->graphicsItem == 0); + gd->graphicsItem = item; + d->itemsWithGestures[item].insert(g); + break; + } + } + } + } + d->sendGestureEvent(ev->gestures().toSet(), ev->cancelledGestures()); + } + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + d->touchEventHandler(static_cast<QTouchEvent *>(event)); + break; case QEvent::Timer: if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) { if (d->restartIndexTimer) { @@ -3962,6 +4022,70 @@ bool QGraphicsScene::event(QEvent *event) return true; } +void QGraphicsScenePrivate::sendGestureEvent(const QSet<QGesture*> &gestures, const QSet<QString> &cancelled) +{ + Q_Q(QGraphicsScene); + typedef QMap<QGraphicsItem*, QSet<QGesture*> > ItemGesturesMap; + ItemGesturesMap itemGestures; + QSet<QGesture*> startedGestures; + for(QSet<QGesture*>::const_iterator it = gestures.begin(), e = gestures.end(); + it != e; ++it) { + QGesture *g = *it; + Q_ASSERT(g != 0); + QGesturePrivate *gd = g->d_func(); + if (gd->graphicsItem != 0) { + itemGestures[gd->graphicsItem].insert(g); + if (g->state() == Qt::GestureStarted || gd->singleshot) + startedGestures.insert(g); + } + } + + QSet<QGesture*> ignoredGestures; + for(ItemGesturesMap::const_iterator it = itemGestures.begin(), e = itemGestures.end(); + it != e; ++it) { + QGraphicsItem *receiver = it.key(); + Q_ASSERT(receiver != 0); + QGraphicsSceneGestureEvent event; + event.setGestures(it.value()); + event.setCancelledGestures(cancelled); + bool processed = sendEvent(receiver, &event); + QSet<QGesture*> started = startedGestures.intersect(it.value()); + if (event.isAccepted()) + foreach(QGesture *g, started) + g->accept(); + if (!started.isEmpty() && !(processed && event.isAccepted())) { + // there are started gestures event that weren't + // accepted, so propagating each gesture independently. + QSet<QGesture*>::const_iterator it = started.begin(), + e = started.end(); + for(; it != e; ++it) { + QGesture *g = *it; + if (processed && g->isAccepted()) { + continue; + } + QGesturePrivate *gd = g->d_func(); + QGraphicsItem *item = gd->graphicsItem; + gd->graphicsItem = 0; + + //### THIS IS BS, DONT FORGET TO REWRITE THIS CODE + // need to make sure we try to deliver event just once to each widget + const QString gestureType = g->type(); + QList<QGraphicsItem*> itemsUnderGesture = q->items(g->hotSpot()); + for (int i = 0; i < itemsUnderGesture.size(); ++i) { + QGraphicsItem *item = itemsUnderGesture.at(i); + if (item != receiver && item->d_func()->hasGesture(gestureType)) { + ignoredGestures.insert(g); + gd->graphicsItem = item; + break; + } + } + } + } + } + if (!ignoredGestures.isEmpty()) + sendGestureEvent(ignoredGestures, cancelled); +} + /*! \reimp @@ -5166,7 +5290,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * painter->setWorldTransform(transform); painter->setClipPath(item->shape(), Qt::IntersectClip); } - + if (!dontDrawChildren) { if (item && item->d_ptr->needSortChildren) { item->d_ptr->needSortChildren = 0; @@ -5856,6 +5980,231 @@ void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) } } +void QGraphicsScenePrivate::addView(QGraphicsView *view) +{ + views << view; + foreach(int gestureId, grabbedGestures) + view->d_func()->grabGesture(gestureId); +} + +void QGraphicsScenePrivate::removeView(QGraphicsView *view) +{ + views.removeAll(view); + foreach(int gestureId, grabbedGestures) + view->releaseGesture(gestureId); +} + +void QGraphicsScenePrivate::grabGesture(QGraphicsItem *item, int gestureId) +{ + if (!grabbedGestures.contains(gestureId)) { + foreach(QGraphicsView *view, views) + view->d_func()->grabGesture(gestureId); + } + (void)itemsWithGestures[item]; + grabbedGestures << gestureId; +} + +void QGraphicsScenePrivate::releaseGesture(QGraphicsItem *item, int gestureId) +{ + itemsWithGestures.remove(item); + //### +} + +void QGraphicsScenePrivate::updateTouchPointsForItem(QGraphicsItem *item, QTouchEvent *touchEvent) +{ + QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints(); + for (int i = 0; i < touchPoints.count(); ++i) { + QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; + touchPoint.setRect(item->mapFromScene(touchPoint.sceneRect()).boundingRect()); + touchPoint.setStartPos(item->d_ptr->genericMapFromScene(touchPoint.startScenePos(), touchEvent->widget())); + touchPoint.setLastPos(item->d_ptr->genericMapFromScene(touchPoint.lastScenePos(), touchEvent->widget())); + } + touchEvent->setTouchPoints(touchPoints); +} + +int QGraphicsScenePrivate::findClosestTouchPointId(const QPointF &scenePos) +{ + int closestTouchPointId = -1; + qreal closestDistance = qreal(0.); + foreach (const QTouchEvent::TouchPoint &touchPoint, sceneCurrentTouchPoints) { + qreal distance = QLineF(scenePos, touchPoint.scenePos()).length(); + if (closestTouchPointId == -1|| distance < closestDistance) { + closestTouchPointId = touchPoint.id(); + closestDistance = distance; + } + } + return closestTouchPointId; +} + +void QGraphicsScenePrivate::touchEventHandler(QTouchEvent *sceneTouchEvent) +{ + typedef QPair<Qt::TouchPointStates, QList<QTouchEvent::TouchPoint> > StatesAndTouchPoints; + QHash<QGraphicsItem *, StatesAndTouchPoints> itemsNeedingEvents; + + for (int i = 0; i < sceneTouchEvent->touchPoints().count(); ++i) { + const QTouchEvent::TouchPoint &touchPoint = sceneTouchEvent->touchPoints().at(i); + + // update state + QGraphicsItem *item = 0; + if (touchPoint.state() == Qt::TouchPointPressed) { + // determine which item this event will go to + cachedItemsUnderMouse = itemsAtPosition(touchPoint.screenPos().toPoint(), + touchPoint.scenePos(), + sceneTouchEvent->widget()); + item = cachedItemsUnderMouse.isEmpty() ? 0 : cachedItemsUnderMouse.first(); + int closestTouchPointId = findClosestTouchPointId(touchPoint.scenePos()); + QGraphicsItem *closestItem = itemForTouchPointId.value(closestTouchPointId); + if (!item + || (closestItem + && (item->isAncestorOf(closestItem) + || closestItem->isAncestorOf(item)))) { + item = closestItem; + } + if (!item) + continue; + + itemForTouchPointId.insert(touchPoint.id(), item); + sceneCurrentTouchPoints.insert(touchPoint.id(), touchPoint); + } else if (touchPoint.state() == Qt::TouchPointReleased) { + item = itemForTouchPointId.take(touchPoint.id()); + if (!item) + continue; + + sceneCurrentTouchPoints.remove(touchPoint.id()); + } else { + item = itemForTouchPointId.value(touchPoint.id()); + if (!item) + continue; + Q_ASSERT(sceneCurrentTouchPoints.contains(touchPoint.id())); + sceneCurrentTouchPoints[touchPoint.id()] = touchPoint; + } + + StatesAndTouchPoints &statesAndTouchPoints = itemsNeedingEvents[item]; + statesAndTouchPoints.first |= touchPoint.state(); + statesAndTouchPoints.second.append(touchPoint); + } + + if (itemsNeedingEvents.isEmpty()) { + sceneTouchEvent->ignore(); + return; + } + + bool acceptSceneTouchEvent = false; + QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator it = itemsNeedingEvents.constBegin(); + const QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator end = itemsNeedingEvents.constEnd(); + for (; it != end; ++it) { + QGraphicsItem *item = it.key(); + + // determine event type from the state mask + QEvent::Type eventType; + switch (it.value().first) { + case Qt::TouchPointPressed: + // all touch points have pressed state + eventType = QEvent::TouchBegin; + break; + case Qt::TouchPointReleased: + // all touch points have released state + eventType = QEvent::TouchEnd; + break; + case Qt::TouchPointStationary: + // don't send the event if nothing changed + continue; + default: + // all other combinations + eventType = QEvent::TouchUpdate; + break; + } + + QTouchEvent touchEvent(eventType); + touchEvent.setWidget(sceneTouchEvent->widget()); + touchEvent.setModifiers(sceneTouchEvent->modifiers()); + touchEvent.setTouchPointStates(it.value().first); + touchEvent.setTouchPoints(it.value().second); + + switch (touchEvent.type()) { + case QEvent::TouchBegin: + { + // if the TouchBegin handler recurses, we assume that means the event + // has been implicitly accepted and continue to send touch events + item->d_ptr->acceptedTouchBeginEvent = true; + bool res = sendTouchBeginEvent(item, &touchEvent) + && touchEvent.isAccepted(); + acceptSceneTouchEvent = acceptSceneTouchEvent || res; + break; + } + default: + if (item->d_ptr->acceptedTouchBeginEvent) { + updateTouchPointsForItem(item, &touchEvent); + (void) sendEvent(item, &touchEvent); + acceptSceneTouchEvent = true; + } + break; + } + } + sceneTouchEvent->setAccepted(acceptSceneTouchEvent); +} + +bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEvent *touchEvent) +{ + Q_Q(QGraphicsScene); + + if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.first() != origin) { + const QTouchEvent::TouchPoint &firstTouchPoint = touchEvent->touchPoints().first(); + cachedItemsUnderMouse = itemsAtPosition(firstTouchPoint.screenPos().toPoint(), + firstTouchPoint.scenePos(), + touchEvent->widget()); + } + Q_ASSERT(cachedItemsUnderMouse.first() == origin); + + // Set focus on the topmost enabled item that can take focus. + bool setFocus = false; + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + if (item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { + if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { + setFocus = true; + if (item != q->focusItem()) + q->setFocusItem(item, Qt::MouseFocusReason); + break; + } + } + } + + // If nobody could take focus, clear it. + if (!stickyFocus && !setFocus) + q->setFocusItem(0, Qt::MouseFocusReason); + + bool res = false; + bool eventAccepted = touchEvent->isAccepted(); + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + // first, try to deliver the touch event + updateTouchPointsForItem(item, touchEvent); + bool acceptTouchEvents = item->acceptTouchEvents(); + touchEvent->setAccepted(acceptTouchEvents); + res = acceptTouchEvents && sendEvent(item, touchEvent); + eventAccepted = touchEvent->isAccepted(); + item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted); + touchEvent->spont = false; + if (res && eventAccepted) { + // the first item to accept the TouchBegin gets an implicit grab. + for (int i = 0; i < touchEvent->touchPoints().count(); ++i) { + const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i); + itemForTouchPointId[touchPoint.id()] = item; + } + break; + } + } + + touchEvent->setAccepted(eventAccepted); + return res; +} + +void QGraphicsScenePrivate::enableTouchEventsOnViews() +{ + foreach (QGraphicsView *view, views) + view->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true); +} + + QT_END_NAMESPACE #include "moc_qgraphicsscene.cpp" diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 1d4a617..aa9fd5d 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -50,6 +50,7 @@ #include <QtGui/qtransform.h> #include <QtGui/qmatrix.h> #include <QtGui/qpen.h> +#include <QtGui/qevent.h> QT_BEGIN_HEADER @@ -79,6 +80,7 @@ class QGraphicsSceneHelpEvent; class QGraphicsSceneHoverEvent; class QGraphicsSceneMouseEvent; class QGraphicsSceneWheelEvent; +class QGraphicsSceneGestureEvent; class QGraphicsSimpleTextItem; class QGraphicsTextItem; class QGraphicsView; diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index a8f6699..983486f 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -57,6 +57,7 @@ #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW +#include "qgraphicssceneevent.h" #include "qgraphicsview.h" #include "qgraphicsscene_bsp_p.h" #include "qgraphicsitem_p.h" @@ -77,6 +78,7 @@ QT_BEGIN_NAMESPACE class QGraphicsView; class QGraphicsWidget; +class QGesture; class QGraphicsScenePrivate : public QObjectPrivate { @@ -165,7 +167,7 @@ public: void grabKeyboard(QGraphicsItem *item); void ungrabKeyboard(QGraphicsItem *item, bool itemIsDying = false); void clearKeyboardGrabber(); - + QGraphicsItem *dragDropItem; QGraphicsWidget *enterWidget; Qt::DropAction lastDropAction; @@ -184,6 +186,9 @@ public: void storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event); QList<QGraphicsView *> views; + void addView(QGraphicsView *view); + void removeView(QGraphicsView *view); + bool painterStateProtection; QMultiMap<QGraphicsItem *, QGraphicsItem *> sceneEventFilters; @@ -246,7 +251,7 @@ public: static bool closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) - { + { return item1->d_ptr->globalStackingOrder < item2->d_ptr->globalStackingOrder; } static inline bool closestItemLast_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) @@ -294,6 +299,22 @@ public: void updatePalette(const QPalette &palette); QStyleOptionGraphicsItem styleOptionTmp; + + // items with gestures -> list of started gestures. + QMap<QGraphicsItem*, QSet<QGesture*> > itemsWithGestures; + QSet<int> grabbedGestures; + void grabGesture(QGraphicsItem *item, int gestureId); + void releaseGesture(QGraphicsItem *item, int gestureId); + void sendGestureEvent(const QSet<QGesture*> &gestures, const QSet<QString> &cancelled); + + QMap<int, QTouchEvent::TouchPoint> sceneCurrentTouchPoints; + QHash<int, QGraphicsItem *> itemForTouchPointId; + static void updateTouchPointsForItem(QGraphicsItem *item, QTouchEvent *touchEvent); + int findClosestTouchPointId(const QPointF &scenePos); + void touchEventHandler(QTouchEvent *touchEvent); + bool sendTouchBeginEvent(QGraphicsItem *item, QTouchEvent *touchEvent); + bool allItemsIgnoreTouchEvents; + void enableTouchEventsOnViews(); }; QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp index 283010d..ec4f5f8 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.cpp +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -76,14 +76,14 @@ received by the view (see \l{QGraphicsSceneMouseEvent::}{lastScreenPos()}, \l{QGraphicsSceneMouseEvent::}{lastScenePos()}, and - \l{QGraphicsSceneMouseEvent::}{lastPos()}). + \l{QGraphicsSceneMouseEvent::}{lastPos()}). \sa QEvent */ /*! \class QGraphicsSceneMouseEvent - \brief The QGraphicsSceneMouseEvent class provides mouse events + \brief The QGraphicsSceneMouseEvent class provides mouse events in the graphics view framework. \since 4.2 \ingroup multimedia @@ -106,7 +106,7 @@ /*! \class QGraphicsSceneWheelEvent - \brief The QGraphicsSceneWheelEvent class provides wheel events + \brief The QGraphicsSceneWheelEvent class provides wheel events in the graphics view framework. \brief The QGraphicsSceneWheelEvent class provides wheel events in the graphics view framework. @@ -157,7 +157,7 @@ /*! \class QGraphicsSceneHoverEvent - \brief The QGraphicsSceneHoverEvent class provides hover events + \brief The QGraphicsSceneHoverEvent class provides hover events in the graphics view framework. \since 4.2 \ingroup multimedia @@ -173,7 +173,7 @@ /*! \class QGraphicsSceneHelpEvent - \brief The QGraphicsSceneHelpEvent class provides events when a + \brief The QGraphicsSceneHelpEvent class provides events when a tooltip is requested. \since 4.2 \ingroup multimedia @@ -199,7 +199,7 @@ /*! \class QGraphicsSceneDragDropEvent \brief The QGraphicsSceneDragDropEvent class provides events for - drag and drop in the graphics view framework. + drag and drop in the graphics view framework. \since 4.2 \ingroup multimedia \ingroup graphicsview-api @@ -268,9 +268,14 @@ #include <QtCore/qpoint.h> #include <QtCore/qsize.h> #include <QtCore/qstring.h> +#include "qgraphicsview.h" +#include "qgraphicsitem.h" +#include <private/qevent_p.h> QT_BEGIN_NAMESPACE +QString qt_getStandardGestureTypeName(Qt::GestureType type); + class QGraphicsSceneEventPrivate { public: @@ -522,7 +527,7 @@ void QGraphicsSceneMouseEvent::setLastPos(const QPointF &pos) } /*! - Returns the last recorded mouse cursor position in scene + Returns the last recorded mouse cursor position in scene coordinates. The last recorded position is the position of the previous mouse event received by the view that created the event. @@ -545,7 +550,7 @@ void QGraphicsSceneMouseEvent::setLastScenePos(const QPointF &pos) } /*! - Returns the last recorded mouse cursor position in screen + Returns the last recorded mouse cursor position in screen coordinates. The last recorded position is the position of the previous mouse event received by the view that created the event. @@ -1275,7 +1280,7 @@ QGraphicsSceneDragDropEvent::~QGraphicsSceneDragDropEvent() /*! Returns the mouse position of the event relative to the view that sent the event. - + \sa QGraphicsView, screenPos(), scenePos() */ QPointF QGraphicsSceneDragDropEvent::pos() const @@ -1373,7 +1378,7 @@ void QGraphicsSceneDragDropEvent::setButtons(Qt::MouseButtons buttons) /*! Returns the keyboard modifiers that were pressed when the drag - and drop event was created. + and drop event was created. \sa Qt::KeyboardModifiers */ @@ -1428,7 +1433,7 @@ void QGraphicsSceneDragDropEvent::setPossibleActions(Qt::DropActions actions) The action must be one of the possible actions as defined by \c possibleActions(). - \sa Qt::DropAction, possibleActions() + \sa Qt::DropAction, possibleActions() */ Qt::DropAction QGraphicsSceneDragDropEvent::proposedAction() const @@ -1673,6 +1678,249 @@ void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos) d->newPos = pos; } +/*! + \class QGraphicsSceneGestureEvent + \brief The QGraphicsSceneGestureEvent class provides gesture events for + the graphics view framework. + \since 4.6 + \ingroup multimedia + \ingroup graphicsview-api + + QGraphicsSceneGestureEvent extends information provided by + QGestureEvent by adding some convenience functions like + \l{QGraphicsSceneEvent::}{widget()} to get a widget that received + original gesture event, and convenience functions mapToScene(), + mapToItem() for converting positions of the gesture into + QGraphicsScene and QGraphicsItem coordinate system respectively. + + The scene sends the event to the first QGraphicsItem under the + mouse cursor that accepts gestures; a graphics item is set to accept + gestures with \l{QGraphicsItem::}{grabGesture()}. + + \sa QGestureEvent +*/ + +/*! + Constructs a QGraphicsSceneGestureEvent. +*/ +QGraphicsSceneGestureEvent::QGraphicsSceneGestureEvent() + : QGraphicsSceneEvent(QEvent::GraphicsSceneGesture) +{ + setAccepted(false); +} + +/*! + Destroys a QGraphicsSceneGestureEvent. +*/ +QGraphicsSceneGestureEvent::~QGraphicsSceneGestureEvent() +{ +} + +/*! + Returns true if the gesture event contains gesture of specific \a + type; returns false otherwise. +*/ +bool QGraphicsSceneGestureEvent::contains(const QString &type) const +{ + return gesture(type) != 0; +} + +/*! + Returns true if the gesture event contains gesture of specific \a + type; returns false otherwise. +*/ +bool QGraphicsSceneGestureEvent::contains(Qt::GestureType type) const +{ + return contains(qt_getStandardGestureTypeName(type)); +} + +/*! + Returns a list of gesture names that this event contains. +*/ +QList<QString> QGraphicsSceneGestureEvent::gestureTypes() const +{ + return m_gestures.keys(); +} + +/*! + Returns extended information about a gesture of specific \a type. +*/ +const QGesture* QGraphicsSceneGestureEvent::gesture(const QString &type) const +{ + return m_gestures.value(type, 0); +} + +/*! + Returns extended information about a gesture of specific \a type. +*/ +const QGesture* QGraphicsSceneGestureEvent::gesture(Qt::GestureType type) const +{ + return gesture(qt_getStandardGestureTypeName(type)); +} + +/*! + Returns extended information about all gestures in the event. +*/ +QList<QGesture*> QGraphicsSceneGestureEvent::gestures() const +{ + return m_gestures.values(); +} + +/*! + Returns a set of gesture names that used to be executed, but were + cancelled (i.e. they were not finished properly). +*/ +QSet<QString> QGraphicsSceneGestureEvent::cancelledGestures() const +{ + return m_cancelledGestures; +} + +/*! + Sets a list of gesture names \a cancelledGestures that used to be + executed, but were cancelled (i.e. they were not finished + properly). +*/ +void QGraphicsSceneGestureEvent::setCancelledGestures(const QSet<QString> &cancelledGestures) +{ + m_cancelledGestures = cancelledGestures; +} + +/*! + Maps the point \a point, which is in a view coordinate system, to + scene coordinate system, and returns the mapped coordinate. + + A \a point is in coordinate system of the widget that received + gesture event. + + \sa mapToItem(), {The Graphics View Coordinate System} +*/ +QPointF QGraphicsSceneGestureEvent::mapToScene(const QPoint &point) const +{ + if (QGraphicsView *view = qobject_cast<QGraphicsView*>(widget())) + return view->mapToScene(point); + return QPointF(); +} + +/*! + Maps the rectangular \a rect, which is in a view coordinate system, to + scene coordinate system, and returns the mapped coordinate. + + A \a rect is in coordinate system of the widget that received + gesture event. + + \sa mapToItem(), {The Graphics View Coordinate System} +*/ +QPolygonF QGraphicsSceneGestureEvent::mapToScene(const QRect &rect) const +{ + if (QGraphicsView *view = qobject_cast<QGraphicsView*>(widget())) + return view->mapToScene(rect); + return QPolygonF(); +} + +/*! + Maps the point \a point, which is in a view coordinate system, to + item's \a item coordinate system, and returns the mapped coordinate. + + If \a item is 0, this function returns the same as mapToScene(). + + \sa mapToScene(), {The Graphics View Coordinate System} +*/ +QPointF QGraphicsSceneGestureEvent::mapToItem(const QPoint &point, QGraphicsItem *item) const +{ + if (item) { + if (QGraphicsView *view = qobject_cast<QGraphicsView*>(widget())) + return item->mapFromScene(view->mapToScene(point)); + } else { + return mapToScene(point); + } + return QPointF(); +} + +/*! + Maps the rectangualar \a rect, which is in a view coordinate system, to + item's \a item coordinate system, and returns the mapped coordinate. + + If \a item is 0, this function returns the same as mapToScene(). + + \sa mapToScene(), {The Graphics View Coordinate System} +*/ +QPolygonF QGraphicsSceneGestureEvent::mapToItem(const QRect &rect, QGraphicsItem *item) const +{ + if (item) { + if (QGraphicsView *view = qobject_cast<QGraphicsView*>(widget())) + return item->mapFromScene(view->mapToScene(rect)); + } else { + return mapToScene(rect); + } + return QPolygonF(); +} + +/*! + Set a list of gesture objects containing extended information about \a gestures. +*/ +void QGraphicsSceneGestureEvent::setGestures(const QList<QGesture*> &gestures) +{ + foreach(QGesture *g, gestures) + m_gestures.insert(g->type(), g); +} + +/*! + Set a list of gesture objects containing extended information about \a gestures. +*/ +void QGraphicsSceneGestureEvent::setGestures(const QSet<QGesture*> &gestures) +{ + foreach(QGesture *g, gestures) + m_gestures.insert(g->type(), g); +} + +/*! + Sets the accept flag of the all gestures inside the event object, + the equivalent of calling \l{QEvent::accept()}{accept()} or + \l{QEvent::setAccepted()}{setAccepted(true)}. + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGraphicsSceneGestureEvent::acceptAll() +{ + QHash<QString, QGesture*>::iterator it = m_gestures.begin(), + e = m_gestures.end(); + for(; it != e; ++it) + it.value()->accept(); + setAccepted(true); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGraphicsSceneGestureEvent::accept(Qt::GestureType type) +{ + if (QGesture *g = m_gestures.value(qt_getStandardGestureTypeName(type), 0)) + g->accept(); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGraphicsSceneGestureEvent::accept(const QString &type) +{ + if (QGesture *g = m_gestures.value(type, 0)) + g->accept(); +} + QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h index d493de3..8a30bb8 100644 --- a/src/gui/graphicsview/qgraphicssceneevent.h +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -44,6 +44,10 @@ #include <QtCore/qcoreevent.h> #include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtGui/qpolygon.h> +#include <QtCore/qset.h> +#include <QtCore/qhash.h> QT_BEGIN_HEADER @@ -302,6 +306,49 @@ public: void setNewPos(const QPointF &pos); }; +class QGesture; +class QGraphicsItem; +class QGraphicsSceneGestureEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneGestureEvent : public QGraphicsSceneEvent +{ + Q_DECLARE_PRIVATE(QGraphicsSceneGestureEvent) +public: + QGraphicsSceneGestureEvent(); + ~QGraphicsSceneGestureEvent(); + + bool contains(const QString &type) const; + bool contains(Qt::GestureType type) const; + + QList<QString> gestureTypes() const; + + const QGesture* gesture(Qt::GestureType type) const; + const QGesture* gesture(const QString &type) const; + QList<QGesture*> gestures() const; + void setGestures(const QList<QGesture*> &gestures); + void setGestures(const QSet<QGesture*> &gestures); + + QSet<QString> cancelledGestures() const; + void setCancelledGestures(const QSet<QString> &cancelledGestures); + + void acceptAll(); +#ifndef Q_NO_USING_KEYWORD + using QEvent::accept; +#else + inline void accept() { QEvent::accept(); } +#endif + void accept(Qt::GestureType type); + void accept(const QString &type); + + QPointF mapToScene(const QPoint &point) const; + QPolygonF mapToScene(const QRect &rect) const; + QPointF mapToItem(const QPoint &point, QGraphicsItem *item) const; + QPolygonF mapToItem(const QRect &rect, QGraphicsItem *item) const; + +protected: + QHash<QString, QGesture*> m_gestures; + QSet<QString> m_cancelledGestures; +}; + #endif // QT_NO_GRAPHICSVIEW QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index fb1a822..6ac2914 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -284,8 +284,12 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < #include <private/qt_x11_p.h> #endif +#include <private/qevent_p.h> + QT_BEGIN_NAMESPACE +bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); + inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for single precision { if (d <= (qreal) INT_MIN) @@ -295,6 +299,23 @@ inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for sing return d >= 0.0 ? int(d + 0.5) : int(d - int(d-1) + 0.5) + int(d-1); } +void QGraphicsViewPrivate::translateTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *touchEvent) +{ + QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints(); + for (int i = 0; i < touchPoints.count(); ++i) { + QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; + // the scene will set the item local pos, startPos, lastPos, and rect before delivering to + // an item, but for now those functions are returning the view's local coordinates + touchPoint.setSceneRect(d->mapToScene(touchPoint.rect())); + touchPoint.setStartScenePos(d->mapToScene(touchPoint.startPos())); + touchPoint.setLastScenePos(d->mapToScene(touchPoint.lastPos())); + + // screenPos, startScreenPos, lastScreenPos, and screenRect are already set + } + + touchEvent->setTouchPoints(touchPoints); +} + /*! \internal */ @@ -591,7 +612,7 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) lastMouseMoveScenePoint = mouseEvent.scenePos(); lastMouseMoveScreenPoint = mouseEvent.screenPos(); mouseEvent.setAccepted(false); - QApplication::sendEvent(scene, &mouseEvent); + qt_sendSpontaneousEvent(scene, &mouseEvent); // Remember whether the last event was accepted or not. lastMouseEvent.setAccepted(mouseEvent.isAccepted()); @@ -1488,7 +1509,7 @@ void QGraphicsView::setScene(QGraphicsScene *scene) this, SLOT(updateScene(QList<QRectF>))); disconnect(d->scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(updateSceneRect(QRectF))); - d->scene->d_func()->views.removeAll(this); + d->scene->d_func()->removeView(this); d->connectedToScene = false; } @@ -1497,7 +1518,7 @@ void QGraphicsView::setScene(QGraphicsScene *scene) connect(d->scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(updateSceneRect(QRectF))); d->updateSceneSlotReimplementedChecked = false; - d->scene->d_func()->views << this; + d->scene->d_func()->addView(this); d->recalculateContentSize(); d->lastCenterPoint = sceneRect().center(); d->keepLastCenterPoint = true; @@ -1507,6 +1528,10 @@ void QGraphicsView::setScene(QGraphicsScene *scene) || !d->scene->d_func()->allItemsUseDefaultCursor) { d->viewport->setMouseTracking(true); } + + // enable touch events if any items is interested in them + if (!d->scene->d_func()->allItemsIgnoreTouchEvents) + d->viewport->setAttribute(Qt::WA_AcceptTouchEvents); } else { d->recalculateContentSize(); } @@ -2590,6 +2615,11 @@ void QGraphicsView::setupViewport(QWidget *widget) || !d->scene->d_func()->allItemsUseDefaultCursor)) { widget->setMouseTracking(true); } + + // enable touch events if any items is interested in them + if (d->scene && !d->scene->d_func()->allItemsIgnoreTouchEvents) + widget->setAttribute(Qt::WA_AcceptTouchEvents); + widget->setAcceptDrops(acceptDrops()); } @@ -2626,6 +2656,9 @@ bool QGraphicsView::event(QEvent *event) } } break; + case QEvent::Gesture: + viewportEvent(event); + return true; default: break; } @@ -2707,6 +2740,34 @@ bool QGraphicsView::viewportEvent(QEvent *event) d->scene->d_func()->updateAll = false; } break; + case QEvent::Gesture: { + QGraphicsSceneGestureEvent gestureEvent; + gestureEvent.setWidget(this); + QGestureEvent *ev = static_cast<QGestureEvent*>(event); + gestureEvent.setGestures(ev->gestures()); + gestureEvent.setCancelledGestures(ev->cancelledGestures()); + QApplication::sendEvent(d->scene, &gestureEvent); + event->setAccepted(gestureEvent.isAccepted()); + return true; + } + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + if (!isEnabled()) + return false; + + if (d->scene && d->sceneInteractionAllowed) { + // Convert and deliver the touch event to the scene. + QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event); + touchEvent->setWidget(viewport()); + QGraphicsViewPrivate::translateTouchEvent(d, touchEvent); + (void) QApplication::sendEvent(d->scene, touchEvent); + } + + return true; + } default: break; } @@ -2965,7 +3026,7 @@ void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) mouseEvent.setAccepted(false); mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); - QApplication::sendEvent(d->scene, &mouseEvent); + qt_sendSpontaneousEvent(d->scene, &mouseEvent); } /*! @@ -3004,7 +3065,7 @@ void QGraphicsView::mousePressEvent(QMouseEvent *event) mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); mouseEvent.setAccepted(false); - QApplication::sendEvent(d->scene, &mouseEvent); + qt_sendSpontaneousEvent(d->scene, &mouseEvent); // Update the original mouse event accepted state. bool isAccepted = mouseEvent.isAccepted(); @@ -3174,7 +3235,7 @@ void QGraphicsView::mouseReleaseEvent(QMouseEvent *event) mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); mouseEvent.setAccepted(false); - QApplication::sendEvent(d->scene, &mouseEvent); + qt_sendSpontaneousEvent(d->scene, &mouseEvent); // Update the last mouse event selected state. d->lastMouseEvent.setAccepted(mouseEvent.isAccepted()); @@ -3634,6 +3695,39 @@ void QGraphicsView::resetTransform() setTransform(QTransform()); } +QPointF QGraphicsViewPrivate::mapToScene(const QPointF &point) const +{ + QPointF p = point; + p.rx() += horizontalScroll(); + p.ry() += verticalScroll(); + return identityMatrix ? p : matrix.inverted().map(p); +} + +QRectF QGraphicsViewPrivate::mapToScene(const QRectF &rect) const +{ + QPointF scrollOffset(horizontalScroll(), verticalScroll()); + QPointF tl = scrollOffset + rect.topLeft(); + QPointF tr = scrollOffset + rect.topRight(); + QPointF br = scrollOffset + rect.bottomRight(); + QPointF bl = scrollOffset + rect.bottomLeft(); + + QPolygonF poly; + poly.resize(4); + if (!identityMatrix) { + QTransform x = matrix.inverted(); + poly[0] = x.map(tl); + poly[1] = x.map(tr); + poly[2] = x.map(br); + poly[3] = x.map(bl); + } else { + poly[0] = tl; + poly[1] = tr; + poly[2] = br; + poly[3] = bl; + } + return poly.boundingRect(); +} + QT_END_NAMESPACE #include "moc_qgraphicsview.cpp" diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h index 9551ff6..5682138 100644 --- a/src/gui/graphicsview/qgraphicsview_p.h +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -177,6 +177,17 @@ public: QRegion exposedRegion; QList<QGraphicsItem *> findItems(const QRegion &exposedRegion, bool *allItems) const; + + void generateStyleOptions(const QList<QGraphicsItem *> &itemList, + QGraphicsItem **itemArray, + QStyleOptionGraphicsItem *styleOptionArray, + const QTransform &worldTransform, + bool allItems, + const QRegion &exposedRegion) const; + + QPointF mapToScene(const QPointF &point) const; + QRectF mapToScene(const QRectF &rect) const; + static void translateTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *touchEvent); }; QT_END_NAMESPACE diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index a1b982a..3016afb 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -41,7 +41,15 @@ HEADERS += \ kernel/qwidgetaction.h \ kernel/qwidgetaction_p.h \ kernel/qwindowdefs.h \ - kernel/qkeymapper_p.h + kernel/qkeymapper_p.h \ + kernel/qgesture.h \ + kernel/qgesture_p.h \ + kernel/qgesturemanager_p.h \ + kernel/qgesturerecognizer_p.h \ + kernel/qgesturerecognizer.h \ + kernel/qgesturestandardrecognizers_p.h \ + kernel/qdirectionrecognizer_p.h \ + kernel/qdirectionsimplificator_p.h SOURCES += \ kernel/qaction.cpp \ @@ -70,7 +78,12 @@ SOURCES += \ kernel/qwhatsthis.cpp \ kernel/qwidget.cpp \ kernel/qwidgetaction.cpp \ - kernel/qkeymapper.cpp + kernel/qkeymapper.cpp \ + kernel/qgesture.cpp \ + kernel/qgesturemanager.cpp \ + kernel/qgesturerecognizer.cpp \ + kernel/qgesturestandardrecognizers.cpp \ + kernel/qdirectionrecognizer.cpp win32 { DEFINES += QT_NO_DIRECTDRAW diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 7219fed..5ef453d 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -85,10 +85,13 @@ #include <stdlib.h> #include "qapplication_p.h" +#include "qevent_p.h" #include "qwidget_p.h" #include "qapplication.h" +#include <private/qgesturemanager_p.h> + #ifdef Q_WS_WINCE #include "qdatetime.h" #include "qguifunctions_wince.h" @@ -97,6 +100,8 @@ extern bool qt_wince_is_mobile(); //qguifunctions_wince.cpp extern bool qt_wince_is_pocket_pc(); //qguifunctions_wince.cpp #endif +#include "qdatetime.h" + //#define ALIEN_DEBUG static void initResources() @@ -132,6 +137,14 @@ int QApplicationPrivate::autoMaximizeThreshold = -1; bool QApplicationPrivate::autoSipEnabled = false; #endif +QGestureManager* QGestureManager::instance() +{ + QApplicationPrivate *d = qApp->d_func(); + if (!d->gestureManager) + d->gestureManager = new QGestureManager(qApp); + return d->gestureManager; +} + QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::Type type) : QCoreApplicationPrivate(argc, argv) { @@ -155,6 +168,8 @@ QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::T directPainters = 0; #endif + gestureManager = 0; + if (!self) self = this; } @@ -884,6 +899,8 @@ void QApplicationPrivate::initialize() QApplicationPrivate::wheel_scroll_lines = 3; #endif #endif + + initializeMultitouch(); } /*! @@ -1034,6 +1051,8 @@ QApplication::~QApplication() delete QDragManager::self(); #endif + d->cleanupMultitouch(); + qt_cleanup(); if (QApplicationPrivate::widgetCount) @@ -1381,7 +1400,7 @@ void QApplication::setStyle(QStyle *style) #endif // QT_NO_STYLE_STYLESHEET QApplicationPrivate::app_style = style; QApplicationPrivate::app_style->setParent(qApp); // take ownership - + // take care of possible palette requirements of certain gui // styles. Do it before polishing the application since the style // might call QApplication::setPalette() itself @@ -2868,7 +2887,8 @@ QWidget *QApplicationPrivate::pickMouseReceiver(QWidget *candidate, const QPoint */ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget, QWidget *nativeWidget, - QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver) + QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver, + bool spontaneous) { Q_ASSERT(receiver); Q_ASSERT(event); @@ -2921,7 +2941,11 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, // We need this quard in case someone opens a modal dialog / popup. If that's the case // leaveAfterRelease is set to null, but we shall not update lastMouseReceiver. const bool wasLeaveAfterRelease = leaveAfterRelease != 0; - bool result = QApplication::sendSpontaneousEvent(receiver, event); + bool result; + if (spontaneous) + result = QApplication::sendSpontaneousEvent(receiver, event); + else + result = QApplication::sendEvent(receiver, event); if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease && !event->buttons() && QWidget::mouseGrabber() != leaveAfterRelease) { @@ -3562,26 +3586,32 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QApplicationPrivate::mouse_buttons |= me->button(); else QApplicationPrivate::mouse_buttons &= ~me->button(); - } + } #if !defined(QT_NO_WHEELEVENT) || !defined(QT_NO_TABLETEVENT) - else if ( + else if (false # ifndef QT_NO_WHEELEVENT - e->type() == QEvent::Wheel -# else - false + || e->type() == QEvent::Wheel # endif # ifndef QT_NO_TABLETEVENT - || e->type() == QEvent::TabletMove - || e->type() == QEvent::TabletPress - || e->type() == QEvent::TabletRelease + || e->type() == QEvent::TabletMove + || e->type() == QEvent::TabletPress + || e->type() == QEvent::TabletRelease # endif - ) { + ) { QInputEvent *ie = static_cast<QInputEvent*>(e); QApplicationPrivate::modifier_buttons = ie->modifiers(); } #endif // !QT_NO_WHEELEVENT || !QT_NO_TABLETEVENT } + if (!d->grabbedGestures.isEmpty() && e->spontaneous() && receiver->isWidgetType()) { + const QEvent::Type t = e->type(); + if (t != QEvent::Gesture && t != QEvent::GraphicsSceneGesture) { + if (QGestureManager::instance()->filterEvent(static_cast<QWidget*>(receiver), e)) + return true; + } + } + // User input and window activation makes tooltips sleep switch (e->type()) { case QEvent::Wheel: @@ -3709,19 +3739,10 @@ bool QApplication::notify(QObject *receiver, QEvent *e) QPoint relpos = mouse->pos(); if (e->spontaneous()) { - if (e->type() == QEvent::MouseButtonPress) { - QWidget *fw = w; - while (fw) { - if (fw->isEnabled() - && QApplicationPrivate::shouldSetFocus(fw, Qt::ClickFocus)) { - fw->setFocus(Qt::MouseFocusReason); - break; - } - if (fw->isWindow()) - break; - fw = fw->parentWidget(); - } + QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, + Qt::ClickFocus, + Qt::MouseFocusReason); } if (e->type() == QEvent::MouseMove && mouse->buttons() == 0) { @@ -3803,17 +3824,9 @@ bool QApplication::notify(QObject *receiver, QEvent *e) bool eventAccepted = wheel->isAccepted(); if (e->spontaneous()) { - QWidget *fw = w; - while (fw) { - if (fw->isEnabled() - && QApplicationPrivate::shouldSetFocus(fw, Qt::WheelFocus)) { - fw->setFocus(Qt::MouseFocusReason); - break; - } - if (fw->isWindow()) - break; - fw = fw->parentWidget(); - } + QApplicationPrivate::giveFocusAccordingToFocusPolicy(w, + Qt::WheelFocus, + Qt::MouseFocusReason); } while (w) { @@ -4018,7 +4031,45 @@ bool QApplication::notify(QObject *receiver, QEvent *e) } break; #endif + case QEvent::TouchBegin: + // Note: TouchUpdate and TouchEnd events are never propagated + { + QWidget *widget = static_cast<QWidget *>(receiver); + QTouchEvent *touchEvent = static_cast<QTouchEvent *>(e); + bool eventAccepted = touchEvent->isAccepted(); + if (widget->testAttribute(Qt::WA_AcceptTouchEvents) && e->spontaneous()) { + // give the widget focus if the focus policy allows it + QApplicationPrivate::giveFocusAccordingToFocusPolicy(widget, + Qt::ClickFocus, + Qt::MouseFocusReason); + } + + while (widget) { + // first, try to deliver the touch event + bool acceptTouchEvents = widget->testAttribute(Qt::WA_AcceptTouchEvents); + touchEvent->setWidget(widget); + touchEvent->setAccepted(acceptTouchEvents); + res = acceptTouchEvents && d->notify_helper(widget, touchEvent); + eventAccepted = touchEvent->isAccepted(); + widget->setAttribute(Qt::WA_WState_AcceptedTouchBeginEvent, res && eventAccepted); + touchEvent->spont = false; + if (res && eventAccepted) { + // the first widget to accept the TouchBegin gets an implicit grab. + for (int i = 0; i < touchEvent->touchPoints().count(); ++i) { + const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i); + d->widgetForTouchPointId[touchPoint.id()] = widget; + } + break; + } else if (widget->isWindow() || widget->testAttribute(Qt::WA_NoMousePropagation)) { + break; + } + widget = widget->parentWidget(); + d->updateTouchPointsForWidget(widget, touchEvent); + } + touchEvent->setAccepted(eventAccepted); + break; + } default: res = d->notify_helper(receiver, e); break; @@ -4985,6 +5036,23 @@ Qt::LayoutDirection QApplication::keyboardInputDirection() return qt_keymapper_private()->keyboardInputDirection; } +void QApplicationPrivate::giveFocusAccordingToFocusPolicy(QWidget *widget, + Qt::FocusPolicy focusPolicy, + Qt::FocusReason focusReason) +{ + QWidget *focusWidget = widget; + while (focusWidget) { + if (focusWidget->isEnabled() + && QApplicationPrivate::shouldSetFocus(focusWidget, focusPolicy)) { + focusWidget->setFocus(focusReason); + break; + } + if (focusWidget->isWindow()) + break; + focusWidget = focusWidget->parentWidget(); + } +} + bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) { QWidget *f = w; @@ -4998,6 +5066,57 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) return true; } +/*! + \since 4.6 + + Adds custom gesture \a recognizer object. + + Qt takes ownership of the provided \a recognizer. + + \sa Qt::AA_EnableGestures, QGestureEvent +*/ +void QApplication::addGestureRecognizer(QGestureRecognizer *recognizer) +{ + QGestureManager::instance()->addRecognizer(recognizer); +} + +/*! + \since 4.6 + + Removes custom gesture \a recognizer object. + + \sa Qt::AA_EnableGestures, QGestureEvent +*/ +void QApplication::removeGestureRecognizer(QGestureRecognizer *recognizer) +{ + Q_D(QApplication); + if (!d->gestureManager) + return; + d->gestureManager->removeRecognizer(recognizer); +} + +/*! + \property QApplication::eventDeliveryDelayForGestures + \since 4.6 + + Specifies the \a delay before input events are delivered to the + gesture enabled widgets. + + The delay allows to postpone widget's input event handling until + gestures framework can successfully recognize a gesture. + + \sa QWidget::grabGesture +*/ +void QApplication::setEventDeliveryDelayForGestures(int delay) +{ + QGestureManager::instance()->setEventDeliveryDelay(delay); +} + +int QApplication::eventDeliveryDelayForGestures() +{ + return QGestureManager::instance()->eventDeliveryDelay(); +} + /*! \fn QDecoration &QApplication::qwsDecoration() Return the QWSDecoration used for decorating windows. @@ -5129,6 +5248,186 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) This normally takes some time. Does nothing on other platforms. */ +void QApplicationPrivate::updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent) +{ + for (int i = 0; i < touchEvent->touchPoints().count(); ++i) { + QTouchEvent::TouchPoint &touchPoint = touchEvent->_touchPoints[i]; + + // preserve the sub-pixel resolution + QRectF rect = touchPoint.screenRect(); + const QPointF screenPos = rect.center(); + const QPointF delta = screenPos - screenPos.toPoint(); + + rect.moveCenter(widget->mapFromGlobal(screenPos.toPoint()) + delta); + touchPoint.setRect(rect); + + touchPoint.setStartPos(widget->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta); + touchPoint.setLastPos(widget->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta); + } +} + +void QApplicationPrivate::initializeMultitouch() +{ + widgetForTouchPointId.clear(); + appCurrentTouchPoints.clear(); + + initializeMultitouch_sys(); +} + +void QApplicationPrivate::cleanupMultitouch() +{ + cleanupMultitouch_sys(); + + widgetForTouchPointId.clear(); + appCurrentTouchPoints.clear(); +} + +int QApplicationPrivate::findClosestTouchPointId(const QPointF &screenPos) +{ + int closestTouchPointId = -1; + qreal closestDistance = qreal(0.); + foreach (const QTouchEvent::TouchPoint &touchPoint, appCurrentTouchPoints) { + qreal distance = QLineF(screenPos, touchPoint.screenPos()).length(); + if (closestTouchPointId == -1 || distance < closestDistance) { + closestTouchPointId = touchPoint.id(); + closestDistance = distance; + } + } + return closestTouchPointId; +} + +void QApplicationPrivate::translateRawTouchEvent(QWidget *window, + const QList<QTouchEvent::TouchPoint> &touchPoints) +{ + QApplicationPrivate *d = self; + QApplication *q = self->q_func(); + + typedef QPair<Qt::TouchPointStates, QList<QTouchEvent::TouchPoint> > StatesAndTouchPoints; + QHash<QWidget *, StatesAndTouchPoints> widgetsNeedingEvents; + + for (int i = 0; i < touchPoints.count(); ++i) { + QTouchEvent::TouchPoint touchPoint = touchPoints.at(i); + + // update state + QWidget *widget = 0; + switch (touchPoint.state()) { + case Qt::TouchPointPressed: + { + // determine which widget this event will go to + if (!window) + window = q->topLevelAt(touchPoint.screenPos().toPoint()); + if (!window) + continue; + widget = window->childAt(window->mapFromGlobal(touchPoint.screenPos().toPoint())); + if (!widget) + widget = window; + + int closestTouchPointId = d->findClosestTouchPointId(touchPoint.screenPos()); + QWidget *closestWidget = d->widgetForTouchPointId.value(closestTouchPointId); + if (closestWidget + && (widget->isAncestorOf(closestWidget) || closestWidget->isAncestorOf(widget))) { + widget = closestWidget; + } + d->widgetForTouchPointId[touchPoint.id()] = widget; + touchPoint.setStartScreenPos(touchPoint.screenPos()); + touchPoint.setLastScreenPos(touchPoint.screenPos()); + d->appCurrentTouchPoints.insert(touchPoint.id(), touchPoint); + break; + } + case Qt::TouchPointReleased: + { + widget = d->widgetForTouchPointId.take(touchPoint.id()); + if (!widget) + continue; + + QTouchEvent::TouchPoint previousTouchPoint = d->appCurrentTouchPoints.take(touchPoint.id()); + touchPoint.setStartScreenPos(previousTouchPoint.startScreenPos()); + touchPoint.setLastScreenPos(previousTouchPoint.screenPos()); + break; + } + default: + widget = d->widgetForTouchPointId.value(touchPoint.id()); + if (!widget) + continue; + Q_ASSERT(d->appCurrentTouchPoints.contains(touchPoint.id())); + QTouchEvent::TouchPoint previousTouchPoint = d->appCurrentTouchPoints.value(touchPoint.id()); + touchPoint.setStartScreenPos(previousTouchPoint.startScreenPos()); + touchPoint.setLastScreenPos(previousTouchPoint.screenPos()); + d->appCurrentTouchPoints[touchPoint.id()] = touchPoint; + break; + } + Q_ASSERT(widget != 0); + + // make the *scene* functions return the same as the *screen* functions + touchPoint.setSceneRect(touchPoint.screenRect()); + touchPoint.setStartScenePos(touchPoint.startScreenPos()); + touchPoint.setLastScenePos(touchPoint.lastScreenPos()); + + StatesAndTouchPoints &maskAndPoints = widgetsNeedingEvents[widget]; + maskAndPoints.first |= touchPoint.state(); + if (touchPoint.isPrimary()) + maskAndPoints.first |= Qt::TouchPointPrimary; + maskAndPoints.second.append(touchPoint); + } + + if (widgetsNeedingEvents.isEmpty()) + return; + + QHash<QWidget *, StatesAndTouchPoints>::ConstIterator it = widgetsNeedingEvents.constBegin(); + const QHash<QWidget *, StatesAndTouchPoints>::ConstIterator end = widgetsNeedingEvents.constEnd(); + for (; it != end; ++it) { + QWidget *widget = it.key(); + if (!QApplicationPrivate::tryModalHelper(widget, 0)) + continue; + + QEvent::Type eventType; + switch (it.value().first & Qt::TouchPointStateMask) { + case Qt::TouchPointPressed: + eventType = QEvent::TouchBegin; + break; + case Qt::TouchPointReleased: + eventType = QEvent::TouchEnd; + break; + case Qt::TouchPointStationary: + // don't send the event if nothing changed + continue; + default: + eventType = QEvent::TouchUpdate; + break; + } + + QTouchEvent touchEvent(eventType, + q->keyboardModifiers(), + it.value().first, + it.value().second); + updateTouchPointsForWidget(widget, &touchEvent); + + switch (touchEvent.type()) { + case QEvent::TouchBegin: + { + // if the TouchBegin handler recurses, we assume that means the event + // has been implicitly accepted and continue to send touch events + widget->setAttribute(Qt::WA_WState_AcceptedTouchBeginEvent); + (void ) QApplication::sendSpontaneousEvent(widget, &touchEvent); + break; + } + default: + if (widget->testAttribute(Qt::WA_WState_AcceptedTouchBeginEvent)) { + if (touchEvent.type() == QEvent::TouchEnd) + widget->setAttribute(Qt::WA_WState_AcceptedTouchBeginEvent, false); + (void) QApplication::sendSpontaneousEvent(widget, &touchEvent); + } + break; + } + } +} + +Q_GUI_EXPORT void qt_translateRawTouchEvent(const QList<QTouchEvent::TouchPoint> &touchPoints, + QWidget *window) +{ + QApplicationPrivate::translateRawTouchEvent(window, touchPoints); +} + QT_END_NAMESPACE #include "moc_qapplication.cpp" diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index b3bb1cd..954f824 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -71,6 +71,7 @@ class QStyle; class QEventLoop; class QIcon; class QInputContext; +class QGestureRecognizer; template <typename T> class QList; class QLocale; #if defined(Q_WS_QWS) @@ -106,6 +107,7 @@ class Q_GUI_EXPORT QApplication : public QCoreApplication Q_PROPERTY(int autoMaximizeThreshold READ autoMaximizeThreshold WRITE setAutoMaximizeThreshold) Q_PROPERTY(bool autoSipEnabled READ autoSipEnabled WRITE setAutoSipEnabled) #endif + Q_PROPERTY(int eventDeliveryDelayForGestures READ eventDeliveryDelayForGestures WRITE setEventDeliveryDelayForGestures) public: enum Type { Tty, GuiClient, GuiServer }; @@ -266,6 +268,12 @@ public: static bool keypadNavigationEnabled(); #endif + void addGestureRecognizer(QGestureRecognizer *recognizer); + void removeGestureRecognizer(QGestureRecognizer *recognizer); + + void setEventDeliveryDelayForGestures(int delay); + int eventDeliveryDelayForGestures(); + Q_SIGNALS: void lastWindowClosed(); void focusChanged(QWidget *old, QWidget *now); @@ -374,6 +382,7 @@ private: friend class QDirectPainter; friend class QDirectPainterPrivate; #endif + friend class QGestureManager; #if defined(Q_WS_WIN) friend QApplicationPrivate* getQApplicationPrivateInternal(); @@ -382,6 +391,9 @@ private: #if defined(Q_WS_MAC) || defined(Q_WS_X11) Q_PRIVATE_SLOT(d_func(), void _q_alertTimeOut()) #endif +#if defined(QT_RX71_MULTITOUCH) + Q_PRIVATE_SLOT(d_func(), void _q_readRX71MultiTouchEvents()) +#endif }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index ace5d1d..479b1fe 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -2998,4 +2998,9 @@ void onApplicationChangedActivation( bool activated ) #endif } +void QApplicationPrivate::initializeMultitouch_sys() +{ } +void QApplicationPrivate::cleanupMultitouch_sys() +{ } + QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index a9e5d60..f2226ce 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -55,6 +55,7 @@ // #include "QtGui/qapplication.h" +#include "QtGui/qevent.h" #include "QtGui/qfont.h" #include "QtGui/qcursor.h" #include "QtGui/qregion.h" @@ -77,11 +78,10 @@ class QClipboard; class QGraphicsScene; class QGraphicsSystem; class QInputContext; -class QKeyEvent; -class QMouseEvent; class QObject; -class QWheelEvent; class QWidget; +class QGestureManager; +class QSocketNotifier; extern bool qt_is_gui_used; #ifndef QT_NO_CLIPBOARD @@ -189,6 +189,12 @@ extern "C" { } #endif +#if defined(Q_WS_WIN) +typedef BOOL (WINAPI *qt_RegisterTouchWindowPtr)(HWND, ULONG); +typedef BOOL (WINAPI *qt_GetTouchInputInfoPtr)(HANDLE, UINT, PVOID, int); +typedef BOOL (WINAPI *qt_CloseTouchInputHandlePtr)(HANDLE); +#endif + class QScopedLoopLevelCounter { QThreadData *threadData; @@ -421,17 +427,67 @@ public: QEvent::Type type, Qt::MouseButtons buttons, QWidget *buttonDown, QWidget *alienWidget); static bool sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget, - QWidget *native, QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver); + QWidget *native, QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver, + bool spontaneous = true); #if defined(Q_WS_WIN) || defined(Q_WS_X11) void sendSyntheticEnterLeave(QWidget *widget); #endif + QGestureManager *gestureManager; + // map<gesture name -> number of widget subscribed to it> + QMap<QString, int> grabbedGestures; + + QHash<int, QWidget *> widgetForTouchPointId; + QMap<int, QTouchEvent::TouchPoint> appCurrentTouchPoints; + static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent); + void initializeMultitouch(); + void initializeMultitouch_sys(); + void cleanupMultitouch(); + void cleanupMultitouch_sys(); + int findClosestTouchPointId(const QPointF &screenPos); + void appendTouchPoint(const QTouchEvent::TouchPoint &touchPoint); + void removeTouchPoint(int touchPointId); + static void translateRawTouchEvent(QWidget *widget, + const QList<QTouchEvent::TouchPoint> &touchPoints); + +#if defined(Q_WS_WIN) + static qt_RegisterTouchWindowPtr RegisterTouchWindow; + static qt_GetTouchInputInfoPtr GetTouchInputInfo; + static qt_CloseTouchInputHandlePtr CloseTouchInputHandle; + + QHash<DWORD, int> touchInputIDToTouchPointID; + QList<QTouchEvent::TouchPoint> appAllTouchPoints; + bool translateTouchEvent(const MSG &msg); +#endif + +#ifdef QT_RX71_MULTITOUCH + bool hasRX71MultiTouch; + + struct RX71TouchPointState { + QSocketNotifier *socketNotifier; + QTouchEvent::TouchPoint touchPoint; + + int minX, maxX, scaleX; + int minY, maxY, scaleY; + int minZ, maxZ; + }; + QList<RX71TouchPointState> allRX71TouchPoints; + + bool readRX71MultiTouchEvents(int deviceNumber); + void fakeMouseEventFromRX71TouchEvent(); + void _q_readRX71MultiTouchEvents(); +#endif + private: #ifdef Q_WS_QWS QMap<const QScreen*, QRect> maxWindowRects; #endif static QApplicationPrivate *self; + + static void giveFocusAccordingToFocusPolicy(QWidget *w, + Qt::FocusPolicy focusPolicy, + Qt::FocusReason focusReason); static bool shouldSetFocus(QWidget *w, Qt::FocusPolicy policy); }; diff --git a/src/gui/kernel/qapplication_qws.cpp b/src/gui/kernel/qapplication_qws.cpp index 0567a1d..f568438 100644 --- a/src/gui/kernel/qapplication_qws.cpp +++ b/src/gui/kernel/qapplication_qws.cpp @@ -3746,4 +3746,9 @@ void QApplication::setArgs(int c, char **v) d->argv = v; } +void QApplicationPrivate::initializeMultitouch_sys() +{ } +void QApplicationPrivate::cleanupMultitouch_sys() +{ } + QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 0c74cfa..66dfee2 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -88,6 +88,7 @@ extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.c #include "qdebug.h" #include <private/qkeymapper_p.h> #include <private/qlocale_p.h> +#include "qevent_p.h" //#define ALIEN_DEBUG @@ -112,6 +113,37 @@ extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.c # include <winable.h> #endif +#ifndef WM_TOUCH +# define WM_TOUCH 0x0240 + +# define TOUCHEVENTF_MOVE 0x0001 +# define TOUCHEVENTF_DOWN 0x0002 +# define TOUCHEVENTF_UP 0x0004 +# define TOUCHEVENTF_INRANGE 0x0008 +# define TOUCHEVENTF_PRIMARY 0x0010 +# define TOUCHEVENTF_NOCOALESCE 0x0020 +# define TOUCHEVENTF_PEN 0x0040 +# define TOUCHEVENTF_PALM 0x0080 + +# define TOUCHINPUTMASKF_TIMEFROMSYSTEM 0x0001 +# define TOUCHINPUTMASKF_EXTRAINFO 0x0002 +# define TOUCHINPUTMASKF_CONTACTAREA 0x0004 + +typedef struct tagTOUCHINPUT +{ + LONG x; + LONG y; + HANDLE hSource; + DWORD dwID; + DWORD dwFlags; + DWORD dwMask; + DWORD dwTime; + ULONG_PTR dwExtraInfo; + DWORD cxContact; + DWORD cyContact; +} TOUCHINPUT, *PTOUCHINPUT; + +#endif #ifndef FLASHW_STOP typedef struct { @@ -1708,6 +1740,9 @@ LRESULT CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam result = widget->translateWheelEvent(msg); } else { switch (message) { + case WM_TOUCH: + result = getQApplicationPrivateInternal()->translateTouchEvent(msg); + break; case WM_KEYDOWN: // keyboard event case WM_SYSKEYDOWN: qt_keymapper_private()->updateKeyMap(msg); @@ -3961,4 +3996,78 @@ void QSessionManager::cancel() #endif //QT_NO_SESSIONMANAGER +qt_RegisterTouchWindowPtr QApplicationPrivate::RegisterTouchWindow = 0; +qt_GetTouchInputInfoPtr QApplicationPrivate::GetTouchInputInfo = 0; +qt_CloseTouchInputHandlePtr QApplicationPrivate::CloseTouchInputHandle = 0; + +void QApplicationPrivate::initializeMultitouch_sys() +{ + QLibrary library(QLatin1String("user32")); + RegisterTouchWindow = static_cast<qt_RegisterTouchWindowPtr>(library.resolve("RegisterTouchWindow")); + GetTouchInputInfo = static_cast<qt_GetTouchInputInfoPtr>(library.resolve("GetTouchInputInfo")); + CloseTouchInputHandle = static_cast<qt_CloseTouchInputHandlePtr>(library.resolve("CloseTouchInputHandle")); + + touchInputIDToTouchPointID.clear(); +} + +void QApplicationPrivate::cleanupMultitouch_sys() +{ + touchInputIDToTouchPointID.clear(); +} + +bool QApplicationPrivate::translateTouchEvent(const MSG &msg) +{ + Q_Q(QApplication); + + QWidget *widgetForHwnd = QWidget::find(msg.hwnd); + if (!widgetForHwnd) + return false; + + QList<QTouchEvent::TouchPoint> touchPoints; + + QVector<TOUCHINPUT> winTouchInputs(msg.wParam); + memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count()); + QApplicationPrivate::GetTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); + for (int i = 0; i < winTouchInputs.count(); ++i) { + const TOUCHINPUT &touchInput = winTouchInputs.at(i); + + int touchPointID = touchInputIDToTouchPointID.value(touchInput.dwID, -1); + if (touchPointID == -1) { + touchPointID = touchInputIDToTouchPointID.count(); + touchInputIDToTouchPointID.insert(touchInput.dwID, touchPointID); + } + + QTouchEvent::TouchPoint touchPoint(touchPointID); + + // update state + QPointF screenPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); + QRectF screenRect; + if (touchInput.dwMask & TOUCHINPUTMASKF_CONTACTAREA) + screenRect.setSize(QSizeF(qreal(touchInput.cxContact) / qreal(100.), + qreal(touchInput.cyContact) / qreal(100.))); + screenRect.moveCenter(screenPos); + + Qt::TouchPointStates state; + if (touchInput.dwFlags & TOUCHEVENTF_DOWN) { + state = Qt::TouchPointPressed; + } else if (touchInput.dwFlags & TOUCHEVENTF_UP) { + state = Qt::TouchPointReleased; + } else { + state = (screenPos == touchPoint.screenPos() + ? Qt::TouchPointStationary + : Qt::TouchPointMoved); + } + if (touchInput.dwFlags & TOUCHEVENTF_PRIMARY) + state |= Qt::TouchPointPrimary; + touchPoint.setState(state); + touchPoint.setScreenRect(screenRect); + + touchPoints.append(touchPoint); + } + QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam); + + translateRawTouchEvent(widgetForHwnd, touchPoints); + return true; +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index 942836a..19c5928 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -128,6 +128,12 @@ extern "C" { #include <private/qbackingstore_p.h> +#ifdef QT_RX71_MULTITOUCH +# include <qsocketnotifier.h> +# include <linux/input.h> +# include <errno.h> +#endif + #if _POSIX_VERSION+0 < 200112L && !defined(Q_OS_BSD4) # define QT_NO_UNSETENV #endif @@ -3214,6 +3220,7 @@ int QApplication::x11ProcessEvent(XEvent* event) { Q_D(QApplication); QScopedLoopLevelCounter loopLevelCounter(d->threadData); + #ifdef ALIEN_DEBUG //qDebug() << "QApplication::x11ProcessEvent:" << event->type; #endif @@ -4484,7 +4491,6 @@ bool QETWidget::translateMouseEvent(const XEvent *event) QMouseEvent e(type, pos, globalPos, button, buttons, modifiers); QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down, qt_last_mouse_receiver); - if (type == QEvent::MouseButtonPress && button == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) { @@ -5098,6 +5104,7 @@ bool QETWidget::translatePropertyEvent(const XEvent *event) return true; } + // // Paint event translation // @@ -5999,4 +6006,225 @@ void QSessionManager::requestPhase2() #endif // QT_NO_SESSIONMANAGER +#if defined(QT_RX71_MULTITOUCH) + +static inline int testBit(const char *array, int bit) +{ + return (array[bit/8] & (1<<(bit%8))); +} + +static int openRX71Device(const QByteArray &deviceName) +{ + int fd = open(deviceName, O_RDONLY | O_NONBLOCK); + if (fd == -1) { + fd = -errno; + return fd; + } + + // fetch the event type mask and check that the device reports absolute coordinates + char eventTypeMask[(EV_MAX + sizeof(char) - 1) * sizeof(char) + 1]; + memset(eventTypeMask, 0, sizeof(eventTypeMask)); + if (ioctl(fd, EVIOCGBIT(0, sizeof(eventTypeMask)), eventTypeMask) < 0) { + close(fd); + return -1; + } + if (!testBit(eventTypeMask, EV_ABS)) { + close(fd); + return -1; + } + + // make sure that we can get the absolute X and Y positions from the device + char absMask[(ABS_MAX + sizeof(char) - 1) * sizeof(char) + 1]; + memset(absMask, 0, sizeof(absMask)); + if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absMask)), absMask) < 0) { + close(fd); + return -1; + } + if (!testBit(absMask, ABS_X) || !testBit(absMask, ABS_Y)) { + close(fd); + return -1; + } + + return fd; +} + +void QApplicationPrivate::initializeMultitouch_sys() +{ + Q_Q(QApplication); + + QByteArray deviceName = QByteArray("/dev/input/event"); + int currentDeviceNumber = 0; + for (;;) { + int fd = openRX71Device(QByteArray(deviceName + QByteArray::number(currentDeviceNumber++))); + if (fd == -ENOENT) { + // no more devices + break; + } + if (fd < 0) { + // not a touch device + continue; + } + + struct input_absinfo abs_x, abs_y, abs_z; + ioctl(fd, EVIOCGABS(ABS_X), &abs_x); + ioctl(fd, EVIOCGABS(ABS_Y), &abs_y); + ioctl(fd, EVIOCGABS(ABS_Z), &abs_z); + + int deviceNumber = allRX71TouchPoints.count(); + + QSocketNotifier *socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, q); + QObject::connect(socketNotifier, SIGNAL(activated(int)), q, SLOT(_q_readRX71MultiTouchEvents())); + + RX71TouchPointState touchPointState = { + socketNotifier, + QTouchEvent::TouchPoint(deviceNumber), + + abs_x.minimum, abs_x.maximum, q->desktop()->screenGeometry().width(), + abs_y.minimum, abs_y.maximum, q->desktop()->screenGeometry().height(), + abs_z.minimum, abs_z.maximum + }; + allRX71TouchPoints.append(touchPointState); + } + + hasRX71MultiTouch = allRX71TouchPoints.count() > 1; + if (!hasRX71MultiTouch) { + for (int i = 0; i < allRX71TouchPoints.count(); ++i) { + QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier; + close(socketNotifier->socket()); + delete socketNotifier; + } + allRX71TouchPoints.clear(); + } +} + +void QApplicationPrivate::cleanupMultitouch_sys() +{ + hasRX71MultiTouch = false; + for (int i = 0; i < allRX71TouchPoints.count(); ++i) { + QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier; + close(socketNotifier->socket()); + delete socketNotifier; + } + allRX71TouchPoints.clear(); +} + +bool QApplicationPrivate::readRX71MultiTouchEvents(int deviceNumber) +{ + RX71TouchPointState &touchPointState = allRX71TouchPoints[deviceNumber]; + QSocketNotifier *socketNotifier = touchPointState.socketNotifier; + int fd = socketNotifier->socket(); + + QTouchEvent::TouchPoint &touchPoint = touchPointState.touchPoint; + + bool down = touchPoint.state() != Qt::TouchPointReleased; + if (down) + touchPoint.setState(Qt::TouchPointStationary); + + bool changed = false; + for (;;) { + struct input_event inputEvent; + int bytesRead = read(fd, &inputEvent, sizeof(inputEvent)); + if (bytesRead <= 0) + break; + if (bytesRead != sizeof(inputEvent)) { + qWarning("Qt: INTERNAL ERROR: short read in readRX71MultiTouchEvents()"); + return false; + } + + switch (inputEvent.type) { + case EV_SYN: + changed = true; + switch (touchPoint.state()) { + case Qt::TouchPointPressed: + case Qt::TouchPointReleased: + // make sure we don't compress pressed and releases with any other events + return changed; + default: + break; + } + continue; + case EV_KEY: + case EV_ABS: + break; + default: + qWarning("Qt: WARNING: unknown event type %d on multitouch device", inputEvent.type); + continue; + } + + QPointF screenPos = touchPoint.screenPos(); + switch (inputEvent.code) { + case BTN_TOUCH: + if (!down && inputEvent.value != 0) + touchPoint.setState(Qt::TouchPointPressed); + else if (down && inputEvent.value == 0) + touchPoint.setState(Qt::TouchPointReleased); + break; + case ABS_TOOL_WIDTH: + case ABS_VOLUME: + case ABS_PRESSURE: + // ignore for now + break; + case ABS_X: + { + qreal newValue = ((qreal(inputEvent.value - touchPointState.minX) + / qreal(touchPointState.maxX - touchPointState.minX)) + * touchPointState.scaleX); + screenPos.rx() = newValue; + touchPoint.setScreenPos(screenPos); + break; + } + case ABS_Y: + { + qreal newValue = ((qreal(inputEvent.value - touchPointState.minY) + / qreal(touchPointState.maxY - touchPointState.minY)) + * touchPointState.scaleY); + screenPos.ry() = newValue; + touchPoint.setScreenPos(screenPos); + break; + } + case ABS_Z: + { + // map Z (signal strength) to pressure for now + qreal newValue = (qreal(inputEvent.value - touchPointState.minZ) + / qreal(touchPointState.maxZ - touchPointState.minZ)); + touchPoint.setPressure(newValue); + break; + } + default: + qWarning("Qt: WARNING: unknown event code %d on multitouch device", inputEvent.code); + continue; + } + } + + if (down && touchPoint.state() != Qt::TouchPointReleased) + touchPoint.setState(changed ? Qt::TouchPointMoved : Qt::TouchPointStationary); + + return changed; +} + +void QApplicationPrivate::_q_readRX71MultiTouchEvents() +{ + // read touch events from all devices + bool changed = false; + for (int i = 0; i < allRX71TouchPoints.count(); ++i) + changed = readRX71MultiTouchEvents(i) || changed; + if (!changed) + return; + + QList<QTouchEvent::TouchPoint> touchPoints; + for (int i = 0; i < allRX71TouchPoints.count(); ++i) + touchPoints.append(allRX71TouchPoints.at(i).touchPoint); + + translateRawTouchEvent(0, touchPoints); +} + +#else // !QT_RX71_MULTITOUCH + +void QApplicationPrivate::initializeMultitouch_sys() +{ } +void QApplicationPrivate::cleanupMultitouch_sys() +{ } + +#endif // QT_RX71_MULTITOUCH + QT_END_NAMESPACE diff --git a/src/gui/kernel/qdirectionrecognizer.cpp b/src/gui/kernel/qdirectionrecognizer.cpp new file mode 100644 index 0000000..2896523 --- /dev/null +++ b/src/gui/kernel/qdirectionrecognizer.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdirectionrecognizer_p.h" + +#include <math.h> + +QT_BEGIN_NAMESPACE + +static const int SIZE = 20; + +QDirectionSimpleRecognizer::QDirectionSimpleRecognizer() +{ +} + +Direction QDirectionSimpleRecognizer::addPosition(const QPoint &pos) +{ + if (!directions.isEmpty()) { + const QPoint tmp = pos - directions.back().point; + if (tmp.manhattanLength() < 5) + return Direction(); + } + if (lastPoint.isNull()) { + lastPoint = pos; + return Direction(); + } + int dx = pos.x() - lastPoint.x(); + int dy = pos.y() - lastPoint.y(); + Qt::DirectionType direction = Qt::NoDirection; + if (dx < 0) { + if (-1*dx >= SIZE/2) + direction = Qt::LeftDirection; + } else { + if (dx >= SIZE/2) + direction = Qt::RightDirection; + } + if (dy < 0) { + if (-1*dy >= SIZE/2) + direction = Qt::UpDirection; + } else { + if (dy >= SIZE/2) + direction = Qt::DownDirection; + } + if (direction == Qt::NoDirection) + return Direction(); + + lastPoint = pos; + directions.push_back(Direction(direction, pos)); + return Direction(direction, pos); +} + + +DirectionList QDirectionSimpleRecognizer::getDirections() const +{ + return directions; +} + +void QDirectionSimpleRecognizer::reset() +{ + directions.clear(); + lastPoint = QPoint(); +} + + +/// QDirectionDiagonalRecognizer + +QDirectionDiagonalRecognizer::QDirectionDiagonalRecognizer() +{ +} + +Direction QDirectionDiagonalRecognizer::addPosition(const QPoint &pos) +{ + if (!directions.isEmpty()) { + const QPoint tmp = pos - directions.back().point; + if (tmp.manhattanLength() < 5) + return Direction(); + } + if (lastPoint.isNull()) { + lastPoint = pos; + return Direction(); + } + int dx = pos.x() - lastPoint.x(); + int dy = pos.y() - lastPoint.y(); + int distance = sqrt(static_cast<double>(dx*dx + dy*dy)); + if (distance < SIZE/2) + return Direction(); + + Qt::DirectionType direction = Qt::NoDirection; + double angle = atan(1.0*qAbs(lastPoint.y() - pos.y())/qAbs(pos.x() - lastPoint.x())) * 180. / M_PI; + if (dx < 0 && dy <= 0) { + angle = 180 - angle; + } else if (dx <= 0 && dy > 0) { + angle += 180; + } else if (dx > 0 && dy > 0) { + angle = 360-angle; + } + if (angle < 0) + angle += 360; + if (angle <= 20) + direction = Qt::RightDirection; + else if (angle <= 65) + direction = Qt::RightUpDirection; + else if (angle <= 110) + direction = Qt::UpDirection; + else if (angle <= 155) + direction = Qt::LeftUpDirection; + else if (angle <= 200) + direction = Qt::LeftDirection; + else if (angle <= 245) + direction = Qt::LeftDownDirection; + else if (angle <= 290) + direction = Qt::DownDirection; + else if (angle <= 335) + direction = Qt::RightDownDirection; + else + direction = Qt::RightDirection; + + if (direction == Qt::NoDirection) + return Direction(); + + lastPoint = pos; + directions.push_back(Direction(direction, pos)); + return Direction(direction, pos); +} + + +DirectionList QDirectionDiagonalRecognizer::getDirections() const +{ + return directions; +} + +void QDirectionDiagonalRecognizer::reset() +{ + directions.clear(); + lastPoint = QPoint(); +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qdirectionrecognizer_p.h b/src/gui/kernel/qdirectionrecognizer_p.h new file mode 100644 index 0000000..ca3d1c1 --- /dev/null +++ b/src/gui/kernel/qdirectionrecognizer_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECTIONRECOGNIZER_P_H +#define QDIRECTIONRECOGNIZER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpoint.h" +#include "qlist.h" + +QT_BEGIN_NAMESPACE + +struct Direction +{ + Qt::DirectionType direction; + QPoint point; + + Direction(Qt::DirectionType dir, const QPoint &pt) + : direction(dir), point(pt) { } + Direction() + : direction(Qt::NoDirection) { } + + inline bool isEmpty() const { return direction == Qt::NoDirection; } + inline bool isNull() const { return direction == Qt::NoDirection; } +}; + +typedef QList<Direction> DirectionList; + +class QDirectionSimpleRecognizer +{ +public: + QDirectionSimpleRecognizer(); + Direction addPosition(const QPoint &pos); + DirectionList getDirections() const; + void reset(); + +private: + QPoint lastPoint; + DirectionList directions; +}; + +class QDirectionDiagonalRecognizer +{ +public: + QDirectionDiagonalRecognizer(); + Direction addPosition(const QPoint &pos); + DirectionList getDirections() const; + void reset(); + +private: + QPoint lastPoint; + DirectionList directions; +}; + +QT_END_NAMESPACE + +#endif // QDIRECTIONRECOGNIZER_P_H diff --git a/src/gui/kernel/qdirectionsimplificator_p.h b/src/gui/kernel/qdirectionsimplificator_p.h new file mode 100644 index 0000000..7b71ca0 --- /dev/null +++ b/src/gui/kernel/qdirectionsimplificator_p.h @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDIRECTIONSIMPLIFICATOR_P_H +#define QDIRECTIONSIMPLIFICATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qdirectionrecognizer_p.h" + +QT_BEGIN_NAMESPACE + +class QDirectionSimplificator +{ +public: + QDirectionSimplificator(const DirectionList &dir); + + bool simplify(DirectionList *result); + +private: + DirectionList directions; + DirectionList lastResult; + enum State { + None, + Trim, // remove first and last element + AccidentalMoves, // 66866 => 6666 + ComplexAccidentalMoves, // 778788 => 777888 (swapping elements without changing direction) + ShortMoves, // (moves of length 1) + } state; + + struct SimplifyTrim + { + SimplifyTrim() : state(0) { } + bool operator()(DirectionList &directions) + { + if (state == 0) { + directions.removeFirst(); + state = 1; + } else if (state == 1) { + directions.removeLast(); + state = 2; + } else if (state == 2 && directions.size() >= 2) { + directions.removeFirst(); + directions.removeLast(); + state = 3; + } else { + return false; + } + return true; + } + int state; + }; + struct SimplifyAccidentalMoves + { + SimplifyAccidentalMoves() : state(0) { } + bool operator()(DirectionList &directions) + { + return false; + } + int state; + }; + struct SimplifyComplexAccidentalMoves + { + SimplifyComplexAccidentalMoves() : state(0) { } + bool operator()(DirectionList &directions) + { + return false; + } + int state; + }; + + SimplifyTrim trim; + SimplifyAccidentalMoves accidentalMoves; + SimplifyComplexAccidentalMoves complexAccidentalMoves; + //SimplifyShortMoves shortMoves; +}; + +QDirectionSimplificator::QDirectionSimplificator(const DirectionList &dir) + : directions(dir), state(None) +{ +} + +bool QDirectionSimplificator::simplify(DirectionList *result) +{ + if (directions.isEmpty() || !result) + return false; + *result = directions; + switch(state) { + case None: + state = Trim; + trim = SimplifyTrim(); + case Trim: + if (trim(*result)) + break; + *result = lastResult; + state = AccidentalMoves; + accidentalMoves = SimplifyAccidentalMoves(); + case AccidentalMoves: + if (accidentalMoves(*result)) + break; + *result = lastResult; + state = ComplexAccidentalMoves; + complexAccidentalMoves = SimplifyComplexAccidentalMoves(); + case ComplexAccidentalMoves: + if (complexAccidentalMoves(*result)) + break; + *result = lastResult; + // state = ShortMoves; + // shortMoves = SimplifyShortMoves(); + // case ShortMoves: + // if (shortMoves(*result)) + // break; + // state = None; + default: + return false; + } + lastResult = *result; + if (lastResult.isEmpty()) + return false; + return true; +} + +QT_END_NAMESPACE + +#endif // QDIRECTIONSIMPLIFICATOR_P_H diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 12bbc7d..c9b9bc8 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -52,6 +52,8 @@ QT_BEGIN_NAMESPACE +QString qt_getStandardGestureTypeName(Qt::GestureType type); + /*! \class QInputEvent \ingroup events @@ -83,6 +85,13 @@ QInputEvent::~QInputEvent() \sa QApplication::keyboardModifiers() */ +/*! \fn void QInputEvent::setModifiers(Qt::KeyboardModifiers modifiers) + + \internal + + Sets the keyboard modifiers flags for this event. +*/ + /*! \class QMouseEvent \ingroup events @@ -3322,6 +3331,9 @@ QDebug operator<<(QDebug dbg, const QEvent *e) { case QEvent::ChildRemoved: n = n ? n : "ChildRemoved"; dbg.nospace() << "QChildEvent(" << n << ", " << (static_cast<const QChildEvent*>(e))->child(); return dbg.space(); + case QEvent::Gesture: + n = "Gesture"; + break; default: dbg.nospace() << "QEvent(" << (const void *)e << ", type = " << e->type() << ')'; return dbg.space(); @@ -3518,4 +3530,563 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) #endif +/*! + \class QGestureEvent + \since 4.6 + \ingroup events + + \brief The QGestureEvent class provides the parameters used for + gesture recognition. + + The QGestureEvent class contains a list of gestures that are being + executed right now (\l{QGestureEvent::}{gestureTypes()}) and a + list of gestures that are cancelled (the gesture might be + cancelled because the window lost focus, or because of timeout, + etc). + + \sa QGesture +*/ + +/*! + Creates new QGestureEvent containing a list of \a gestures that + are being executed and a list of gesture that were cancelled (\a + cancelledGestures). +*/ +QGestureEvent::QGestureEvent(const QSet<QGesture*> &gestures, + const QSet<QString> &cancelledGestures) + : QEvent(QEvent::Gesture), m_cancelledGestures(cancelledGestures) +{ + setAccepted(false); + foreach(QGesture *r, gestures) + m_gestures.insert(r->type(), r); +} + +/*! + Destroys the QGestureEvent object. +*/ +QGestureEvent::~QGestureEvent() +{ +} + +/*! + Returns true if the gesture event contains gesture of specific \a + type; returns false otherwise. +*/ +bool QGestureEvent::contains(Qt::GestureType type) const +{ + return contains(qt_getStandardGestureTypeName(type)); +} + +/*! + Returns true if the gesture event contains gesture of specific \a + type; returns false otherwise. +*/ +bool QGestureEvent::contains(const QString &type) const +{ + return gesture(type) != 0; +} + +/*! + Returns a list of gesture names that this event contains. +*/ +QList<QString> QGestureEvent::gestureTypes() const +{ + return m_gestures.keys(); +} + +/*! + Returns extended information about a gesture of specific \a type. +*/ +const QGesture* QGestureEvent::gesture(Qt::GestureType type) const +{ + return gesture(qt_getStandardGestureTypeName(type)); +} + +/*! + Returns extended information about a gesture of specific \a type. +*/ +const QGesture* QGestureEvent::gesture(const QString &type) const +{ + return m_gestures.value(type, 0); +} + +/*! + Returns extended information about all gestures in the event. +*/ +QList<QGesture*> QGestureEvent::gestures() const +{ + return m_gestures.values(); +} + +/*! + Returns a set of gesture names that used to be executed, but were + cancelled (i.e. they were not finished properly). +*/ +QSet<QString> QGestureEvent::cancelledGestures() const +{ + return m_cancelledGestures; +} + +/*! + Sets the accept flag of the all gestures inside the event object, + the equivalent of calling \l{QEvent::accept()}{accept()} or + \l{QEvent::setAccepted()}{setAccepted(true)}. + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGestureEvent::acceptAll() +{ + QHash<QString, QGesture*>::iterator it = m_gestures.begin(), + e = m_gestures.end(); + for(; it != e; ++it) + it.value()->accept(); + setAccepted(true); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGestureEvent::accept(Qt::GestureType type) +{ + if (QGesture *g = m_gestures.value(qt_getStandardGestureTypeName(type), 0)) + g->accept(); +} + +/*! + Sets the accept flag of the specified gesture inside the event + object, the equivalent of calling + \l{QGestureEvent::gesture()}{gesture(type)}->\l{QGesture::accept()}{accept()} + + Setting the accept parameter indicates that the event receiver + wants the gesture. Unwanted gestures might be propagated to the parent + widget. +*/ +void QGestureEvent::accept(const QString &type) +{ + if (QGesture *g = m_gestures.value(type, 0)) + g->accept(); +} + +/*! \class QTouchEvent + \brief The QTouchEvent class contains parameters that describe a touch event +. + \since 4.6 + \ingroup events + + Touch events occur when pressing, releasing, or moving one or more + touch points on a touch device (such as a touch-screen or + track-pad), and if the widget has the Qt::WA_AcceptTouchEvents + attribute. + + All touch events are of type QEvent::TouchBegin, + QEvent::TouchUpdate, or QEvent::TouchEnd. The touchPoints() + function returns a list of all touch points contained in the event. + Information about each touch point can be retreived using the + QTouchEvent::TouchPoint class. + + Similar to QMouseEvent, Qt automatically grabs each touch point on + the first press inside a widget; the widget will receive all + updates for the touch point until it is released. Note that it is + possible for a widget to receive events for multiple touch points, + and that multiple widgets may be receiving touch events at the same + time. + + A touch event contains a special accept flag that indicates + whether the receiver wants the event. By default, the event is + accepted. You should call ignore() if the touch event is not handled by + your widget. A QEvent::TouchBegin event is propagated up the parent widget + chain until a widget accepts it with accept(), or an event filter + consumes it. If the QEvent::TouchBegin event is neither accepted nor consumed, + then mouse events are simulated from the state of the first touch + point. + + The Qt::TouchPointState enum describes the different states that a + touch point may have. + + QTouchEvent::TouchPoint Qt::TouchPointState Qt::WA_AcceptTouchEvents +*/ + +/*! \enum Qt::TouchPointState + \since 4.6 + + This enum represents the state of a touch point at the time the + QTouchEvent occurred. + + \value TouchPointPressed The touch point is now pressed. + \value TouchPointMoved The touch point moved. + \value TouchPointStationary The touch point did not move. + \value TouchPointReleased The touch point was released. + + \omitvalue TouchPointStateMask + \omitvalue TouchPointPrimary +*/ + +/*! \class QTouchEvent::TouchPoint + \brief The QTouchEvent::TouchPoint class provide information about a touch point in a QTouchEvent. + \since 4.6 +*/ + +/*! + Constructs a QTouchEvent with the given \a type and \a + touchPoints. The \a modifiers are the current keyboard modifiers + at the time of the event. +*/ +QTouchEvent::QTouchEvent(QEvent::Type type, + Qt::KeyboardModifiers modifiers, + Qt::TouchPointStates touchPointStates, + const QList<QTouchEvent::TouchPoint> &touchPoints) + : QInputEvent(type, modifiers), + _touchPointStates(touchPointStates), + _touchPoints(touchPoints) +{ } + +/*! + Destroys the QTouchEvent. +*/ +QTouchEvent::~QTouchEvent() +{ } + +/*! \fn QWidget *QTouchEvent::widget() const + + Returns the widget on which the event occurred. +*/ + + +/*! \fn Qt::TouchPointStates QTouchEvent::touchPointStates() const + + Returns a bitwise OR of all the touch point states for this event. +*/ + +/*! \fn const QList<QTouchEvent::TouchPoint> &QTouchEvent::touchPoints() const + + Returns the list of touch points contained in the touch event. +*/ + +/*! \fn void QTouchEvent::setWidget(QWidget *widget) + + \internal + + Sets the widget for this event. +*/ + +/*! \fn void QTouchEvent::setTouchPointStates(Qt::TouchPointStates touchPointStates) + + \internal + + Sets a bitwise OR of all the touch point states for this event. +*/ + +/*! \fn void QTouchEvent::setTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints) + + \internal + + Sets the list of touch points for this event. +*/ + +/*! \internal + + Constructs a QTouchEvent::TouchPoint for use in a QTouchEvent. +*/ +QTouchEvent::TouchPoint::TouchPoint(int id) + : d(new QTouchEventTouchPointPrivate(id)) +{ } + +/*! \internal + + Constructs a copy of \a other. +*/ +QTouchEvent::TouchPoint::TouchPoint(const QTouchEvent::TouchPoint &other) + : d(other.d) +{ + d->ref.ref(); +} + +/*! \internal + + Destroys the QTouchEvent::TouchPoint. +*/ +QTouchEvent::TouchPoint::~TouchPoint() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + Returns the id number of this touch point. + + Id numbers are globally sequential, starting at zero, meaning the + first touch point in the application has id 0, the second has id 1, + and so on. +*/ +int QTouchEvent::TouchPoint::id() const +{ + return d->id; +} + +/*! + Returns the current state of this touch point. +*/ +Qt::TouchPointState QTouchEvent::TouchPoint::state() const +{ + return Qt::TouchPointState(int(d->state) & Qt::TouchPointStateMask); +} + +/*! + Returns true if this touch point is the primary touch point. The primary touch point is the + point for which the windowing system generates mouse events. +*/ +bool QTouchEvent::TouchPoint::isPrimary() const +{ + return (d->state & Qt::TouchPointPrimary) != 0; +} + +/*! + Returns the position of this touch point, relative to the widget + or item that received the event. +*/ +QPointF QTouchEvent::TouchPoint::pos() const +{ + return d->rect.center(); +} + +/*! + Returns the scene position of this touch point. +*/ +QPointF QTouchEvent::TouchPoint::scenePos() const +{ + return d->sceneRect.center(); +} + +/*! + Returns the screen position of this touch point. +*/ +QPointF QTouchEvent::TouchPoint::screenPos() const +{ + return d->screenRect.center(); +} + +/*! + Returns the starting position of this touch point, relative to the + widget that received the event. +*/ +QPointF QTouchEvent::TouchPoint::startPos() const +{ + return d->startPos; +} + +/*! + Returns the starting scene position of this touch point. +*/ +QPointF QTouchEvent::TouchPoint::startScenePos() const +{ + return d->startScenePos; +} + +/*! + Returns the starting screen position of this touch point. +*/ +QPointF QTouchEvent::TouchPoint::startScreenPos() const +{ + return d->startScreenPos; +} + +/*! + Returns the position of this touch point from the previous touch + event, relative to the widget that received the event. +*/ +QPointF QTouchEvent::TouchPoint::lastPos() const +{ + return d->lastPos; +} + +/*! + Returns the scene position of this touch point from the previous + touch event. +*/ +QPointF QTouchEvent::TouchPoint::lastScenePos() const +{ + return d->lastScenePos; +} + +/*! + Returns the screen position of this touch point from the previous + touch event. +*/ +QPointF QTouchEvent::TouchPoint::lastScreenPos() const +{ + return d->lastScreenPos; +} + +/*! + Returns the rect for this touch point. The rect is centered around the point returned by pos(). + Note this function returns an empty rect if the device does not report touch point sizes. +*/ +QRectF QTouchEvent::TouchPoint::rect() const +{ + return d->screenRect; +} + +/*! + Returns the rect for this touch point in scene coordinates. +*/ +QRectF QTouchEvent::TouchPoint::sceneRect() const +{ + return d->sceneRect; +} + +/*! + Returns the rect for this touch point in screen coordinates. +*/ +QRectF QTouchEvent::TouchPoint::screenRect() const +{ + return d->screenRect; +} + +/*! + Returns the pressure of this touch point. The return value is in + the range 0.0 to 1.0. +*/ +qreal QTouchEvent::TouchPoint::pressure() const +{ + return d->pressure; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setId(int id) +{ + if (d->ref != 1) + d = d->detach(); + d->id = id; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setState(Qt::TouchPointStates state) +{ + if (d->ref != 1) + d = d->detach(); + d->state = state; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setPos(const QPointF &pos) +{ + if (d->ref != 1) + d = d->detach(); + d->rect.moveCenter(pos); +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setScenePos(const QPointF &scenePos) +{ + if (d->ref != 1) + d = d->detach(); + d->sceneRect.moveCenter(scenePos); +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos) +{ + if (d->ref != 1) + d = d->detach(); + d->screenRect.moveCenter(screenPos); +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setStartPos(const QPointF &startPos) +{ + if (d->ref != 1) + d = d->detach(); + d->startPos = startPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setStartScenePos(const QPointF &startScenePos) +{ + if (d->ref != 1) + d = d->detach(); + d->startScenePos = startScenePos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setStartScreenPos(const QPointF &startScreenPos) +{ + if (d->ref != 1) + d = d->detach(); + d->startScreenPos = startScreenPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setLastPos(const QPointF &lastPos) +{ + if (d->ref != 1) + d = d->detach(); + d->lastPos = lastPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setLastScenePos(const QPointF &lastScenePos) +{ + if (d->ref != 1) + d = d->detach(); + d->lastScenePos = lastScenePos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setLastScreenPos(const QPointF &lastScreenPos) +{ + if (d->ref != 1) + d = d->detach(); + d->lastScreenPos = lastScreenPos; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setRect(const QRectF &rect) +{ + if (d->ref != 1) + d = d->detach(); + d->rect = rect; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setSceneRect(const QRectF &sceneRect) +{ + if (d->ref != 1) + d = d->detach(); + d->sceneRect = sceneRect; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect) +{ + if (d->ref != 1) + d = d->detach(); + d->screenRect = screenRect; +} + +/*! \internal */ +void QTouchEvent::TouchPoint::setPressure(qreal pressure) +{ + if (d->ref != 1) + d = d->detach(); + d->pressure = pressure; +} + +/*! \internal */ +QTouchEvent::TouchPoint &QTouchEvent::TouchPoint::operator=(const QTouchEvent::TouchPoint &other) +{ + other.d->ref.ref(); + if (!d->ref.deref()) + delete d; + d = other.d; + return *this; +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index c2cb05f..ee81e78 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -52,6 +52,9 @@ #include <QtGui/qmime.h> #include <QtGui/qdrag.h> #include <QtCore/qvariant.h> +#include <QtCore/qmap.h> +#include <QtCore/qset.h> +#include <QtGui/qgesture.h> QT_BEGIN_HEADER @@ -67,6 +70,7 @@ public: QInputEvent(Type type, Qt::KeyboardModifiers modifiers = Qt::NoModifier); ~QInputEvent(); inline Qt::KeyboardModifiers modifiers() const { return modState; } + inline void setModifiers(Qt::KeyboardModifiers modifiers) { modState = modifiers; } protected: Qt::KeyboardModifiers modState; }; @@ -710,6 +714,41 @@ private: }; #endif +class Q_GUI_EXPORT QGestureEvent : public QEvent +{ +public: + QGestureEvent(const QSet<QGesture*> &gestures, + const QSet<QString> &cancelledGestures = QSet<QString>()); + ~QGestureEvent(); + + bool contains(Qt::GestureType type) const; + bool contains(const QString &type) const; + + QList<QString> gestureTypes() const; + + const QGesture* gesture(Qt::GestureType type) const; + const QGesture* gesture(const QString &type) const; + QList<QGesture*> gestures() const; + + QSet<QString> cancelledGestures() const; + + void acceptAll(); +#ifndef Q_NO_USING_KEYWORD + using QEvent::accept; +#else + inline void accept() { QEvent::accept(); } +#endif + void accept(Qt::GestureType type); + void accept(const QString &type); + +protected: + QHash<QString, QGesture*> m_gestures; + QSet<QString> m_cancelledGestures; + + friend class QApplication; + friend class QGestureManager; +}; + #ifndef QT_NO_DEBUG_STREAM Q_GUI_EXPORT QDebug operator<<(QDebug, const QEvent *); #endif @@ -719,6 +758,85 @@ inline bool operator==(QKeyEvent *e, QKeySequence::StandardKey key){return (e ? inline bool operator==(QKeySequence::StandardKey key, QKeyEvent *e){return (e ? e->matches(key) : false);} #endif // QT_NO_SHORTCUT +class QTouchEventTouchPointPrivate; +class Q_GUI_EXPORT QTouchEvent : public QInputEvent +{ +public: + class Q_GUI_EXPORT TouchPoint + { + public: + TouchPoint(int id = -1); + TouchPoint(const TouchPoint &other); + ~TouchPoint(); + + int id() const; + + Qt::TouchPointState state() const; + bool isPrimary() const; + + QPointF pos() const; + QPointF startPos() const; + QPointF lastPos() const; + + QPointF scenePos() const; + QPointF startScenePos() const; + QPointF lastScenePos() const; + + QPointF screenPos() const; + QPointF startScreenPos() const; + QPointF lastScreenPos() const; + + QRectF rect() const; + QRectF sceneRect() const; + QRectF screenRect() const; + + qreal pressure() const; + + // internal + void setId(int id); + void setState(Qt::TouchPointStates state); + void setPos(const QPointF &pos); + void setScenePos(const QPointF &scenePos); + void setScreenPos(const QPointF &screenPos); + void setStartPos(const QPointF &startPos); + void setStartScenePos(const QPointF &startScenePos); + void setStartScreenPos(const QPointF &startScreenPos); + void setLastPos(const QPointF &lastPos); + void setLastScenePos(const QPointF &lastScenePos); + void setLastScreenPos(const QPointF &lastScreenPos); + void setRect(const QRectF &rect); + void setSceneRect(const QRectF &sceneRect); + void setScreenRect(const QRectF &screenRect); + void setPressure(qreal pressure); + TouchPoint &operator=(const TouchPoint &other); + + private: + QTouchEventTouchPointPrivate *d; + }; + + QTouchEvent(QEvent::Type type, + Qt::KeyboardModifiers modifiers = Qt::NoModifier, + Qt::TouchPointStates touchPointStates = 0, + const QList<QTouchEvent::TouchPoint> &touchPoints = QList<QTouchEvent::TouchPoint>()); + ~QTouchEvent(); + + inline QWidget *widget() const { return _widget; } + inline Qt::TouchPointStates touchPointStates() const { return _touchPointStates; } + inline const QList<QTouchEvent::TouchPoint> &touchPoints() const { return _touchPoints; } + + // internal + inline void setWidget(QWidget *widget) { _widget = widget; } + inline void setTouchPointStates(Qt::TouchPointStates touchPointStates) { _touchPointStates = touchPointStates; } + inline void setTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints) { _touchPoints = touchPoints; } + +protected: + QWidget *_widget; + Qt::TouchPointStates _touchPointStates; + QList<QTouchEvent::TouchPoint> _touchPoints; + + friend class QApplicationPrivate; +}; + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index 9cc1e81..53d7a23 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -89,6 +89,34 @@ protected: friend class QMouseEvent; }; +class QTouchEventTouchPointPrivate +{ +public: + inline QTouchEventTouchPointPrivate(int id) + : ref(1), + id(id), + state(Qt::TouchPointReleased), + pressure(qreal(-1.)) + { } + + inline QTouchEventTouchPointPrivate *detach() + { + QTouchEventTouchPointPrivate *d = new QTouchEventTouchPointPrivate(*this); + d->ref = 1; + if (!this->ref.deref()) + delete this; + return d; + } + + QAtomicInt ref; + int id; + Qt::TouchPointStates state; + QRectF rect, sceneRect, screenRect; + QPointF startPos, startScenePos, startScreenPos; + QPointF lastPos, lastScenePos, lastScreenPos; + qreal pressure; +}; + QT_END_NAMESPACE #endif // QEVENT_P_H diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp new file mode 100644 index 0000000..2bb7de7 --- /dev/null +++ b/src/gui/kernel/qgesture.cpp @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgesture.h" +#include <private/qgesture_p.h> + +QT_BEGIN_NAMESPACE + +QString qt_getStandardGestureTypeName(Qt::GestureType type); + +/*! + \class QGesture + \since 4.6 + + \brief The QGesture class represents a gesture, containing all + properties that describe a gesture. + + The widget receives a QGestureEvent with a list of QGesture + objects that represent gestures that are occuring on it. The class + has a list of properties that can be queried by the user to get + some gesture-specific arguments (i.e. position of the tap in the + DoubleTap gesture). + + When creating custom gesture recognizers, they might add new + properties to the gesture object, or custom gesture developers + might subclass the QGesture objects to provide some extended + information. However, if the gesture developer wants to add a new + property to the gesture object that describe coordinate (like a + QPoint or QRect), it is required to subclass the QGesture and + re-implement the \l{QGesture::}{translate()} function to make sure + the coordinates are translated properly when the gesture event is + propagated to parent widgets. + + \sa QGestureEvent, QGestureRecognizer +*/ + +/*! + Creates a new gesture object of type \a type in a \a state and + marks it as a child of \a parent. + + Usually QGesture objects should only be contructed by the + QGestureRecognizer classes. +*/ +QGesture::QGesture(QObject *parent, const QString &type, Qt::GestureState state) + : QObject(*new QGesturePrivate, parent), m_accept(0) +{ + Q_D(QGesture); + d->type = type; + d->state = state; +} + +/*! + Creates a new gesture object of type \a type in a \a state and + marks it as a child of \a parent. + + This constructor also fills some basic information about the + gesture like a \a startPos which describes the start point of the + gesture, \a lastPos - last point where the gesture happened, \a + pos - a current point, \a rect - a bounding rect of the gesture, + \a hotSpot - a center point of the gesture, \a startTime - a time + when the gesture has started, \a duration - how long the gesture + is going on. + + Usually QGesture objects should only be contructed by the + QGestureRecognizer classes. +*/ +QGesture::QGesture(QObject *parent, const QString &type, const QPoint &startPos, + const QPoint &lastPos, const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration, Qt::GestureState state) + : QObject(*new QGesturePrivate, parent) +{ + Q_D(QGesture); + d->type = type; + d->state = state; + d->init(startPos, lastPos, pos, rect, hotSpot, startTime, duration); +} + +/*! \internal +*/ +QGesture::QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, + Qt::GestureState state) + : QObject(dd, parent) +{ + Q_D(QGesture); + d->type = type; + d->state = state; +} + +/*! + Destroys the gesture object. +*/ +QGesture::~QGesture() +{ +} + +/*! + \property QGesture::type + + \brief The type of the gesture. +*/ +QString QGesture::type() const +{ + return d_func()->type; +} + + +/*! + \property QGesture::state + + \brief The current state of the gesture. +*/ +Qt::GestureState QGesture::state() const +{ + return d_func()->state; +} + +/*! + Translates the internal gesture properties that represent + coordinates by \a offset. + + Custom gesture recognizer developer have to re-implement this + function if they want to store custom properties that represent + coordinates. +*/ +void QGesture::translate(const QPoint &offset) +{ + Q_D(QGesture); + d->rect.translate(offset); + d->hotSpot += offset; + d->startPos += offset; + d->lastPos += offset; + d->pos += offset; +} + +/*! + \property QGesture::rect + + \brief The bounding rect of a gesture. +*/ +QRect QGesture::rect() const +{ + return d_func()->rect; +} + +void QGesture::setRect(const QRect &rect) +{ + d_func()->rect = rect; +} + +/*! + \property QGesture::hotSpot + + \brief The center point of a gesture. +*/ +QPoint QGesture::hotSpot() const +{ + return d_func()->hotSpot; +} + +void QGesture::setHotSpot(const QPoint &point) +{ + d_func()->hotSpot = point; +} + +/*! + \property QGesture::startTime + + \brief The time when the gesture has started. +*/ +QDateTime QGesture::startTime() const +{ + return d_func()->startTime; +} + +/*! + \property QGesture::duration + + \brief The duration time of a gesture. +*/ +uint QGesture::duration() const +{ + return d_func()->duration; +} + +/*! + \property QGesture::startPos + + \brief The start position of the pointer. +*/ +QPoint QGesture::startPos() const +{ + return d_func()->startPos; +} + +void QGesture::setStartPos(const QPoint &point) +{ + d_func()->startPos = point; +} + +/*! + \property QGesture::lastPos + + \brief The last recorded position of the pointer. +*/ +QPoint QGesture::lastPos() const +{ + return d_func()->lastPos; +} + +void QGesture::setLastPos(const QPoint &point) +{ + d_func()->lastPos = point; +} + +/*! + \property QGesture::pos + + \brief The current position of the pointer. +*/ +QPoint QGesture::pos() const +{ + return d_func()->pos; +} + +void QGesture::setPos(const QPoint &point) +{ + d_func()->pos = point; +} + +/*! + \class QPanningGesture + \since 4.6 + + \brief The QPanningGesture class represents a Pan gesture, + providing additional information related to panning. + + This class is provided for convenience, panning direction + information is also contained in the QGesture object in it's + properties. +*/ + +/*! \internal +*/ +QPanningGesture::QPanningGesture(QObject *parent) + : QGesture(*new QPanningGesturePrivate, parent, + qt_getStandardGestureTypeName(Qt::PanGesture)) +{ +} + +/*! \internal +*/ +QPanningGesture::~QPanningGesture() +{ +} + +/*! + \property QPanningGesture::lastDirection + + \brief The last recorded direction of panning. +*/ +Qt::DirectionType QPanningGesture::lastDirection() const +{ + return d_func()->lastDirection; +} + +/*! + \property QPanningGesture::direction + + \brief The current direction of panning. +*/ +Qt::DirectionType QPanningGesture::direction() const +{ + return d_func()->direction; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h new file mode 100644 index 0000000..95486bf --- /dev/null +++ b/src/gui/kernel/qgesture.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURE_H +#define QGESTURE_H + +#include "qobject.h" +#include "qlist.h" +#include "qdatetime.h" +#include "qpoint.h" +#include "qrect.h" +#include "qmetatype.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGesturePrivate; +class Q_GUI_EXPORT QGesture : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGesture) + + Q_PROPERTY(QString type READ type) + Q_PROPERTY(Qt::GestureState state READ state) + + Q_PROPERTY(QDateTime startTime READ startTime) + Q_PROPERTY(uint duration READ duration) + + Q_PROPERTY(QRect rect READ rect WRITE setRect) + Q_PROPERTY(QPoint hotSpot READ hotSpot WRITE setHotSpot) + Q_PROPERTY(QPoint startPos READ startPos WRITE setStartPos) + Q_PROPERTY(QPoint lastPos READ lastPos WRITE setLastPos) + Q_PROPERTY(QPoint pos READ pos WRITE setPos) + +public: + QGesture(QObject *parent, const QString &type, + Qt::GestureState state = Qt::GestureStarted); + QGesture(QObject *parent, + const QString &type, const QPoint &startPos, + const QPoint &lastPos, const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration, Qt::GestureState state); + virtual ~QGesture(); + + inline void setAccepted(bool accepted) { m_accept = accepted; } + inline bool isAccepted() const { return m_accept; } + + inline void accept() { m_accept = true; } + inline void ignore() { m_accept = false; } + + QString type() const; + Qt::GestureState state() const; + + QDateTime startTime() const; + uint duration() const; + + QRect rect() const; + void setRect(const QRect &rect); + QPoint hotSpot() const; + void setHotSpot(const QPoint &point); + + QPoint startPos() const; + void setStartPos(const QPoint &point); + QPoint lastPos() const; + void setLastPos(const QPoint &point); + QPoint pos() const; + void setPos(const QPoint &point); + +protected: + QGesture(QGesturePrivate &dd, QObject *parent, const QString &type, + Qt::GestureState state = Qt::GestureStarted); + virtual void translate(const QPoint &offset); + +private: + ushort m_accept : 1; + + friend class QGestureManager; + friend class QApplication; + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; + friend class QGestureRecognizerPan; + friend class QDoubleTapGestureRecognizer; + friend class QTapAndHoldGestureRecognizer; +}; + +class QPanningGesturePrivate; +class Q_GUI_EXPORT QPanningGesture : public QGesture +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPanningGesture) + + Q_PROPERTY(Qt::DirectionType lastDirection READ lastDirection) + Q_PROPERTY(Qt::DirectionType direction READ direction) + +public: + Qt::DirectionType lastDirection() const; + Qt::DirectionType direction() const; + +private: + QPanningGesture(QObject *parent = 0); + ~QPanningGesture(); + + friend class QGestureRecognizerPan; +}; + +Q_DECLARE_METATYPE(Qt::DirectionType) +Q_DECLARE_METATYPE(Qt::GestureState) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGESTURE_H diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h new file mode 100644 index 0000000..0f2e791 --- /dev/null +++ b/src/gui/kernel/qgesture_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURE_P_H +#define QGESTURE_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 "qrect.h" +#include "qpoint.h" +#include "qdatetime.h" +#include "private/qobject_p.h" + +QT_BEGIN_NAMESPACE + +class QGraphicsItem; +class QGesturePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGesture) + +public: + QGesturePrivate() + : state(Qt::NoGesture), graphicsItem(0), singleshot(0), duration(0) { } + + void init(const QPoint &startPos, const QPoint &lastPos, + const QPoint &pos, const QRect &rect, + const QPoint &hotSpot, const QDateTime &startTime, + uint duration) + { + this->rect = rect; + this->hotSpot = hotSpot; + this->startTime = startTime; + this->duration = duration; + this->startPos = startPos; + this->lastPos = lastPos; + this->pos = pos; + } + + QString type; + Qt::GestureState state; + + QPointer<QWidget> widget; + QGraphicsItem *graphicsItem; + uint singleshot:1; + + QRect rect; + QPoint hotSpot; + QDateTime startTime; + uint duration; + QPoint startPos; + QPoint lastPos; + QPoint pos; +}; + +class QPanningGesturePrivate : public QGesturePrivate +{ + Q_DECLARE_PUBLIC(QPanningGesture) + +public: + Qt::DirectionType lastDirection; + Qt::DirectionType direction; +}; + +QT_END_NAMESPACE + +#endif // QGESTURE_P_H diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp new file mode 100644 index 0000000..a1d55fa --- /dev/null +++ b/src/gui/kernel/qgesturemanager.cpp @@ -0,0 +1,644 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgesturemanager_p.h" +#include "qgesture.h" +#include "qgesture_p.h" +#include "qevent.h" + +#include "qapplication.h" +#include "qapplication_p.h" +#include "qwidget.h" +#include "qwidget_p.h" + +#include "qgesturestandardrecognizers_p.h" + +#include "qdebug.h" + +// #define GESTURE_DEBUG +#ifndef GESTURE_DEBUG +# define DEBUG if (0) qDebug +#else +# define DEBUG qDebug +#endif + +QT_BEGIN_NAMESPACE + +bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); + +static const unsigned int MaximumGestureRecognitionTimeout = 2000; + +QGestureManager::QGestureManager(QObject *parent) + : QObject(parent), eventDeliveryDelayTimeout(300), + delayedPressTimer(0), lastMousePressReceiver(0), lastMousePressEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0), + lastGestureId(0), state(NotGesture) +{ + qRegisterMetaType<Qt::DirectionType>(); + qRegisterMetaType<Qt::GestureState>(); + + recognizers << new QDoubleTapGestureRecognizer(this); + recognizers << new QTapAndHoldGestureRecognizer(this); + recognizers << new QGestureRecognizerPan(this); + + foreach(QGestureRecognizer *r, recognizers) + connect(r, SIGNAL(stateChanged(QGestureRecognizer::Result)), + this, SLOT(recognizerStateChanged(QGestureRecognizer::Result))); +} + +void QGestureManager::addRecognizer(QGestureRecognizer *recognizer) +{ + recognizer->setParent(this); + recognizers << recognizer; +} + +void QGestureManager::removeRecognizer(QGestureRecognizer *recognizer) +{ + recognizers.remove(recognizer); +} + +bool QGestureManager::filterEvent(QWidget *receiver, QEvent *event) +{ + QPoint currentPos; + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + currentPos = static_cast<QMouseEvent*>(event)->pos(); break; + default: break; + } + + const QMap<QString, int> &grabbedGestures = qApp->d_func()->grabbedGestures; + + bool ret = false; + QSet<QGestureRecognizer*> startedGestures; + QSet<QGestureRecognizer*> finishedGestures; + QSet<QGestureRecognizer*> newMaybeGestures; + QSet<QGestureRecognizer*> cancelledGestures; + QSet<QGestureRecognizer*> notGestures; + if (state == NotGesture || state == MaybeGesture) { + DEBUG() << "QGestureManager: current event processing state: " + << (state == NotGesture ? "NotGesture" : "MaybeGesture"); + + QSet<QGestureRecognizer*> stillMaybeGestures; + // try other recognizers. + foreach(QGestureRecognizer *r, recognizers) { + if (grabbedGestures.value(r->gestureType(), 0) <= 0) + continue; + QGestureRecognizer::Result result = r->filterEvent(event); + if (result == QGestureRecognizer::GestureStarted) { + DEBUG() << "QGestureManager: gesture started: " << r; + startedGestures << r; + } else if (result == QGestureRecognizer::GestureFinished) { + DEBUG() << "QGestureManager: gesture finished: " << r; + finishedGestures << r; + } else if (result == QGestureRecognizer::MaybeGesture) { + DEBUG() << "QGestureManager: maybe gesture: " << r; + newMaybeGestures << r; + } else if (result == QGestureRecognizer::NotGesture) { + // if it was maybe gesture, but isn't a gesture anymore. + DEBUG() << "QGestureManager: not gesture: " << r; + notGestures << r; + } + } + Q_ASSERT(activeGestures.isEmpty()); + activeGestures += startedGestures; + for(QMap<QGestureRecognizer*, int>::iterator it = maybeGestures.begin(); + it != maybeGestures.end();) { + QGestureRecognizer *r = it.key(); + if (startedGestures.contains(r) || finishedGestures.contains(r) || + notGestures.contains(r)) { + killTimer(it.value()); + it = maybeGestures.erase(it); + } else { + ++it; + } + } + foreach(QGestureRecognizer *r, newMaybeGestures) { + if (!maybeGestures.contains(r)) { + int timerId = startTimer(MaximumGestureRecognitionTimeout); + if (!timerId) + qWarning("QGestureManager: couldn't start timer!"); + maybeGestures.insert(r, timerId); + } + } + if (!finishedGestures.isEmpty() || !startedGestures.isEmpty()) { + // gesture found! + ret = true; + QSet<QGesture*> started; + foreach(QGestureRecognizer *r, finishedGestures) { + if (QGesture *gesture = r->getGesture()) { + started << gesture; + gesture->d_func()->singleshot = true; + } + } + foreach(QGestureRecognizer *r, startedGestures) { + if (QGesture *gesture = r->getGesture()) { + started << gesture; + gesture->d_func()->singleshot = false; + } + } + + if (!activeGestures.isEmpty()) { + DEBUG() << "QGestureManager: new state = Gesture"; + state = Gesture; + } else if (!maybeGestures.isEmpty()) { + DEBUG() << "QGestureManager: new state = Maybe"; + state = MaybeGesture; + } else { + DEBUG() << "QGestureManager: new state = NotGesture"; + state = NotGesture; + } + + Q_ASSERT(!started.isEmpty()); + ret = sendGestureEvent(receiver, started, QSet<QGesture*>()); + } else if (!maybeGestures.isEmpty()) { + if (state != MaybeGesture) { + // We got a new set of events that look like a start + // of some gesture, so we switch to state MaybeGesture + // and wait for more events. + DEBUG() << "QGestureManager: new state = Maybe. Waiting for events"; + state = MaybeGesture; + // start gesture timer + } else { + // we still not sure if it is a gesture or not. + } + } else if (state == MaybeGesture) { + // last time we thought it looks like gesture, but now we + // know for sure that it isn't. + DEBUG() << "QGestureManager: new state = NotGesture"; + state = NotGesture; + } + foreach(QGestureRecognizer *r, finishedGestures) + r->reset(); + foreach(QGestureRecognizer *r, cancelledGestures) + r->reset(); + foreach(QGestureRecognizer *r, notGestures) + r->reset(); + } else if (state == Gesture) { + DEBUG() << "QGestureManager: current event processing state: Gesture"; + Q_ASSERT(!activeGestures.isEmpty()); + + foreach(QGestureRecognizer *r, recognizers) { + if (grabbedGestures.value(r->gestureType(), 0) <= 0) + continue; + QGestureRecognizer::Result result = r->filterEvent(event); + if (result == QGestureRecognizer::GestureStarted) { + DEBUG() << "QGestureManager: gesture started: " << r; + startedGestures << r; + } else if (result == QGestureRecognizer::GestureFinished) { + DEBUG() << "QGestureManager: gesture finished: " << r; + finishedGestures << r; + } else if (result == QGestureRecognizer::MaybeGesture) { + DEBUG() << "QGestureManager: maybe gesture: " << r; + newMaybeGestures << r; + } else if (result == QGestureRecognizer::NotGesture) { + // if it was an active gesture, but isn't a gesture anymore. + if (activeGestures.contains(r)) { + DEBUG() << "QGestureManager: cancelled gesture: " << r; + cancelledGestures << r; + } else { + DEBUG() << "QGestureManager: not gesture: " << r; + notGestures << r; + } + } + } + + for(QMap<QGestureRecognizer*, int>::iterator it = maybeGestures.begin(); + it != maybeGestures.end();) { + QGestureRecognizer *r = it.key(); + if (startedGestures.contains(r) || finishedGestures.contains(r) || + notGestures.contains(r)) { + killTimer(it.value()); + it = maybeGestures.erase(it); + } else { + ++it; + } + } + foreach(QGestureRecognizer *r, newMaybeGestures) { + if (!maybeGestures.contains(r)) { + int timerId = startTimer(MaximumGestureRecognitionTimeout); + if (!timerId) + qWarning("QGestureManager: couldn't start timer!"); + maybeGestures.insert(r, timerId); + } + } + QSet<QGesture*> started, updated; + if (!finishedGestures.isEmpty() || !startedGestures.isEmpty()) { + // another gesture found! + ret = true; + foreach(QGestureRecognizer *r, finishedGestures) { + if (QGesture *gesture = r->getGesture()) { + gesture->d_func()->singleshot = !activeGestures.contains(r); + if (gesture->d_func()->singleshot) + started << gesture; + else + updated << gesture; + } + } + foreach(QGestureRecognizer *r, startedGestures) { + if (QGesture *gesture = r->getGesture()) { + gesture->d_func()->singleshot = !activeGestures.contains(r); + if (gesture->d_func()->singleshot) + started << gesture; + else + updated << gesture; + } + } + } + activeGestures -= newMaybeGestures; + activeGestures -= cancelledGestures; + activeGestures += startedGestures; + activeGestures -= finishedGestures; + QSet<QString> cancelledGestureNames; + foreach(QGestureRecognizer *r, cancelledGestures) + cancelledGestureNames << r->gestureType(); + ret = sendGestureEvent(receiver, started, updated, cancelledGestureNames); + + foreach(QGestureRecognizer *r, finishedGestures) + r->reset(); + foreach(QGestureRecognizer *r, cancelledGestures) + r->reset(); + foreach(QGestureRecognizer *r, notGestures) + r->reset(); + if (!activeGestures.isEmpty()) { + // nothing changed, we are still handling a gesture + } else if (!maybeGestures.isEmpty()) { + DEBUG() << "QGestureManager: new state = Maybe. Waiting for events: " << maybeGestures; + state = MaybeGesture; + } else { + DEBUG() << "QGestureManager: new state = NotGesture"; + state = NotGesture; + } + } + + if (delayedPressTimer && state == Gesture) { + DEBUG() << "QGestureManager: gesture started. Forgetting about postponed mouse press event"; + killTimer(delayedPressTimer); + delayedPressTimer = 0; + lastMousePressReceiver = 0; + } else if (delayedPressTimer && (state == NotGesture || + event->type() == QEvent::MouseButtonRelease)) { + // not a gesture or released button too fast, so replay press + // event back. + DEBUG() << "QGestureManager: replaying mouse press event"; + QMap<QGestureRecognizer*, int>::const_iterator it = maybeGestures.begin(), + e = maybeGestures.end();; + for (; it != e; ++it) { + it.key()->reset(); + killTimer(it.value()); + } + maybeGestures.clear(); + state = NotGesture; + + if (lastMousePressReceiver) { + QApplication::sendEvent(lastMousePressReceiver, &lastMousePressEvent); + if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *me = static_cast<QMouseEvent*>(event); + QMouseEvent move(QEvent::MouseMove, me->pos(), me->globalPos(), me->button(), + me->buttons(), me->modifiers()); + QApplication::sendEvent(lastMousePressReceiver, &move); + ret = false; + } + lastMousePressReceiver = 0; + } + lastMousePressReceiver = 0; + killTimer(delayedPressTimer); + delayedPressTimer = 0; + } else if (state == MaybeGesture && event->type() == QEvent::MouseButtonPress + && eventDeliveryDelayTimeout) { + // postpone the press event delivery until we know for + // sure whether it is a gesture. + DEBUG() << "QGestureManager: postponing mouse press event"; + QMouseEvent *me = static_cast<QMouseEvent*>(event); + lastMousePressReceiver = receiver; + lastMousePressEvent = QMouseEvent(QEvent::MouseButtonPress, me->pos(), + me->globalPos(), me->button(), + me->buttons(), me->modifiers()); + Q_ASSERT(delayedPressTimer == 0); + delayedPressTimer = startTimer(eventDeliveryDelayTimeout); + if (!delayedPressTimer) + qWarning("QGestureManager: couldn't start delayed press timer!"); + ret = true; + } + if (delayedPressTimer && event->type() == QEvent::MouseMove) { + // if we have postponed a mouse press event, postpone all + // subsequent mouse move events as well. + ret = true; + } + + lastPos = currentPos; + return ret; +} + +void QGestureManager::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == delayedPressTimer) { + DEBUG() << "QGestureManager: replaying mouse press event due to timeout"; + // sanity checks + Q_ASSERT(state != Gesture); + + QMap<QGestureRecognizer*, int>::const_iterator it = maybeGestures.begin(), + e = maybeGestures.end();; + for (; it != e; ++it) { + it.key()->reset(); + killTimer(it.value()); + } + maybeGestures.clear(); + state = NotGesture; + + if (lastMousePressReceiver) { + // we neither received a mouse release event nor gesture + // started, so we replay stored mouse press event. + QApplication::sendEvent(lastMousePressReceiver, &lastMousePressEvent); + lastMousePressReceiver = 0; + } + + lastMousePressReceiver = 0; + killTimer(delayedPressTimer); + delayedPressTimer = 0; + } else { + // sanity checks, remove later + Q_ASSERT((state == Gesture && !activeGestures.isEmpty()) || (state != Gesture && activeGestures.isEmpty())); + + typedef QMap<QGestureRecognizer*, int> MaybeGestureMap; + for (MaybeGestureMap::iterator it = maybeGestures.begin(), e = maybeGestures.end(); + it != e; ++it) { + if (it.value() == event->timerId()) { + DEBUG() << "QGestureManager: gesture timeout."; + QGestureRecognizer *r = it.key(); + r->reset(); + maybeGestures.erase(it); + killTimer(event->timerId()); + break; + } + } + + if (state == MaybeGesture && maybeGestures.isEmpty()) { + DEBUG() << "QGestureManager: new state = NotGesture because of timeout"; + state = NotGesture; + } + } +} + +bool QGestureManager::inGestureMode() +{ + return state == Gesture; +} + +void QGestureManager::recognizerStateChanged(QGestureRecognizer::Result result) +{ + QGestureRecognizer *recognizer = qobject_cast<QGestureRecognizer*>(sender()); + if (!recognizer) + return; + if (qApp->d_func()->grabbedGestures.value(recognizer->gestureType(), 0) <= 0) { + recognizer->reset(); + return; + } + + switch (result) { + case QGestureRecognizer::GestureStarted: + case QGestureRecognizer::GestureFinished: { + if (result == QGestureRecognizer::GestureStarted) { + DEBUG() << "QGestureManager: gesture started: " << recognizer; + activeGestures << recognizer; + DEBUG() << "QGestureManager: new state = Gesture"; + state = Gesture; + } else { + DEBUG() << "QGestureManager: gesture finished: " << recognizer; + } + if (maybeGestures.contains(recognizer)) { + killTimer(maybeGestures.value(recognizer)); + maybeGestures.remove(recognizer); + } + QSet<QGesture*> gestures; + if (QGesture *gesture = recognizer->getGesture()) + gestures << gesture; + if(!gestures.isEmpty()) { + //FIXME: sendGestureEvent(targetWidget, gestures); + } + if (result == QGestureRecognizer::GestureFinished) + recognizer->reset(); + } + break; + case QGestureRecognizer::MaybeGesture: { + DEBUG() << "QGestureManager: maybe gesture: " << recognizer; + if (activeGestures.contains(recognizer)) { + //FIXME: sendGestureEvent(targetWidget, QSet<QGesture*>(), QSet<QString>() << recognizer->gestureType()); + } + if (!maybeGestures.contains(recognizer)) { + int timerId = startTimer(MaximumGestureRecognitionTimeout); + if (!timerId) + qWarning("QGestureManager: couldn't start timer!"); + maybeGestures.insert(recognizer, timerId); + } + } + break; + case QGestureRecognizer::NotGesture: + DEBUG() << "QGestureManager: not gesture: " << recognizer; + if (maybeGestures.contains(recognizer)) { + killTimer(maybeGestures.value(recognizer)); + maybeGestures.remove(recognizer); + } + recognizer->reset(); + break; + default: + Q_ASSERT(false); + } + + if (delayedPressTimer && state == Gesture) { + killTimer(delayedPressTimer); + delayedPressTimer = 0; + } +} + +bool QGestureManager::sendGestureEvent(QWidget *receiver, + const QSet<QGesture*> &startedGestures, + const QSet<QGesture*> &updatedGestures, + const QSet<QString> &cancelled) +{ + DEBUG() << "QGestureManager::sendGestureEvent: sending to" << receiver + << "gestures:" << startedGestures << "," << updatedGestures + << "cancelled:" << cancelled; + // grouping gesture objects by receiver widgets. + typedef QMap<QWidget*, QSet<QGesture*> > WidgetGesturesMap; + WidgetGesturesMap widgetGestures; + for(QSet<QGesture*>::const_iterator it = startedGestures.begin(), e = startedGestures.end(); + it != e; ++it) { + QGesture *g = *it; + QGesturePrivate *gd = g->d_func(); + if (receiver) { + // find the target widget + gd->widget = 0; + gd->graphicsItem = 0; + QWidget *w = receiver; + QPoint offset; + const QString gestureType = g->type(); + while (w) { + if (w->d_func()->hasGesture(gestureType)) + break; + if (w->isWindow()) { + w = 0; + break; + } + offset += w->pos(); + w = w->parentWidget(); + } + if (w && w != gd->widget) { + DEBUG() << "QGestureManager::sendGestureEvent:" << g << "propagating to widget" << w << "offset" << offset; + g->translate(offset); + } + gd->widget = w; + } + if (!gd->widget) { + DEBUG() << "QGestureManager: didn't find a widget to send gesture event (" + << g->type() << ") for tree:" << receiver; + // TODO: maybe we should reset gesture recognizers when nobody interested in its gestures. + continue; + } + widgetGestures[gd->widget].insert(g); + } + + QSet<QGesture*> ignoredGestures; + bool ret = false; + for(WidgetGesturesMap::const_iterator it = widgetGestures.begin(), e = widgetGestures.end(); + it != e; ++it) { + QWidget *receiver = it.key(); + Q_ASSERT(receiver != 0 /*should be taken care above*/); + QSet<QGesture*> gestures = it.value(); + // mark all gestures as ignored by default + for(QSet<QGesture*>::iterator it = gestures.begin(), e = gestures.end(); it != e; ++it) + (*it)->ignore(); + // TODO: send cancelled gesture event to the widget that received the original gesture! + QGestureEvent event(gestures, cancelled); + DEBUG() << "QGestureManager::sendGestureEvent: sending now to" << receiver + << "gestures" << gestures; + bool processed = qt_sendSpontaneousEvent(receiver, &event); + QSet<QGesture*> started = startedGestures & gestures; + DEBUG() << "QGestureManager::sendGestureEvent:" << + (event.isAccepted() ? "" : "not") << "all gestures were accepted"; + if (!started.isEmpty() && !(processed && event.isAccepted())) { + // there are started gestures events that weren't + // accepted, so propagating each gesture independently. + if (event.isAccepted()) { + foreach(QGesture *g, started) + g->accept(); + } + QSet<QGesture*>::const_iterator it = started.begin(), + e = started.end(); + for(; it != e; ++it) { + QGesture *g = *it; + if (processed && g->isAccepted()) { + ret = true; + continue; + } + // if it wasn't accepted, find the first parent widget + // that is subscribed to the gesture. + QGesturePrivate *gd = g->d_func(); + QWidget *w = gd->widget; + gd->widget = 0; + + if (w && !w->isWindow()) { + g->translate(w->pos()); + w = w->parentWidget(); + QPoint offset; + const QString gestureType = g->type(); + while (w) { + if (w->d_func()->hasGesture(gestureType)) { + DEBUG() << "QGestureManager::sendGestureEvent:" << receiver + << "didn't accept gesture" << g << "propagating to" << w; + ignoredGestures.insert(g); + gd->widget = w; + break; + } + if (w->isWindow()) { + w = 0; + break; + } + offset += w->pos(); + w = w->parentWidget(); + } + if (w) { + g->translate(offset); + } else { + DEBUG() << "QGestureManager::sendGestureEvent:" << receiver + << "didn't accept gesture" << g << "and nobody wants it"; + } + } + } + } + } + if (ignoredGestures.isEmpty()) + return ret; + // try to send all gestures that were ignored to the next parent + return sendGestureEvent(0, ignoredGestures, QSet<QGesture*>(), cancelled) || ret; +} + +int QGestureManager::eventDeliveryDelay() const +{ + return eventDeliveryDelayTimeout; +} + +void QGestureManager::setEventDeliveryDelay(int ms) +{ + eventDeliveryDelayTimeout = ms; +} + +int QGestureManager::makeGestureId(const QString &name) +{ + gestureIdMap[++lastGestureId] = name; + return lastGestureId; +} + +void QGestureManager::releaseGestureId(int gestureId) +{ + gestureIdMap.remove(gestureId); +} + +QString QGestureManager::gestureNameFromId(int gestureId) const +{ + return gestureIdMap.value(gestureId); +} + +QT_END_NAMESPACE + +#include "moc_qgesturemanager_p.cpp" + diff --git a/src/gui/kernel/qgesturemanager_p.h b/src/gui/kernel/qgesturemanager_p.h new file mode 100644 index 0000000..745feb3 --- /dev/null +++ b/src/gui/kernel/qgesturemanager_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTUREMANAGER_P_H +#define QGESTUREMANAGER_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 "qlist.h" +#include "qset.h" +#include "qevent.h" +#include "qbasictimer.h" +#include "qpointer.h" + +#include "qgesturerecognizer.h" + +QT_BEGIN_NAMESPACE + +class QWidget; +class Q_AUTOTEST_EXPORT QGestureManager : public QObject +{ + Q_OBJECT +public: + QGestureManager(QObject *parent); + + int eventDeliveryDelay() const; + void setEventDeliveryDelay(int ms); + + void addRecognizer(QGestureRecognizer *recognizer); + void removeRecognizer(QGestureRecognizer *recognizer); + + bool filterEvent(QWidget *receiver, QEvent *event); + bool inGestureMode(); + + int makeGestureId(const QString &name); + void releaseGestureId(int gestureId); + QString gestureNameFromId(int gestureId) const; + + // declared in qapplication.cpp + static QGestureManager* instance(); + + bool sendGestureEvent(QWidget *receiver, + const QSet<QGesture*> &startedGestures, + const QSet<QGesture*> &updatedGestures, + const QSet<QString> &cancelled = QSet<QString>()); + +protected: + void timerEvent(QTimerEvent *event); + +private slots: + void recognizerStateChanged(QGestureRecognizer::Result); + +private: + QSet<QGestureRecognizer*> activeGestures; + QMap<QGestureRecognizer*, int> maybeGestures; + QSet<QGestureRecognizer*> recognizers; + + QPoint lastPos; + + int eventDeliveryDelayTimeout; + int delayedPressTimer; + QPointer<QWidget> lastMousePressReceiver; + QMouseEvent lastMousePressEvent; + + QMap<int, QString> gestureIdMap; + int lastGestureId; + + enum State { + Gesture, + NotGesture, + MaybeGesture // that mean timers are up and waiting for some + // more events, and input events are handled by + // gesture recognizer explicitely + } state; +}; + +QT_END_NAMESPACE + +#endif // QGESTUREMANAGER_P_H diff --git a/src/gui/kernel/qgesturerecognizer.cpp b/src/gui/kernel/qgesturerecognizer.cpp new file mode 100644 index 0000000..879f557 --- /dev/null +++ b/src/gui/kernel/qgesturerecognizer.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgesturerecognizer.h" +#include "qgesture.h" + +#include <private/qobject_p.h> +#include <private/qgesturerecognizer_p.h> + +QT_BEGIN_NAMESPACE + +QString qt_getStandardGestureTypeName(Qt::GestureType gestureType); + +/*! + \class QGestureRecognizer + \since 4.6 + + \brief The QGestureRecognizer class is the base class for + implementing custom gestures. + + This is a base class, to create a custom gesture type, you should + subclass it and implement its pure virtual functions. + + Usually gesture recognizer implements state machine, storing its + state internally in the recognizer object. The recognizer receives + input events through the \l{QGestureRecognizer::}{filterEvent()} + virtual function and decides whether the parsed event should + change the state of the recognizer - i.e. if the event starts or + ends a gesture or if it isn't related to gesture at all. +*/ + +/*! + \enum QGestureRecognizer::Result + \since 4.6 + + This enum type defines the state of the gesture recognizer. + + \value Ignore Gesture recognizer ignores the event. + + \value NotGesture Not a gesture. + + \value GestureStarted The continuous gesture has started. When the + recognizer is in this state, a \l{QGestureEvent}{gesture event} + containing QGesture objects returned by the + \l{QGestureRecognizer::}{getGesture()} will be sent to a widget. + + \value GestureFinished The gesture has ended. A + \l{QGestureEvent}{gesture event} will be sent to a widget. + + \value MaybeGesture Gesture recognizer hasn't decided yet if a + gesture has started, but it might start soon after the following + events are received by the recognizer. This means that gesture + manager shouldn't reset() the internal state of the gesture + recognizer. +*/ + +/*! \fn QGestureRecognizer::Result QGestureRecognizer::filterEvent(const QEvent *event) + + This is a pure virtual function that needs to be implemented in + subclasses. + + Parses input \a event and returns the result, which specifies if + the event sequence is a gesture or not. +*/ + +/*! \fn QGesture* QGestureRecognizer::getGesture() + + Returns a gesture object that will be send to the widget. This + function is called when the gesture recognizer changed its state + to QGestureRecognizer::GestureStarted or + QGestureRecognizer::GestureFinished. + + The returned QGesture object must point to the same object in a + single gesture sequence. + + The gesture object is owned by the recognizer itself. +*/ + +/*! \fn void QGestureRecognizer::reset() + + Resets the internal state of the gesture recognizer. +*/ + +/*! \fn void QGestureRecognizer::stateChanged(QGestureRecognizer::Result result) + + The gesture recognizer might emit the stateChanged() signal when + the gesture state changes asynchronously, i.e. without any event + being filtered through filterEvent(). +*/ + +QGestureRecognizerPrivate::QGestureRecognizerPrivate() + : gestureType(Qt::UnknownGesture) +{ +} + +/*! + Creates a new gesture recognizer object that handles gestures of + the specific \a gestureType as a child of \a parent. + + \sa QApplication::addGestureRecognizer(), + QApplication::removeGestureRecognizer(), +*/ +QGestureRecognizer::QGestureRecognizer(const QString &gestureType, QObject *parent) + : QObject(*new QGestureRecognizerPrivate, parent) +{ + Q_D(QGestureRecognizer); + d->customGestureType = gestureType; +} + +/*! + Returns the name of the gesture that is handled by the recognizer. +*/ +QString QGestureRecognizer::gestureType() const +{ + Q_D(const QGestureRecognizer); + if (d->gestureType == Qt::UnknownGesture) + return d->customGestureType; + return qt_getStandardGestureTypeName(d->gestureType); +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturerecognizer.h b/src/gui/kernel/qgesturerecognizer.h new file mode 100644 index 0000000..67d7949 --- /dev/null +++ b/src/gui/kernel/qgesturerecognizer.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURERECOGNIZER_H +#define QGESTURERECOGNIZER_H + +#include "qevent.h" +#include "qlist.h" +#include "qset.h" + +QT_BEGIN_NAMESPACE + +class QGesture; +class QGestureRecognizerPrivate; +class Q_GUI_EXPORT QGestureRecognizer : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGestureRecognizer) + +public: + enum Result + { + Ignore, + NotGesture, + GestureStarted, //TODO: rename to just Gesture? + GestureFinished, + MaybeGesture + }; + + explicit QGestureRecognizer(const QString &gestureType, QObject *parent = 0); + + QString gestureType() const; + + virtual QGestureRecognizer::Result filterEvent(const QEvent* event) = 0; + virtual QGesture* getGesture() = 0; + virtual void reset() = 0; + +signals: + void stateChanged(QGestureRecognizer::Result result); + +private: + friend class QDoubleTapGestureRecognizer; + friend class QTapAndHoldGestureRecognizer; + friend class QGestureRecognizerPan; +}; + +QT_END_NAMESPACE + +#endif // QGESTURERECOGNIZER_P_H diff --git a/src/gui/kernel/qgesturerecognizer_p.h b/src/gui/kernel/qgesturerecognizer_p.h new file mode 100644 index 0000000..4d80b99 --- /dev/null +++ b/src/gui/kernel/qgesturerecognizer_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURERECOGNIZER_P_H +#define QGESTURERECOGNIZER_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 <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QGestureRecognizerPrivate : public QObjectPrivate +{ +public: + QGestureRecognizerPrivate(); + +public: + Qt::GestureType gestureType; + QString customGestureType; +}; + +QT_END_NAMESPACE + +#endif // QGESTURERECOGNIZER_P_H diff --git a/src/gui/kernel/qgesturestandardrecognizers.cpp b/src/gui/kernel/qgesturestandardrecognizers.cpp new file mode 100644 index 0000000..f6b8b90 --- /dev/null +++ b/src/gui/kernel/qgesturestandardrecognizers.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgesturestandardrecognizers_p.h" +#include "qgesture_p.h" +#include "qgesturerecognizer_p.h" + +// #define GESTURE_RECOGNIZER_DEBUG +#ifndef GESTURE_RECOGNIZER_DEBUG +# define DEBUG if (0) qDebug +#else +# define DEBUG qDebug +#endif + +QT_BEGIN_NAMESPACE + +QString qt_getStandardGestureTypeName(Qt::GestureType gestureType) +{ + switch (gestureType) { + case Qt::TapGesture: + return QLatin1String("__QTapGesture"); + case Qt::DoubleTapGesture: + return QLatin1String("__QDoubleTapGesture"); + case Qt::TrippleTapGesture: + return QLatin1String("__QTrippleTapGesture"); + case Qt::TapAndHoldGesture: + return QLatin1String("__QTapAndHoldGesture"); + case Qt::PanGesture: + return QLatin1String("__QPanGesture"); + case Qt::PinchGesture: + return QLatin1String("__QPinchGesture"); + case Qt::UnknownGesture: + break; + } + qFatal("QGestureRecognizer::gestureType: got an unhandled gesture type."); + return QLatin1String("__unknown_gesture"); +} + +// +// QGestureRecognizerPan +// + +QGestureRecognizerPan::QGestureRecognizerPan(QObject *parent) + : QGestureRecognizer(QString(), parent), + mousePressed(false), gestureState(Qt::NoGesture), + lastDirection(Qt::NoDirection), currentDirection(Qt::NoDirection) +{ + Q_D(QGestureRecognizer); + d->gestureType = Qt::PanGesture; +} + +QGestureRecognizer::Result QGestureRecognizerPan::filterEvent(const QEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress) { + const QMouseEvent *ev = static_cast<const QMouseEvent*>(event); + if (currentDirection != Qt::NoDirection) { + DEBUG() << "Pan: MouseButtonPress: fail. another press during pan"; + reset(); + return QGestureRecognizer::NotGesture; + } + if (ev->button() != Qt::LeftButton) { + return QGestureRecognizer::NotGesture; + } + DEBUG() << "Pan: MouseButtonPress: maybe gesture started"; + mousePressed = true; + pressedPos = lastPos = currentPos = ev->pos(); + return QGestureRecognizer::MaybeGesture; + } else if (event->type() == QEvent::MouseButtonRelease) { + const QMouseEvent *ev = static_cast<const QMouseEvent*>(event); + if (mousePressed && currentDirection != Qt::NoDirection + && ev->button() == Qt::LeftButton) { + DEBUG() << "Pan: MouseButtonRelease: pan detected"; + gestureState = Qt::GestureFinished; + currentPos = ev->pos(); + internalReset(); + return QGestureRecognizer::GestureFinished; + } + DEBUG() << "Pan: MouseButtonRelease: some weird release detected, ignoring"; + reset(); + return QGestureRecognizer::NotGesture; + } else if (event->type() == QEvent::MouseMove) { + if (!mousePressed) + return QGestureRecognizer::NotGesture; + const QMouseEvent *ev = static_cast<const QMouseEvent*>(event); + lastPos = currentPos; + currentPos = ev->pos(); + Qt::DirectionType newDirection = + simpleRecognizer.addPosition(ev->pos()).direction; + DEBUG() << "Pan: MouseMove: simplerecognizer result = " << newDirection; + QGestureRecognizer::Result result = QGestureRecognizer::NotGesture; + if (currentDirection == Qt::NoDirection) { + if (newDirection == Qt::NoDirection) { + result = QGestureRecognizer::MaybeGesture; + } else { + result = QGestureRecognizer::GestureStarted; + gestureState = Qt::GestureStarted; + } + } else { + result = QGestureRecognizer::GestureStarted; + gestureState = Qt::GestureUpdated; + } + if (newDirection != Qt::NoDirection) { + if (currentDirection != newDirection) + lastDirection = currentDirection; + currentDirection = newDirection; + } + return result; + } + return QGestureRecognizer::Ignore; +} + +QGesture* QGestureRecognizerPan::getGesture() +{ + if (currentDirection == Qt::NoDirection) + return 0; + QPanningGesturePrivate *d = gesture.d_func(); + d->startPos = pressedPos; + d->lastPos = lastPos; + d->pos = currentPos; + d->hotSpot = pressedPos; + d->state = gestureState; + d->lastDirection = lastDirection; + d->direction = currentDirection; + + return &gesture; +} + +void QGestureRecognizerPan::reset() +{ + mousePressed = false; + lastDirection = Qt::NoDirection; + currentDirection = Qt::NoDirection; + gestureState = Qt::NoGesture; + diagonalRecognizer.reset(); + simpleRecognizer.reset(); +} + +void QGestureRecognizerPan::internalReset() +{ + mousePressed = false; + diagonalRecognizer.reset(); + simpleRecognizer.reset(); +} + + +// +// QDoubleTapGestureRecognizer +// +QDoubleTapGestureRecognizer::QDoubleTapGestureRecognizer(QObject *parent) + : QGestureRecognizer(QString(), parent), + gesture(0, qt_getStandardGestureTypeName(Qt::DoubleTapGesture)) +{ + Q_D(QGestureRecognizer); + d->gestureType = Qt::DoubleTapGesture; +} + +QGestureRecognizer::Result QDoubleTapGestureRecognizer::filterEvent(const QEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress) { + const QMouseEvent *ev = static_cast<const QMouseEvent*>(event); + if (pressedPosition.isNull()) { + pressedPosition = ev->pos(); + return QGestureRecognizer::MaybeGesture; + } else if ((pressedPosition - ev->pos()).manhattanLength() < 10) { + return QGestureRecognizer::GestureFinished; + } + return QGestureRecognizer::NotGesture; + } else if (event->type() == QEvent::MouseButtonRelease) { + const QMouseEvent *ev = static_cast<const QMouseEvent*>(event); + if (!pressedPosition.isNull() && (pressedPosition - ev->pos()).manhattanLength() < 10) + return QGestureRecognizer::MaybeGesture; + return QGestureRecognizer::NotGesture; + } else if (event->type() == QEvent::MouseButtonDblClick) { + const QMouseEvent *ev = static_cast<const QMouseEvent*>(event); + pressedPosition = ev->pos(); + return QGestureRecognizer::GestureFinished; + } + return QGestureRecognizer::NotGesture; +} + +QGesture* QDoubleTapGestureRecognizer::getGesture() +{ + QGesturePrivate *d = gesture.d_func(); + d->startPos = pressedPosition; + d->lastPos = pressedPosition; + d->pos = pressedPosition; + d->hotSpot = pressedPosition; + d->state = Qt::GestureFinished; + return &gesture; +} + +void QDoubleTapGestureRecognizer::reset() +{ + pressedPosition = QPoint(); +} + +// +// QTapAndHoldGestureRecognizer +// +const int QTapAndHoldGestureRecognizer::iterationCount = 40; +const int QTapAndHoldGestureRecognizer::iterationTimeout = 50; + +QTapAndHoldGestureRecognizer::QTapAndHoldGestureRecognizer(QObject *parent) + : QGestureRecognizer(QString(), parent), + gesture(0, qt_getStandardGestureTypeName(Qt::TapAndHoldGesture)), + iteration(0) +{ + Q_D(QGestureRecognizer); + d->gestureType = Qt::TapAndHoldGesture; +} + +QGestureRecognizer::Result QTapAndHoldGestureRecognizer::filterEvent(const QEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress) { + const QMouseEvent *ev = static_cast<const QMouseEvent*>(event); + if (timer.isActive()) + timer.stop(); + timer.start(QTapAndHoldGestureRecognizer::iterationTimeout, this); + pressedPosition = ev->pos(); + return QGestureRecognizer::MaybeGesture; + } else if (event->type() == QEvent::MouseMove) { + const QMouseEvent *ev = static_cast<const QMouseEvent*>(event); + if ((pressedPosition - ev->pos()).manhattanLength() < 15) + return QGestureRecognizer::GestureStarted; + else + return QGestureRecognizer::NotGesture; + } else if (event->type() == QEvent::MouseButtonRelease) { + timer.stop(); + return QGestureRecognizer::NotGesture; + } + return QGestureRecognizer::Ignore; +} + +void QTapAndHoldGestureRecognizer::timerEvent(QTimerEvent *event) +{ + if (event->timerId() != timer.timerId()) + return; + if (iteration == QTapAndHoldGestureRecognizer::iterationCount) { + timer.stop(); + emit stateChanged(QGestureRecognizer::GestureFinished); + } else { + emit stateChanged(QGestureRecognizer::GestureStarted); + } + ++iteration; +} + +QGesture* QTapAndHoldGestureRecognizer::getGesture() +{ + QGesturePrivate *d = gesture.d_func(); + d->startPos = pressedPosition; + d->lastPos = pressedPosition; + d->pos = pressedPosition; + d->hotSpot = pressedPosition; + if (iteration >= QTapAndHoldGestureRecognizer::iterationCount) + d->state = Qt::GestureFinished; + else + d->state = iteration == 0 ? Qt::GestureStarted : Qt::GestureUpdated; + return &gesture; +} + +void QTapAndHoldGestureRecognizer::reset() +{ + pressedPosition = QPoint(); + timer.stop(); + iteration = 0; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qgesturestandardrecognizers_p.h b/src/gui/kernel/qgesturestandardrecognizers_p.h new file mode 100644 index 0000000..81db3e6 --- /dev/null +++ b/src/gui/kernel/qgesturestandardrecognizers_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGESTURESTANDARDRECOGNIZERS_P_H +#define QGESTURESTANDARDRECOGNIZERS_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 "qevent.h" +#include "qbasictimer.h" +#include "qdebug.h" + +#include "qgesturerecognizer.h" +#include "private/qdirectionrecognizer_p.h" + +QT_BEGIN_NAMESPACE + +class QGestureRecognizerPan : public QGestureRecognizer +{ + Q_OBJECT +public: + QGestureRecognizerPan(QObject *parent); + + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture(); + void reset(); + +private: + void internalReset(); + + QPanningGesture gesture; + + QPoint pressedPos; + QPoint lastPos; + QPoint currentPos; + bool mousePressed; + Qt::GestureState gestureState; + Qt::DirectionType lastDirection; + Qt::DirectionType currentDirection; + QDirectionDiagonalRecognizer diagonalRecognizer; + QDirectionSimpleRecognizer simpleRecognizer; +}; + +class QDoubleTapGestureRecognizer : public QGestureRecognizer +{ + Q_OBJECT +public: + QDoubleTapGestureRecognizer(QObject *parent); + + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture(); + void reset(); + +private: + QGesture gesture; + QPoint pressedPosition; +}; + +class QTapAndHoldGestureRecognizer : public QGestureRecognizer +{ + Q_OBJECT +public: + QTapAndHoldGestureRecognizer(QObject *parent); + + QGestureRecognizer::Result filterEvent(const QEvent *event); + QGesture* getGesture(); + void reset(); + +protected: + void timerEvent(QTimerEvent *event); + +private: + QGesture gesture; + QPoint pressedPosition; + QBasicTimer timer; + int iteration; + static const int iterationCount; + static const int iterationTimeout; +}; + +QT_END_NAMESPACE + +#endif // QGESTURESTANDARDRECOGNIZERS_P_H diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 8c6e0a7..8dd32f5 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -109,6 +109,7 @@ #include "private/qabstractscrollarea_p.h" #include "private/qgraphicssystem_p.h" +#include "private/qgesturemanager_p.h" // widget/widget data creation count //#define QWIDGET_EXTRA_DEBUG @@ -127,6 +128,8 @@ Q_GUI_EXPORT void qt_x11_set_global_double_buffer(bool enable) } #endif +QString qt_getStandardGestureTypeName(Qt::GestureType); + static inline bool qRectIntersects(const QRect &r1, const QRect &r2) { return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right()) && @@ -7474,10 +7477,14 @@ bool QWidget::event(QEvent *event) case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: case QEvent::ContextMenu: #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: #endif + case QEvent::Gesture: return false; default: break; @@ -7873,6 +7880,46 @@ bool QWidget::event(QEvent *event) d->needWindowChange = false; break; #endif + case QEvent::Gesture: + event->ignore(); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event); + const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().first(); + if (touchPoint.isPrimary()) + break; + + // fake a mouse event! + QEvent::Type eventType = QEvent::None; + switch (touchEvent->type()) { + case QEvent::TouchBegin: + eventType = QEvent::MouseButtonPress; + break; + case QEvent::TouchUpdate: + eventType = QEvent::MouseMove; + break; + case QEvent::TouchEnd: + eventType = QEvent::MouseButtonRelease; + break; + default: + Q_ASSERT(!true); + break; + } + if (eventType == QEvent::None) + break; + + QMouseEvent mouseEvent(eventType, + touchPoint.pos().toPoint(), + touchPoint.screenPos().toPoint(), + Qt::LeftButton, + Qt::LeftButton, + touchEvent->modifiers()); + (void) QApplication::sendEvent(this, &mouseEvent); + break; + } #ifndef QT_NO_PROPERTIES case QEvent::DynamicPropertyChange: { const QByteArray &propName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName(); @@ -9881,6 +9928,12 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) } break; +#if defined(Q_WS_WIN) + case Qt::WA_AcceptTouchEvents: + if (on) + d->registerTouchWindow(); + break; +#endif default: break; } @@ -10963,6 +11016,103 @@ QWindowSurface *QWidget::windowSurface() const return bs ? bs->windowSurface : 0; } +/*! + \since 4.6 + + Subscribes the widget to the specified \a gesture type. + + Returns the id of the gesture. + + \sa releaseGesture(), setGestureEnabled() +*/ +int QWidget::grabGesture(const QString &gesture) +{ + Q_D(QWidget); + int id = d->grabGesture(QGestureManager::instance()->makeGestureId(gesture)); + if (d->extra && d->extra->proxyWidget) + d->extra->proxyWidget->QGraphicsItem::d_ptr->grabGesture(id); + return id; +} + +int QWidgetPrivate::grabGesture(int gestureId) +{ + gestures << gestureId; + ++qApp->d_func()->grabbedGestures[QGestureManager::instance()->gestureNameFromId(gestureId)]; + return gestureId; +} + +bool QWidgetPrivate::releaseGesture(int gestureId) +{ + QApplicationPrivate *qAppPriv = qApp->d_func(); + if (gestures.contains(gestureId)) { + QString name = QGestureManager::instance()->gestureNameFromId(gestureId); + Q_ASSERT(qAppPriv->grabbedGestures[name] > 0); + --qAppPriv->grabbedGestures[name]; + gestures.remove(gestureId); + return true; + } + return false; +} + +bool QWidgetPrivate::hasGesture(const QString &name) const +{ + QGestureManager *gm = QGestureManager::instance(); + QSet<int>::const_iterator it = gestures.begin(), + e = gestures.end(); + for (; it != e; ++it) { + if (gm->gestureNameFromId(*it) == name) + return true; + } + return false; +} + +/*! + \since 4.6 + + Subscribes the widget to the specified \a gesture type. + + Returns the id of the gesture. + + \sa releaseGesture(), setGestureEnabled() +*/ +int QWidget::grabGesture(Qt::GestureType gesture) +{ + return grabGesture(qt_getStandardGestureTypeName(gesture)); +} + +/*! + \since 4.6 + + Unsubscribes the widget from a gesture, which is specified by the + \a gestureId. + + \sa grabGesture(), setGestureEnabled() +*/ +void QWidget::releaseGesture(int gestureId) +{ + Q_D(QWidget); + if (d->releaseGesture(gestureId)) { + if (d->extra && d->extra->proxyWidget) + d->extra->proxyWidget->QGraphicsItem::d_ptr->releaseGesture(gestureId); + QGestureManager::instance()->releaseGestureId(gestureId); + } +} + +/*! + \since 4.6 + + If \a enable is true, the gesture with the given \a gestureId is + enabled; otherwise the gesture is disabled. + + The id of the gesture is returned by the grabGesture(). + + \sa grabGesture(), releaseGesture() +*/ +void QWidget::setGestureEnabled(int gestureId, bool enable) +{ + //### +} + void QWidgetPrivate::getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const { if (left) diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index 975da5a..82e9f36 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -59,6 +59,8 @@ #include <QtGui/qevent.h> #endif +#include <QtGui/qgesture.h> + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -90,6 +92,7 @@ class QDragLeaveEvent; class QDropEvent; class QShowEvent; class QHideEvent; +class QGestureEvent; class QInputContext; class QIcon; class QWindowSurface; @@ -610,6 +613,11 @@ public: void setWindowSurface(QWindowSurface *surface); QWindowSurface *windowSurface() const; + int grabGesture(const QString &gesture); + int grabGesture(Qt::GestureType gesture); + void releaseGesture(int gestureId); + void setGestureEnabled(int gestureId, bool enable = true); + Q_SIGNALS: void customContextMenuRequested(const QPoint &pos); @@ -745,6 +753,7 @@ private: friend bool isWidgetOpaque(const QWidget *); friend class QGLWidgetPrivate; #endif + friend class QGestureManager; #ifdef Q_WS_X11 friend void qt_net_update_user_time(QWidget *tlw, unsigned long timestamp); friend void qt_net_remove_user_time(QWidget *tlw); diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index c285866..cf065d3 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -57,9 +57,11 @@ #include "private/qobject_p.h" #include "QtCore/qrect.h" #include "QtCore/qlocale.h" +#include "QtCore/qset.h" #include "QtGui/qregion.h" #include "QtGui/qsizepolicy.h" #include "QtGui/qstyle.h" +#include "QtGui/qevent.h" #ifdef Q_WS_WIN #include "QtCore/qt_windows.h" @@ -522,6 +524,11 @@ public: QList<QAction*> actions; #endif + QSet<int> gestures; + int grabGesture(int gestureId); + bool releaseGesture(int gestureId); + bool hasGesture(const QString &type) const; + // Bit fields. uint high_attributes[3]; // the low ones are in QWidget::widget_attributes QPalette::ColorRole fg_role : 8; @@ -556,6 +563,7 @@ public: void unregisterOleDnd(QWidget *widget, QOleDropTarget *target); #endif void grabMouseWhileInWindow(); + void registerTouchWindow(); #elif defined(Q_WS_MAC) // <--------------------------------------------------------- MAC // This is new stuff uint needWindowChange : 1; diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 988ddf2..e39ca77 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -500,6 +500,9 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO q->setAttribute(Qt::WA_WState_Created); // accept move/resize events hd = 0; // no display context + if (q->testAttribute(Qt::WA_AcceptTouchEvents)) + registerTouchWindow(); + if (window) { // got window from outside if (IsWindowVisible(window)) q->setAttribute(Qt::WA_WState_Visible); @@ -2069,8 +2072,16 @@ void QWidgetPrivate::setModal_sys() { } +void QWidgetPrivate::registerTouchWindow() +{ + Q_Q(QWidget); - + // enable WM_TOUCH* messages on our window + if (q->testAttribute(Qt::WA_WState_Created) + && QApplicationPrivate::RegisterTouchWindow + && q->windowType() != Qt::Desktop) + QApplicationPrivate::RegisterTouchWindow(q->effectiveWinId(), 0); +} QT_END_NAMESPACE diff --git a/src/gui/styles/qproxystyle.cpp b/src/gui/styles/qproxystyle.cpp index 589ca9c..7177bed 100644 --- a/src/gui/styles/qproxystyle.cpp +++ b/src/gui/styles/qproxystyle.cpp @@ -93,7 +93,7 @@ void QProxyStylePrivate::ensureBaseStyle() const baseStyle = 0; } } - } + } if (!baseStyle) // Use application desktop style baseStyle = QStyleFactory::create(QApplicationPrivate::desktopStyleKey()); @@ -368,7 +368,7 @@ bool QProxyStyle::event(QEvent *e) return d->baseStyle->event(e); } -/*! +/*! Returns an icon for the given \a standardIcon. Reimplement this slot to provide your own icons in a QStyle diff --git a/src/gui/widgets/qabstractscrollarea.cpp b/src/gui/widgets/qabstractscrollarea.cpp index 00be29b..e1beb98 100644 --- a/src/gui/widgets/qabstractscrollarea.cpp +++ b/src/gui/widgets/qabstractscrollarea.cpp @@ -909,6 +909,7 @@ bool QAbstractScrollArea::event(QEvent *e) case QEvent::DragMove: case QEvent::DragLeave: #endif + case QEvent::Gesture: return false; case QEvent::StyleChange: case QEvent::LayoutDirectionChange: |