summaryrefslogtreecommitdiffstats
path: root/src/gui/graphicsview/qgraphicsscene.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/graphicsview/qgraphicsscene.cpp')
-rw-r--r--src/gui/graphicsview/qgraphicsscene.cpp396
1 files changed, 396 insertions, 0 deletions
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp
index 1fbda85..8e5ebdb 100644
--- a/src/gui/graphicsview/qgraphicsscene.cpp
+++ b/src/gui/graphicsview/qgraphicsscene.cpp
@@ -247,6 +247,8 @@ static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000;
#ifdef Q_WS_X11
#include <private/qt_x11_p.h>
#endif
+#include <private/qgesturemanager_p.h>
+#include <private/qgesture_p.h>
QT_BEGIN_NAMESPACE
@@ -1336,6 +1338,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());
@@ -3762,6 +3765,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
@@ -3913,6 +3919,52 @@ bool QGraphicsScene::event(QEvent *event)
// geometries that do not have an explicit style set.
update();
break;
+ case QEvent::GraphicsSceneGesture: {
+ qDebug() << "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();
+ 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) {
@@ -3929,6 +3981,74 @@ bool QGraphicsScene::event(QEvent *event)
return true;
}
+void QGraphicsScenePrivate::sendGestureEvent(const QSet<QGesture*> &gestures, const QSet<QString> &cancelled)
+{
+ qDebug() << "QGraphicsScenePrivate::sendGestureEvent:" << gestures;
+ 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);
+ }
+ }
+
+ qDebug() << "QGraphicsScenePrivate::sendGestureEvent: started: " << startedGestures;
+ 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);
+ qDebug() << "QGraphicsScenePrivate::sendGestureEvent: sending to " << receiver << it.value();
+ 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;
+ qDebug() << "result: " << g << ":" << processed << g->isAccepted();
+ 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
@@ -5539,6 +5659,282 @@ 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.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 *> &currentTouchPoints = itemCurrentTouchPoints[item];
+ eventType = appendTouchPoint(touchPoint, &currentTouchPoints);
+ // 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 *> &currentTouchPoints = itemCurrentTouchPoints[item];
+ sceneActiveTouchPoints = sceneCurrentTouchPoints;
+ activeTouchPoints = currentTouchPoints;
+ eventType = removeTouchPoint(touchPoint, &currentTouchPoints);
+ } 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"