Add new signal: QGraphicsScene::focusItemChanged().

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.

Task-number: QTBUG-10570
Change-Id: I9cbbd9a2d15d6f568e1597c2c33ec049eb70f793
Reviewed-by: Jan Arve Sæther <jan-arve.saether@digia.com>
Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
This commit is contained in:
Andreas Aardal Hanssen 2012-11-29 22:33:43 +01:00 committed by The Qt Project
parent 2dc6ad8adf
commit af8a6cdd87
4 changed files with 162 additions and 7 deletions

View File

@ -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);
}
/*!
@ -5436,6 +5455,25 @@ bool QGraphicsScene::focusNextPrevChild(bool next)
\sa setSelectionArea(), selectedItems(), QGraphicsItem::setSelected()
*/
/*!
\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

View File

@ -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)

View File

@ -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);

View File

@ -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;