diff options
author | Andreas Aardal Hanssen <andreas.aardal.hanssen@nokia.com> | 2009-10-28 10:25:31 (GMT) |
---|---|---|
committer | Andreas Aardal Hanssen <andreas.aardal.hanssen@nokia.com> | 2009-10-28 10:37:17 (GMT) |
commit | cc34b794439b59e3e226e2a8dc896fec5e27689d (patch) | |
tree | 8e23260564836f2913581eba7865bdfc0c072500 | |
parent | 44f8fac4e2fc4a4258b921ac595c2826ca96e99c (diff) | |
download | Qt-cc34b794439b59e3e226e2a8dc896fec5e27689d.zip Qt-cc34b794439b59e3e226e2a8dc896fec5e27689d.tar.gz Qt-cc34b794439b59e3e226e2a8dc896fec5e27689d.tar.bz2 |
Fix initial focus bug in ItemIsFocusScope.
The task provides an example that doesn't gain input focus when started.
The fix contains two parts: one is to allow items with focus scope
ancestors to become focus items even if the scope is inactive. The other
is to fix up the focusItem pointers on reparent. Before, the focus
scopes' focusItem pointers always pointed to itself, or the next scope.
Now these items are treated no differently than other items in that
respect.
The change has a performance impact when reparenting a large subtree
that has a sub focus item onto another item (one more dig).
Task-number: QT-2331
Reviewed-by: Alexis Menard
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 39 | ||||
-rw-r--r-- | tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp | 87 |
2 files changed, 112 insertions, 14 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 0107fba..ef43d5c 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1039,13 +1039,31 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } // Update focus scope item ptr in new scope. - if (newParent) { + QGraphicsItem *newFocusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem; + if (newFocusScopeItem && newParent) { + if (subFocusItem) { + // Find the subFocusItem's topmost focus scope. + QGraphicsItem *ancestorScope = 0; + QGraphicsItem *p = subFocusItem->d_ptr->parent; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) + ancestorScope = p; + if (p->isPanel()) + break; + p = p->parentItem(); + } + if (ancestorScope) + newFocusScopeItem = ancestorScope; + } + QGraphicsItem *p = newParent; while (p) { if (p->flags() & QGraphicsItem::ItemIsFocusScope) { - p->d_ptr->focusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem; - // ### The below line might not make sense... - if (subFocusItem) + p->d_ptr->focusScopeItem = newFocusScopeItem; + // Ensure the new item is no longer the subFocusItem. The + // only way to set focus on a child of a focus scope is + // by setting focus on the scope itself. + if (subFocusItem && !p->focusItem()) subFocusItem->d_ptr->clearSubFocus(); break; } @@ -2983,8 +3001,11 @@ void QGraphicsItemPrivate::setFocusHelper(Qt::FocusReason focusReason, bool clim while (p) { if (p->flags() & QGraphicsItem::ItemIsFocusScope) { p->d_ptr->focusScopeItem = q_ptr; - if (!q_ptr->isActive() || !p->focusItem()) + if (!p->focusItem()) { + // If you call setFocus on a child of a focus scope that + // doesn't currently have a focus item, then stop. return; + } break; } p = p->d_ptr->parent; @@ -10751,8 +10772,12 @@ QDebug operator<<(QDebug debug, QGraphicsItem *item) return debug; } - debug << "QGraphicsItem(this =" << ((void*)item) - << ", parent =" << ((void*)item->parentItem()) + if (QGraphicsObject *o = item->toGraphicsObject()) + debug << o->metaObject()->className(); + else + debug << "QGraphicsItem"; + debug << "(this =" << (void*)item + << ", parent =" << (void*)item->parentItem() << ", pos =" << item->pos() << ", z =" << item->zValue() << ", flags = " << item->flags() << ")"; diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index dabf64c..2c948cc 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -391,6 +391,7 @@ private slots: void moveWhileDeleting(); void ensureDirtySceneTransform(); void focusScope(); + void focusScope2(); void stackBefore(); void sceneModality(); void panelModality(); @@ -8465,7 +8466,7 @@ void tst_QGraphicsItem::focusScope() QVERIFY(!scope2->focusScopeItem()); scope3->setParentItem(scope2); QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); - QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope2); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope3); QGraphicsRectItem *scope1 = new QGraphicsRectItem; scope1->setData(0, "scope1"); @@ -8474,9 +8475,9 @@ void tst_QGraphicsItem::focusScope() QVERIFY(!scope1->focusScopeItem()); scope2->setParentItem(scope1); - QCOMPARE(scope1->focusItem(), (QGraphicsItem *)scope1); - QCOMPARE(scope2->focusItem(), (QGraphicsItem *)0); - QCOMPARE(scope3->focusItem(), (QGraphicsItem *)0); + QCOMPARE(scope1->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); QCOMPARE(scope1->focusScopeItem(), (QGraphicsItem *)scope2); QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); @@ -8527,11 +8528,13 @@ void tst_QGraphicsItem::focusScope() rect5->setFocus(); rect5->setParentItem(rect4); QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)rect5); - QVERIFY(!rect5->hasFocus()); + QVERIFY(rect5->hasFocus()); rect4->setParentItem(0); + QVERIFY(rect5->hasFocus()); QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); - QVERIFY(scope3->hasFocus()); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)0); + QVERIFY(!scope3->hasFocus()); QGraphicsRectItem *rectA = new QGraphicsRectItem; QGraphicsRectItem *scopeA = new QGraphicsRectItem(rectA); @@ -8542,7 +8545,7 @@ void tst_QGraphicsItem::focusScope() scopeB->setFocus(); scene.addItem(rectA); - QVERIFY(!rect5->hasFocus()); + QVERIFY(rect5->hasFocus()); QVERIFY(!scopeB->hasFocus()); scopeA->setFocus(); @@ -8550,6 +8553,76 @@ void tst_QGraphicsItem::focusScope() QCOMPARE(scopeB->focusItem(), (QGraphicsItem *)scopeB); } +void tst_QGraphicsItem::focusScope2() +{ + QGraphicsRectItem *child1 = new QGraphicsRectItem; + child1->setFlags(QGraphicsItem::ItemIsFocusable); + child1->setFocus(); + QCOMPARE(child1->focusItem(), (QGraphicsItem *)child1); + + QGraphicsRectItem *child2 = new QGraphicsRectItem; + child2->setFlags(QGraphicsItem::ItemIsFocusable); + + QGraphicsRectItem *rootFocusScope = new QGraphicsRectItem; + rootFocusScope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + rootFocusScope->setFocus(); + QCOMPARE(rootFocusScope->focusItem(), (QGraphicsItem *)rootFocusScope); + + child1->setParentItem(rootFocusScope); + child2->setParentItem(rootFocusScope); + + QCOMPARE(rootFocusScope->focusScopeItem(), (QGraphicsItem *)child1); + QCOMPARE(rootFocusScope->focusItem(), (QGraphicsItem *)child1); + + QGraphicsRectItem *siblingChild1 = new QGraphicsRectItem; + siblingChild1->setFlags(QGraphicsItem::ItemIsFocusable); + siblingChild1->setFocus(); + + QGraphicsRectItem *siblingChild2 = new QGraphicsRectItem; + siblingChild2->setFlags(QGraphicsItem::ItemIsFocusable); + + QGraphicsRectItem *siblingFocusScope = new QGraphicsRectItem; + siblingFocusScope->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + + siblingChild1->setParentItem(siblingFocusScope); + siblingChild2->setParentItem(siblingFocusScope); + + QCOMPARE(siblingFocusScope->focusScopeItem(), (QGraphicsItem *)siblingChild1); + QCOMPARE(siblingFocusScope->focusItem(), (QGraphicsItem *)0); + + QGraphicsItem *root = new QGraphicsRectItem; + rootFocusScope->setParentItem(root); + siblingFocusScope->setParentItem(root); + + QCOMPARE(root->focusItem(), (QGraphicsItem *)child1); + + QGraphicsScene scene; + scene.addItem(root); + + QEvent activate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &activate); + scene.setFocus(); + + QCOMPARE(scene.focusItem(), (QGraphicsItem *)child1); + + // You cannot set focus on a descendant of a focus scope directly; + // this will only change the scope's focus scope item pointer. If + // you want to give true input focus, you must set it directly on + // the scope itself + siblingChild2->setFocus(); + QVERIFY(!siblingChild2->hasFocus()); + QVERIFY(!siblingChild2->focusItem()); + QCOMPARE(siblingFocusScope->focusScopeItem(), (QGraphicsItem *)siblingChild2); + QCOMPARE(siblingFocusScope->focusItem(), (QGraphicsItem *)0); + + // Set focus on the scope; focus is forwarded to the focus scope item. + siblingFocusScope->setFocus(); + QVERIFY(siblingChild2->hasFocus()); + QVERIFY(siblingChild2->focusItem()); + QCOMPARE(siblingFocusScope->focusScopeItem(), (QGraphicsItem *)siblingChild2); + QCOMPARE(siblingFocusScope->focusItem(), (QGraphicsItem *)siblingChild2); +} + void tst_QGraphicsItem::stackBefore() { QGraphicsRectItem parent; |