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.cpp322
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 *> &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"