diff options
-rw-r--r-- | src/widgets/graphicsview/qgraphicsscene.cpp | 50 | ||||
-rw-r--r-- | src/widgets/graphicsview/qgraphicsscene.h | 1 | ||||
-rw-r--r-- | src/widgets/graphicsview/qgraphicsscene_p.h | 3 | ||||
-rw-r--r-- | tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp | 115 |
4 files changed, 162 insertions, 7 deletions
diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index 139fad0163..e14a98f332 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -742,12 +742,14 @@ void QGraphicsScenePrivate::setActivePanelHelper(QGraphicsItem *item, bool durin if (panel == activePanel || (!q->isActive() && !duringActivationEvent)) return; + QGraphicsItem *oldFocusItem = focusItem; + // Deactivate the last active panel. if (activePanel) { if (QGraphicsItem *fi = activePanel->focusItem()) { // Remove focus from the current focus item. if (fi == q->focusItem()) - q->setFocusItem(0, Qt::ActiveWindowFocusReason); + setFocusItemHelper(0, Qt::ActiveWindowFocusReason, /* emitFocusChanged = */ false); } QEvent event(QEvent::WindowDeactivate); @@ -775,14 +777,14 @@ void QGraphicsScenePrivate::setActivePanelHelper(QGraphicsItem *item, bool durin // focusable, or on the first focusable item in the panel's // focus chain as a last resort. if (QGraphicsItem *focusItem = panel->focusItem()) { - focusItem->setFocus(Qt::ActiveWindowFocusReason); + setFocusItemHelper(focusItem, Qt::ActiveWindowFocusReason, /* emitFocusChanged = */ false); } else if (panel->flags() & QGraphicsItem::ItemIsFocusable) { - panel->setFocus(Qt::ActiveWindowFocusReason); + setFocusItemHelper(panel, Qt::ActiveWindowFocusReason, /* emitFocusChanged = */ false); } else if (panel->isWidget()) { QGraphicsWidget *fw = static_cast<QGraphicsWidget *>(panel)->d_func()->focusNext; do { if (fw->focusPolicy() & Qt::TabFocus) { - fw->setFocus(Qt::ActiveWindowFocusReason); + setFocusItemHelper(fw, Qt::ActiveWindowFocusReason, /* emitFocusChanged = */ false); break; } fw = fw->d_func()->focusNext; @@ -796,13 +798,23 @@ void QGraphicsScenePrivate::setActivePanelHelper(QGraphicsItem *item, bool durin q->sendEvent(item, &event); } } + + emit q->focusItemChanged(focusItem, oldFocusItem, Qt::ActiveWindowFocusReason); } /*! \internal + + \a emitFocusChanged needs to be false when focus passes from one + item to another through setActivePanel(); i.e. when activation + passes from one panel to another, to avoid getting two focusChanged() + emissions; one focusChanged(0, lastFocus), then one + focusChanged(newFocus, 0). Instead setActivePanel() emits the signal + once itself: focusChanged(newFocus, oldFocus). */ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, - Qt::FocusReason focusReason) + Qt::FocusReason focusReason, + bool emitFocusChanged) { Q_Q(QGraphicsScene); if (item == focusItem) @@ -818,10 +830,14 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, // Set focus on the scene if an item requests focus. if (item) { q->setFocus(focusReason); - if (item == focusItem) + if (item == focusItem) { + if (emitFocusChanged) + emit q->focusItemChanged(focusItem, (QGraphicsItem *)0, focusReason); return; + } } + QGraphicsItem *oldFocusItem = focusItem; if (focusItem) { lastFocusItem = focusItem; @@ -862,6 +878,9 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, QFocusEvent event(QEvent::FocusIn, focusReason); sendEvent(item, &event); } + + if (emitFocusChanged) + emit q->focusItemChanged(focusItem, oldFocusItem, focusReason); } /*! @@ -5437,6 +5456,25 @@ bool QGraphicsScene::focusNextPrevChild(bool next) */ /*! + \fn QGraphicsScene::focusChanged(QGraphicsItem *newFocusItem, QGraphicsItem *oldFocusItem, Qt::FocusReason reason) + + This signal is emitted by QGraphicsScene whenever focus changes in the + scene (i.e., when an item gains or loses input focus, or when focus + passes from one item to another). You can connect to this signal if you + need to keep track of when other items gain input focus. It is + particularily useful for implementing virtual keyboards, input methods, + and cursor items. + + \a oldFocusItem is a pointer to the item that previously had focus, or + 0 if no item had focus before the signal was emitted. \a newFocusItem + is a pointer to the item that gained input focus, or 0 if focus was lost. + \a reason is the reason for the focus change (e.g., if the scene was + deactivated while an input field had focus, \a oldFocusItem would point + to the input field item, \a newFocusItem would be 0, and \a reason would be + Qt::ActiveWindowFocusReason. +*/ + +/*! \since 4.4 Returns the scene's style, or the same as QApplication::style() if the diff --git a/src/widgets/graphicsview/qgraphicsscene.h b/src/widgets/graphicsview/qgraphicsscene.h index 27337d3a2a..3f00b74f53 100644 --- a/src/widgets/graphicsview/qgraphicsscene.h +++ b/src/widgets/graphicsview/qgraphicsscene.h @@ -292,6 +292,7 @@ Q_SIGNALS: void changed(const QList<QRectF> ®ion); void sceneRectChanged(const QRectF &rect); void selectionChanged(); + void focusItemChanged(QGraphicsItem *newFocus, QGraphicsItem *oldFocus, Qt::FocusReason reason); private: Q_DECLARE_PRIVATE(QGraphicsScene) diff --git a/src/widgets/graphicsview/qgraphicsscene_p.h b/src/widgets/graphicsview/qgraphicsscene_p.h index 6799a835ac..bcb95c694a 100644 --- a/src/widgets/graphicsview/qgraphicsscene_p.h +++ b/src/widgets/graphicsview/qgraphicsscene_p.h @@ -157,7 +157,8 @@ public: int activationRefCount; int childExplicitActivation; void setActivePanelHelper(QGraphicsItem *item, bool duringActivationEvent); - void setFocusItemHelper(QGraphicsItem *item, Qt::FocusReason focusReason); + void setFocusItemHelper(QGraphicsItem *item, Qt::FocusReason focusReason, + bool emitFocusChanged = true); QList<QGraphicsWidget *> popupWidgets; void addPopup(QGraphicsWidget *widget); diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp index 69e687d633..3c253bf9cc 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp @@ -63,6 +63,7 @@ #define Q_CHECK_PAINTEVENTS #endif +Q_DECLARE_METATYPE(Qt::FocusReason) Q_DECLARE_METATYPE(QList<int>) Q_DECLARE_METATYPE(QList<QRectF>) Q_DECLARE_METATYPE(QMatrix) @@ -270,6 +271,7 @@ private slots: void siblingIndexAlwaysValid(); void removeFullyTransparentItem(); void zeroScale(); + void focusItemChangedSignal(); // task specific tests below me void task139710_bspTreeCrash(); @@ -4565,6 +4567,119 @@ void tst_QGraphicsScene::zeroScale() QTRY_COMPARE(cl.changes.count(), 2); } +void tst_QGraphicsScene::focusItemChangedSignal() +{ + qRegisterMetaType<QGraphicsItem *>("QGraphicsItem *"); + qRegisterMetaType<Qt::FocusReason>("Qt::FocusReason"); + + QGraphicsScene scene; + QSignalSpy spy(&scene, SIGNAL(focusItemChanged(QGraphicsItem *, QGraphicsItem *, Qt::FocusReason))); + QVERIFY(spy.isValid()); + QCOMPARE(spy.count(), 0); + scene.setFocus(); + QCOMPARE(spy.count(), 0); + QEvent activateEvent(QEvent::WindowActivate); + qApp->sendEvent(&scene, &activateEvent); + QCOMPARE(spy.count(), 0); + + QGraphicsRectItem *topLevelItem1 = new QGraphicsRectItem; + topLevelItem1->setFlag(QGraphicsItem::ItemIsFocusable); + scene.addItem(topLevelItem1); + QCOMPARE(spy.count(), 0); + QVERIFY(!topLevelItem1->hasFocus()); + + QGraphicsRectItem *topLevelItem2 = new QGraphicsRectItem; + topLevelItem2->setFlag(QGraphicsItem::ItemIsFocusable); + topLevelItem2->setFocus(); + QVERIFY(!topLevelItem2->hasFocus()); + scene.addItem(topLevelItem2); + QCOMPARE(spy.count(), 1); + QList<QVariant> arguments = spy.takeFirst(); + QCOMPARE(arguments.size(), 3); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(0)), (QGraphicsItem *)topLevelItem2); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(1)), (QGraphicsItem *)0); + QCOMPARE(qVariantValue<Qt::FocusReason>(arguments.at(2)), Qt::OtherFocusReason); + QVERIFY(topLevelItem2->hasFocus()); + + scene.clearFocus(); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.size(), 3); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(0)), (QGraphicsItem *)0); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(1)), (QGraphicsItem *)topLevelItem2); + QCOMPARE(qVariantValue<Qt::FocusReason>(arguments.at(2)), Qt::OtherFocusReason); + + scene.setFocus(Qt::MenuBarFocusReason); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.size(), 3); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(0)), (QGraphicsItem *)topLevelItem2); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(1)), (QGraphicsItem *)0); + QCOMPARE(qVariantValue<Qt::FocusReason>(arguments.at(2)), Qt::MenuBarFocusReason); + + for (int i = 0; i < 3; ++i) { + topLevelItem1->setFocus(Qt::TabFocusReason); + arguments = spy.takeFirst(); + QCOMPARE(arguments.size(), 3); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(0)), (QGraphicsItem *)topLevelItem1); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(1)), (QGraphicsItem *)topLevelItem2); + QCOMPARE(qVariantValue<Qt::FocusReason>(arguments.at(2)), Qt::TabFocusReason); + + topLevelItem2->setFocus(Qt::TabFocusReason); + arguments = spy.takeFirst(); + QCOMPARE(arguments.size(), 3); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(0)), (QGraphicsItem *)topLevelItem2); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(1)), (QGraphicsItem *)topLevelItem1); + QCOMPARE(qVariantValue<Qt::FocusReason>(arguments.at(2)), Qt::TabFocusReason); + } + + // The following two are unexpected, but fixing this (i.e., losing and gaining focus + // when the scene activation changes) breaks quite a few tests so leave this fix + // for some future release. See QTBUG-28346. + QEvent deactivateEvent(QEvent::WindowDeactivate); + qApp->sendEvent(&scene, &deactivateEvent); + QEXPECT_FAIL("", "QTBUG-28346", Continue); + QCOMPARE(spy.count(), 1); + qApp->sendEvent(&scene, &activateEvent); + QEXPECT_FAIL("", "QTBUG-28346", Continue); + QCOMPARE(spy.count(), 1); + + QGraphicsRectItem *panel1 = new QGraphicsRectItem; + panel1->setFlags(QGraphicsItem::ItemIsPanel | QGraphicsItem::ItemIsFocusable); + panel1->setFocus(); + scene.addItem(panel1); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.size(), 3); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(0)), (QGraphicsItem *)panel1); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(1)), (QGraphicsItem *)topLevelItem2); + QCOMPARE(qVariantValue<Qt::FocusReason>(arguments.at(2)), Qt::ActiveWindowFocusReason); + + QGraphicsRectItem *panel2 = new QGraphicsRectItem; + panel2->setFlags(QGraphicsItem::ItemIsPanel | QGraphicsItem::ItemIsFocusable); + scene.addItem(panel2); + QCOMPARE(spy.count(), 0); + + for (int i = 0; i < 3; ++i) { + scene.setActivePanel(panel2); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.size(), 3); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(0)), (QGraphicsItem *)panel2); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(1)), (QGraphicsItem *)panel1); + QCOMPARE(qVariantValue<Qt::FocusReason>(arguments.at(2)), Qt::ActiveWindowFocusReason); + + scene.setActivePanel(panel1); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QCOMPARE(arguments.size(), 3); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(0)), (QGraphicsItem *)panel1); + QCOMPARE(qVariantValue<QGraphicsItem *>(arguments.at(1)), (QGraphicsItem *)panel2); + QCOMPARE(qVariantValue<Qt::FocusReason>(arguments.at(2)), Qt::ActiveWindowFocusReason); + } + +} + void tst_QGraphicsScene::taskQTBUG_15977_renderWithDeviceCoordinateCache() { QGraphicsScene scene; |