diff options
Diffstat (limited to 'src/gui/graphicsview/qgraphicsscene.cpp')
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 69e08d1..3d4bb5c 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -247,6 +247,7 @@ static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; #ifdef Q_WS_X11 #include <private/qt_x11_p.h> #endif +#include <private/qgesturemanager_p.h> QT_BEGIN_NAMESPACE @@ -1335,6 +1336,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()); @@ -3796,6 +3798,9 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::GraphicsSceneHoverEnter: case QEvent::GraphicsSceneHoverLeave: case QEvent::GraphicsSceneHoverMove: + case QEvent::GraphicsSceneTouchBegin: + case QEvent::GraphicsSceneTouchUpdate: + case QEvent::GraphicsSceneTouchEnd: // 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 @@ -3947,6 +3952,48 @@ 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); + QList<QString> gestureTypes = ev->gestureTypes(); + QGraphicsView *view = qobject_cast<QGraphicsView*>(ev->widget()); + if (!view) { + qWarning("QGraphicsScene::event: gesture event was received without a view"); + break; + } + + // find graphics items that intersects with gestures hot spots. + QPolygonF poly; + QMap<QString, QPointF> sceneHotSpots; + foreach(const QString &type, gestureTypes) { + QPointF pt = ev->mapToScene(ev->gesture(type)->hotSpot()); + sceneHotSpots.insert(type, pt); + poly << pt; + } + QList<QGraphicsItem*> itemsInGestureArea = items(poly, Qt::IntersectsItemBoundingRect); + + foreach(QGraphicsItem *item, itemsInGestureArea) { + QMap<QString, QPointF>::const_iterator it = sceneHotSpots.begin(), + e = sceneHotSpots.end(); + for(; it != e; ++it) { + if (item->contains(item->mapFromScene(it.value()))) { + const QString gestureName = it.key(); + foreach(int gestureId, item->d_ptr->gestures) { + if (QGestureManager::instance()->gestureNameFromId(gestureId) == gestureName) { + d->sendEvent(item, ev); + if (ev->isAccepted()) + break; + } + } + } + } + } + } + break; + case QEvent::GraphicsSceneTouchBegin: + case QEvent::GraphicsSceneTouchUpdate: + case QEvent::GraphicsSceneTouchEnd: + d->touchEventHandler(static_cast<QGraphicsSceneTouchEvent *>(event)); + break; case QEvent::Timer: if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) { if (d->restartIndexTimer) { @@ -5574,6 +5621,281 @@ 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); + } + itemsWithGestures << item; + grabbedGestures << gestureId; +} + +void QGraphicsScenePrivate::releaseGesture(QGraphicsItem *item, int gestureId) +{ + //### +} + +void QGraphicsScenePrivate::updateTouchPointsForItem(QGraphicsItem *item, + QGraphicsSceneTouchEvent *touchEvent) +{ + QList<QGraphicsSceneTouchEvent::TouchPoint *> touchPoints = touchEvent->touchPoints(); + for (int i = 0; i < touchPoints.count(); ++i) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = touchPoints.at(i); + touchPoint->setPos(item->d_ptr->genericMapFromScene(touchPoint->scenePos(), touchEvent->widget())); + touchPoint->setStartPos(item->d_ptr->genericMapFromScene(touchPoint->startScenePos(), touchEvent->widget())); + touchPoint->setLastPos(item->d_ptr->genericMapFromScene(touchPoint->lastScenePos(), touchEvent->widget())); + } +} + +QGraphicsSceneTouchEvent::TouchPoint *QGraphicsScenePrivate::findClosestTouchPoint(const QList<QGraphicsSceneTouchEvent::TouchPoint *> &sceneActiveTouchPoints, + const QPointF &scenePos) +{ + QGraphicsSceneTouchEvent::TouchPoint *closestTouchPoint = 0; + qreal closestDistance; + for (int i = 0; i < sceneActiveTouchPoints.count(); ++i) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = sceneActiveTouchPoints.at(i); + qreal distance = QLineF(scenePos, touchPoint->scenePos()).length(); + if (!closestTouchPoint || distance < closestDistance) { + closestTouchPoint = touchPoint; + closestDistance = distance; + } + } + return closestTouchPoint; +} + +QEvent::Type QGraphicsScenePrivate::appendTouchPoint(QGraphicsSceneTouchEvent::TouchPoint *touchPoint, + QList<QGraphicsSceneTouchEvent::TouchPoint *> *currentTouchPoints) +{ + QEvent::Type eventType = currentTouchPoints->isEmpty() + ? QEvent::GraphicsSceneTouchBegin + : QEvent::GraphicsSceneTouchUpdate; + + // insort touch point (for the app) + int at = 0; + for (; at < sceneCurrentTouchPoints.count(); ++at) { + if (sceneCurrentTouchPoints.at(at)->id() > touchPoint->id()) + break; + } + sceneCurrentTouchPoints.insert(at, touchPoint); + // again, for the items's currentTouchPoints + for (at = 0; at < currentTouchPoints->count(); ++at) { + if (currentTouchPoints->at(at)->id() > touchPoint->id()) + break; + } + currentTouchPoints->insert(at, touchPoint); + + return eventType; +} + +QEvent::Type QGraphicsScenePrivate::removeTouchPoint(QGraphicsSceneTouchEvent::TouchPoint *touchPoint, + QList<QGraphicsSceneTouchEvent::TouchPoint *> *currentTouchPoints) +{ + // remove touch point from all known touch points + for (int i = qMin(sceneCurrentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) { + if (sceneCurrentTouchPoints.at(i) == touchPoint) { + sceneCurrentTouchPoints.removeAt(i); + break; + } + } + // again, for the items's currentTouchPoints + for (int i = qMin(currentTouchPoints->count() - 1, touchPoint->id()); i >= 0; --i) { + if (currentTouchPoints->at(i) == touchPoint) { + currentTouchPoints->removeAt(i); + break; + } + } + + return currentTouchPoints->isEmpty() ? QEvent::GraphicsSceneTouchEnd : QEvent::GraphicsSceneTouchUpdate; +} + +void QGraphicsScenePrivate::touchEventHandler(QGraphicsSceneTouchEvent *sceneTouchEvent) +{ + QList<QGraphicsSceneTouchEvent::TouchPoint *> sceneActiveTouchPoints = sceneCurrentTouchPoints; + + QHash<QGraphicsItem *, QGraphicsSceneTouchEvent *> itemsNeedingEvents; + for (int i = 0; i < sceneTouchEvent->touchPoints().count(); ++i) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = sceneTouchEvent->touchPoints().at(i); + + // update state + QGraphicsItem *item = 0; + QEvent::Type eventType = QEvent::None; + QList<QGraphicsSceneTouchEvent::TouchPoint *> activeTouchPoints; + 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(); + QGraphicsSceneTouchEvent::TouchPoint *closestTouchPoint = findClosestTouchPoint(sceneActiveTouchPoints, touchPoint->scenePos()); + if (closestTouchPoint) { + QGraphicsItem *closestItem = itemForTouchPointId.value(closestTouchPoint->id()); + if (!item + || (closestItem + && (item->isAncestorOf(closestItem) + || closestItem->isAncestorOf(item)))) { + item = closestItem; + } + } + if (!item) + continue; + + itemForTouchPointId.insert(touchPoint->id(), item); + + QList<QGraphicsSceneTouchEvent::TouchPoint *> ¤tTouchPoints = itemCurrentTouchPoints[item]; + eventType = appendTouchPoint(touchPoint, ¤tTouchPoints); + // make sure new points are added to activeTouchPoints as well + sceneActiveTouchPoints = sceneCurrentTouchPoints; + activeTouchPoints = currentTouchPoints; + } else if (touchPoint->state() == Qt::TouchPointReleased) { + item = itemForTouchPointId.take(touchPoint->id()); + if (!item) + continue; + + QList<QGraphicsSceneTouchEvent::TouchPoint *> ¤tTouchPoints = itemCurrentTouchPoints[item]; + sceneActiveTouchPoints = sceneCurrentTouchPoints; + activeTouchPoints = currentTouchPoints; + eventType = removeTouchPoint(touchPoint, ¤tTouchPoints); + } else { + item = itemForTouchPointId.value(touchPoint->id()); + if (!item) + continue; + + sceneActiveTouchPoints = sceneCurrentTouchPoints; + activeTouchPoints = itemCurrentTouchPoints.value(item); + eventType = QEvent::GraphicsSceneTouchUpdate; + } + Q_ASSERT(eventType != QEvent::None); + + if (touchPoint->state() != Qt::TouchPointStationary) { + QGraphicsSceneTouchEvent *&touchEvent = itemsNeedingEvents[item]; + if (!touchEvent) { + touchEvent = new QGraphicsSceneTouchEvent(eventType); + touchEvent->setWidget(sceneTouchEvent->widget()); + touchEvent->setModifiers(sceneTouchEvent->modifiers()); + } + Q_ASSERT(touchEvent->type() == eventType); + touchEvent->setTouchPoints(activeTouchPoints); + } + } + + if (itemsNeedingEvents.isEmpty()) { + sceneTouchEvent->ignore(); + return; + } + + bool acceptSceneTouchEvent = false; + QHash<QGraphicsItem *, QGraphicsSceneTouchEvent *>::ConstIterator it = itemsNeedingEvents.constBegin(); + const QHash<QGraphicsItem *, QGraphicsSceneTouchEvent *>::ConstIterator end = itemsNeedingEvents.constEnd(); + for (; it != end; ++it) { + QGraphicsItem *item = it.key(); + + QGraphicsSceneTouchEvent *touchEvent = it.value(); + + switch (touchEvent->type()) { + case QEvent::GraphicsSceneTouchBegin: + { + // 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; + } + case QEvent::GraphicsSceneTouchEnd: + { + QList<QGraphicsSceneTouchEvent::TouchPoint *> currentTouchPoints = itemCurrentTouchPoints.take(item); + if (!currentTouchPoints.isEmpty()) { + qFatal("Qt: INTERNAL ERROR, the widget's currentTouchPoints should be empty!"); + } + // fall-through intended + } + default: + if (item->d_ptr->acceptedTouchBeginEvent) { + updateTouchPointsForItem(item, touchEvent); + (void) sendEvent(item, touchEvent); + acceptSceneTouchEvent = true; + } + break; + } + + delete touchEvent; + } + sceneTouchEvent->setAccepted(acceptSceneTouchEvent); +} + +bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QGraphicsSceneTouchEvent *touchEvent) +{ + Q_Q(QGraphicsScene); + + if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.first() != origin) { + QGraphicsSceneTouchEvent::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); + touchEvent->ignore(); + res = item->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) { + QGraphicsSceneTouchEvent::TouchPoint *touchPoint = touchEvent->touchPoints().at(i); + itemForTouchPointId[touchPoint->id()] = item; + } + if (origin != item) + itemCurrentTouchPoints.remove(origin); + itemCurrentTouchPoints[item] = touchEvent->touchPoints(); + break; + } + } + + touchEvent->setAccepted(eventAccepted); + return res; +} + QT_END_NAMESPACE #include "moc_qgraphicsscene.cpp" |