diff options
Diffstat (limited to 'src/gui/graphicsview/qgraphicsscene.cpp')
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index e6c3503..291b3af 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 @@ -1357,6 +1359,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()); @@ -3793,6 +3796,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 @@ -3946,6 +3952,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::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) { @@ -3962,6 +4014,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 @@ -5808,6 +5924,229 @@ 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, + QGraphicsSceneTouchEvent *touchEvent) +{ + QList<QGraphicsSceneTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints(); + for (int i = 0; i < touchPoints.count(); ++i) { + QGraphicsSceneTouchEvent::TouchPoint &touchPoint = touchPoints[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())); +#ifdef Q_CC_GNU +# warning FIXME +#endif + // ### touchPoint.setSize(item->d_ptr->genericMapFromScene(touchPoint.sceneSize(), touchEvent->widget())); + } + touchEvent->setTouchPoints(touchPoints); +} + +int QGraphicsScenePrivate::findClosestTouchPointId(const QPointF &scenePos) +{ + int closestTouchPointId = -1; + qreal closestDistance = qreal(0.); + foreach (const QGraphicsSceneTouchEvent::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(QGraphicsSceneTouchEvent *sceneTouchEvent) +{ + typedef QPair<Qt::TouchPointStates, QList<QGraphicsSceneTouchEvent::TouchPoint> > StatesAndTouchPoints; + QHash<QGraphicsItem *, StatesAndTouchPoints> itemsNeedingEvents; + + for (int i = 0; i < sceneTouchEvent->touchPoints().count(); ++i) { + const QGraphicsSceneTouchEvent::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::GraphicsSceneTouchBegin; + break; + case Qt::TouchPointReleased: + // all touch points have released state + eventType = QEvent::GraphicsSceneTouchEnd; + break; + case Qt::TouchPointStationary: + // don't send the event if nothing changed + continue; + default: + // all other combinations + eventType = QEvent::GraphicsSceneTouchUpdate; + break; + } + + QGraphicsSceneTouchEvent 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::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; + } + default: + if (item->d_ptr->acceptedTouchBeginEvent) { + updateTouchPointsForItem(item, &touchEvent); + (void) sendEvent(item, &touchEvent); + acceptSceneTouchEvent = true; + } + break; + } + } + sceneTouchEvent->setAccepted(acceptSceneTouchEvent); +} + +bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QGraphicsSceneTouchEvent *touchEvent) +{ + Q_Q(QGraphicsScene); + + if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.first() != origin) { + const 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); + 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 QGraphicsSceneTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i); + itemForTouchPointId[touchPoint.id()] = item; + } + break; + } + } + + touchEvent->setAccepted(eventAccepted); + return res; +} + QT_END_NAMESPACE #include "moc_qgraphicsscene.cpp" |