From fd73185195633fd97228a0c832c247b761967598 Mon Sep 17 00:00:00 2001 From: Andreas Aardal Hanssen Date: Fri, 17 Apr 2009 11:11:07 +0200 Subject: Fix two bugs in Graphics View's mouse grab logics + add autotests. Change fd4d94df4eb16e9e54f61dd8ee45914e24832ae9 introduced mouse grab and keyboard grab support to Graphics View (this was as part of 4.4). The change was missing a large set of autotests that I wrote to test the behavior of these features. As part of task 245317, which involves investigating modality in Graphics View, I figured it would be a good idea to start off but reconstructing these autotests. So this change is mainly about adding autotests for mouse grabbing. And of course, as it always is, I found two bugs while writing these tests. 1) Calling QGraphicsItem::grabMouse() while the item is holding the implicit mouse grab will now upgrade the implicit grab to an explicit grab. 2) Adding a popup to the scene will automatically grab the mouse. Before, the popup would get the grab on show(), but if it was already visible when added to the scene it would not gain the grab. Task-number: 245317 Reviewed-by: jasplin --- src/gui/graphicsview/qgraphicsscene.cpp | 13 +- src/gui/graphicsview/qgraphicswidget_p.cpp | 9 +- tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp | 302 +++++++++++++++++++++ 3 files changed, 317 insertions(+), 7 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index c616e91..d9e0eec 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -902,11 +902,18 @@ void QGraphicsScenePrivate::grabMouse(QGraphicsItem *item, bool implicit) { // Append to list of mouse grabber items, and send a mouse grab event. if (mouseGrabberItems.contains(item)) { - if (mouseGrabberItems.last() == item) - qWarning("QGraphicsItem::grabMouse: already a mouse grabber"); - else + if (mouseGrabberItems.last() == item) { + Q_ASSERT(!implicit); + if (!lastMouseGrabberItemHasImplicitMouseGrab) { + qWarning("QGraphicsItem::grabMouse: already a mouse grabber"); + } else { + // Upgrade to an explicit mouse grab + lastMouseGrabberItemHasImplicitMouseGrab = false; + } + } else { qWarning("QGraphicsItem::grabMouse: already blocked by mouse grabber: %p", mouseGrabberItems.last()); + } return; } diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp index 789f8da..06ffe73 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.cpp +++ b/src/gui/graphicsview/qgraphicswidget_p.cpp @@ -66,15 +66,16 @@ void QGraphicsWidgetPrivate::init(QGraphicsItem *parentItem, Qt::WindowFlags wFl isWidget = 1; // QGraphicsItem::isWidget() returns true. focusNext = focusPrev = q; focusPolicy = Qt::NoFocus; + + if (!parentItem) + adjustWindowFlags(&wFlags); + windowFlags = wFlags; + q->setParentItem(parentItem); q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType)); q->setGraphicsItem(q); resolveLayoutDirection(); - - if (!parentItem) - adjustWindowFlags(&wFlags); - windowFlags = wFlags; q->unsetWindowFrameMargins(); } qreal QGraphicsWidgetPrivate::titleBarHeight(const QStyleOptionTitleBar &options) const diff --git a/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp b/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp index dee3f54..a737a37 100644 --- a/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp +++ b/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp @@ -146,6 +146,9 @@ private slots: void setSizes(); void closePopupOnOutsideClick(); void defaultSize(); + void explicitMouseGrabber(); + void implicitMouseGrabber(); + void popupMouseGrabber(); // Task fixes void task236127_bspTreeIndexFails(); @@ -1784,6 +1787,305 @@ void tst_QGraphicsWidget::defaultSize() } +void tst_QGraphicsWidget::explicitMouseGrabber() +{ + QGraphicsWidget *widget = new QGraphicsWidget; + EventSpy widgetGrabEventSpy(widget, QEvent::GrabMouse); + EventSpy widgetUngrabEventSpy(widget, QEvent::UngrabMouse); + + // Grab without scene + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::grabMouse: cannot grab mouse without scene"); + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::ungrabMouse: cannot ungrab mouse without scene"); + widget->ungrabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 0); + + // Add to scene + QGraphicsScene scene; + scene.addItem(widget); + + // Ungrab while not grabber + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::ungrabMouse: not a mouse grabber"); + widget->ungrabMouse(); + + // Simple grab with scene + QVERIFY(!scene.mouseGrabberItem()); + widget->grabMouse(); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 1); + widget->ungrabMouse(); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + + // Grab while grabbing + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 2); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsItem::grabMouse: already a mouse grabber"); + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 2); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + widget->ungrabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + + // Add two more widgets to the scene + QGraphicsWidget *widget2 = new QGraphicsWidget; + scene.addItem(widget2); + EventSpy widget2GrabEventSpy(widget2, QEvent::GrabMouse); + EventSpy widget2UngrabEventSpy(widget2, QEvent::UngrabMouse); + QGraphicsWidget *widget3 = new QGraphicsWidget; + scene.addItem(widget3); + EventSpy widget3GrabEventSpy(widget3, QEvent::GrabMouse); + EventSpy widget3UngrabEventSpy(widget3, QEvent::UngrabMouse); + + widget->setData(0, "widget"); + widget2->setData(0, "widget2"); + widget3->setData(0, "widget3"); + + // Simple nested grabbing + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 3); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + widget2->grabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 3); + QCOMPARE(widget2GrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + widget3->grabMouse(); + QCOMPARE(widget2UngrabEventSpy.count(), 1); + QCOMPARE(widget3GrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); + widget3->ungrabMouse(); + QCOMPARE(widget3UngrabEventSpy.count(), 1); + QCOMPARE(widget2GrabEventSpy.count(), 2); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + widget2->ungrabMouse(); + QCOMPARE(widget2UngrabEventSpy.count(), 2); + QCOMPARE(widgetGrabEventSpy.count(), 4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + widget->ungrabMouse(); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + // Out of order ungrab + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 5); + widget2->grabMouse(); + QCOMPARE(widget2GrabEventSpy.count(), 3); + widget3->grabMouse(); + QCOMPARE(widget3GrabEventSpy.count(), 2); + widget2->ungrabMouse(); + QCOMPARE(widget3UngrabEventSpy.count(), 2); + QCOMPARE(widget2UngrabEventSpy.count(), 4); + QCOMPARE(widgetGrabEventSpy.count(), 6); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); +} + +void tst_QGraphicsWidget::implicitMouseGrabber() +{ + QGraphicsScene scene; + QGraphicsWidget *widget = new QGraphicsWidget; + widget->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget->resize(200, 200); + EventSpy widgetGrabEventSpy(widget, QEvent::GrabMouse); + EventSpy widgetUngrabEventSpy(widget, QEvent::UngrabMouse); + scene.addItem(widget); + + QVERIFY(!scene.mouseGrabberItem()); + + // Click on an item, see if gain and lose implicit mouse grab. + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 1); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QCOMPARE(widgetGrabEventSpy.count(), 1); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + + // Click on an item that already grabs the mouse. Shouldn't have any effect. + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 2); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 2); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 2); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + widget->ungrabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + // Implicit mouse grabber tries to explicitly grab the mouse + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 3); + widget->grabMouse(); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(50, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 3); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + widget->ungrabMouse(); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + QCOMPARE(widgetGrabEventSpy.count(), 3); + QCOMPARE(widgetUngrabEventSpy.count(), 3); + + // Arrival of a new widget + QGraphicsWidget *widget2 = new QGraphicsWidget; + widget2->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget2->resize(200, 200); + widget2->setPos(205, 0); + EventSpy widget2GrabEventSpy(widget2, QEvent::GrabMouse); + EventSpy widget2UngrabEventSpy(widget2, QEvent::UngrabMouse); + scene.addItem(widget2); + + // Implicit grab while there's an explicit grab is not possible. + widget->grabMouse(); + QCOMPARE(widgetGrabEventSpy.count(), 4); + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(250, 50)); + qApp->sendEvent(&scene, &event); + } + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + QCOMPARE(widgetGrabEventSpy.count(), 4); + QCOMPARE(widget2GrabEventSpy.count(), 0); + QCOMPARE(widget2UngrabEventSpy.count(), 0); + + scene.removeItem(widget); + QCOMPARE(widgetUngrabEventSpy.count(), 4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); +} + +void tst_QGraphicsWidget::popupMouseGrabber() +{ + QGraphicsScene scene; + QGraphicsWidget *widget = new QGraphicsWidget(0, Qt::Popup); + widget->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget->resize(200, 200); + EventSpy widgetGrabEventSpy(widget, QEvent::GrabMouse); + EventSpy widgetUngrabEventSpy(widget, QEvent::UngrabMouse); + + // Simply adding a visible popup to the scene immediately grabs the mouse. + scene.addItem(widget); + QCOMPARE(widgetGrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + + // Hiding it loses the grab again. + widget->hide(); + QCOMPARE(widgetUngrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)0); + + // Showing it grabs the mosue again + widget->show(); + QCOMPARE(widgetGrabEventSpy.count(), 2); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget); + + // Add two popups + QGraphicsWidget *widget2 = new QGraphicsWidget(0, Qt::Popup); + widget2->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget2->resize(200, 200); + EventSpy widget2GrabEventSpy(widget2, QEvent::GrabMouse); + EventSpy widget2UngrabEventSpy(widget2, QEvent::UngrabMouse); + QGraphicsWidget *widget3 = new QGraphicsWidget(0, Qt::Popup); + widget3->setFlag(QGraphicsItem::ItemIsMovable); // can grab mouse + widget3->resize(200, 200); + EventSpy widget3GrabEventSpy(widget3, QEvent::GrabMouse); + EventSpy widget3UngrabEventSpy(widget3, QEvent::UngrabMouse); + + // Adding to the scene grabs + scene.addItem(widget2); + QCOMPARE(widgetUngrabEventSpy.count(), 2); + QCOMPARE(widget2GrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + + // Adding to the scene grabs again + scene.addItem(widget3); + QCOMPARE(widget2UngrabEventSpy.count(), 1); + QCOMPARE(widget3GrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); + + // Hiding the topmost widget causes widget 2 to regain grab. + widget3->hide(); + QCOMPARE(widget2GrabEventSpy.count(), 2); + QCOMPARE(widget3UngrabEventSpy.count(), 1); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + widget3->show(); + QCOMPARE(widget2UngrabEventSpy.count(), 2); + QCOMPARE(widget3GrabEventSpy.count(), 2); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); + + // Clicking outside the popup still causes it to close (despite that it's + // an explicit mouse grabber). + { + QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); + event.ignore(); + event.setButton(Qt::LeftButton); + event.setScenePos(QPointF(500, 500)); // outside + qApp->sendEvent(&scene, &event); + } + QVERIFY(!widget3->isVisible()); + QCOMPARE(widget3UngrabEventSpy.count(), 2); + QCOMPARE(widget2GrabEventSpy.count(), 3); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + QVERIFY(widget2->isVisible()); + QVERIFY(widget->isVisible()); + widget3->show(); + QCOMPARE(widget3GrabEventSpy.count(), 3); + QCOMPARE(widget2UngrabEventSpy.count(), 3); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); + + // This is something of a curiosity. What happens if you call + // ungrabMouse() on a popup? The answer is - it loses the grab. If you + // hide and show the popup again, it will regain the grab. + widget3->ungrabMouse(); + QCOMPARE(widget3UngrabEventSpy.count(), 3); + QCOMPARE(widget2GrabEventSpy.count(), 4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget2); + widget3->hide(); + widget3->show(); + QCOMPARE(widget3GrabEventSpy.count(), 4); + QCOMPARE(widget2UngrabEventSpy.count(), 4); + QCOMPARE(scene.mouseGrabberItem(), (QGraphicsItem *)widget3); +} + class ProxyStyle : public QCommonStyle { public: -- cgit v0.12