summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/widgets/graphicsview/qgraphicsscene.cpp50
-rw-r--r--src/widgets/graphicsview/qgraphicsscene.h1
-rw-r--r--src/widgets/graphicsview/qgraphicsscene_p.h3
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp115
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> &region);
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;