diff options
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 183 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.h | 5 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem_p.h | 8 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 13 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsview.cpp | 4 | ||||
-rw-r--r-- | tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp | 89 |
6 files changed, 266 insertions, 36 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 9295ee5..838bd34 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -341,6 +341,8 @@ activates all non-panel items. Window items (i.e., QGraphicsItem::isWindow() returns true) are panels. This flag was introduced in Qt 4.6. + + \omitvalue ItemIsFocusScope Internal only (for now). */ /*! @@ -929,12 +931,9 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) scene->d_func()->index->itemChange(q, QGraphicsItem::ItemParentChange, newParentVariant); } - QGraphicsItem *lastSubFocusItem = subFocusItem; - if (subFocusItem) { - // Update the child focus chain; when reparenting an item that has a - // focus child, ensure that that focus child clears its focus child - // chain from our parents before it's reparented. - subFocusItem->clearFocus(); + if (subFocusItem && parent) { + // Make sure none of the old parents point to this guy. + subFocusItem->d_ptr->clearSubFocus(parent); } // We anticipate geometry changes. If the item is deleted, it will be @@ -960,6 +959,41 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } } + // Ensure any last parent focus scope does not point to this item or any of + // its descendents. + QGraphicsItem *p = parent; + QGraphicsItem *parentFocusScopeItem = 0; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) { + // If this item's focus scope's focus scope item points + // to this item or a descendent, then clear it. + QGraphicsItem *fsi = p->d_ptr->focusScopeItem; + if (q_ptr == fsi || q_ptr->isAncestorOf(fsi)) { + parentFocusScopeItem = fsi; + p->d_ptr->focusScopeItem = 0; + } + break; + } + p = p->d_ptr->parent; + } + + // Update focus scope item ptr in new scope. + if (newParent) { + QGraphicsItem *p = newParent; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) { + // ### We really want the parent's focus scope item to point + // to this item's focusItem... + if (q_ptr->flags() & QGraphicsItem::ItemIsFocusScope) + p->d_ptr->focusScopeItem = q_ptr; + else + p->d_ptr->focusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem; + break; + } + p = p->d_ptr->parent; + } + } + if ((parent = newParent)) { bool implicitUpdate = false; if (parent->d_func()->scene && parent->d_func()->scene != scene) { @@ -1026,11 +1060,10 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) dirtySceneTransform = 1; // Restore the sub focus chain. - if (lastSubFocusItem) { + if (subFocusItem) { + subFocusItem->d_ptr->setSubFocus(newParent); if (parent && parent->isActive()) - lastSubFocusItem->setFocus(); - else - lastSubFocusItem->d_ptr->setSubFocus(); + subFocusItem->setFocus(); } // Deliver post-change notification @@ -1199,6 +1232,17 @@ QGraphicsItem::~QGraphicsItem() clearFocus(); + // Update focus scope item ptr. + QGraphicsItem *p = d_ptr->parent; + while (p) { + if (p->flags() & ItemIsFocusScope) { + if (p->d_ptr->focusScopeItem == this) + p->d_ptr->focusScopeItem = 0; + break; + } + p = p->d_ptr->parent; + } + if (!d_ptr->children.isEmpty()) { QList<QGraphicsItem *> oldChildren = d_ptr->children; qDeleteAll(oldChildren); @@ -1938,11 +1982,28 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo } // Enable subfocus - if (newVisible && isWidget) { - QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr); - QGraphicsWidget *fw = widget->focusWidget(); - if (fw && fw != scene->focusItem()) - scene->setFocusItem(fw); + if (newVisible) { + QGraphicsItem *p = parent; + bool done = false; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) { + QGraphicsItem *fsi = p->d_ptr->focusScopeItem; + if (q_ptr == fsi || q_ptr->isAncestorOf(fsi)) { + done = true; + while (fsi->d_ptr->focusScopeItem && fsi->d_ptr->focusScopeItem->isVisible()) + fsi = fsi->d_ptr->focusScopeItem; + scene->setFocusItem(fsi); + } + break; + } + p = p->d_ptr->parent; + } + if (!done) { + QGraphicsItem *fi = subFocusItem; + if (fi && fi != scene->focusItem()) { + scene->setFocusItem(fi); + } + } } // Deliver post-change notification. @@ -2730,28 +2791,53 @@ bool QGraphicsItem::hasFocus() const */ void QGraphicsItem::setFocus(Qt::FocusReason focusReason) { + d_ptr->setFocusHelper(focusReason, /* climb = */ true); +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::setFocusHelper(Qt::FocusReason focusReason, bool climb) +{ // Disabled / unfocusable items cannot accept focus. - if (!isEnabled() || !(d_ptr->flags & QGraphicsItem::ItemIsFocusable)) + if (!q_ptr->isEnabled() || !(flags & QGraphicsItem::ItemIsFocusable)) return; // Find focus proxy. - QGraphicsItem *f = this; + QGraphicsItem *f = q_ptr; while (f->d_ptr->focusProxy) f = f->d_ptr->focusProxy; // Return if it already has focus. - if (d_ptr->scene && d_ptr->scene->focusItem() == f) + if (scene && scene->focusItem() == f) return; // Update the child focus chain. - d_ptr->setSubFocus(); + setSubFocus(); + + // Update focus scope item ptr. + QGraphicsItem *p = parent; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) { + p->d_ptr->focusScopeItem = q_ptr; + if (!q_ptr->isActive()) + return; + break; + } + p = p->d_ptr->parent; + } + + if (climb) { + while (f->d_ptr->focusScopeItem && f->d_ptr->focusScopeItem->isVisible()) + f = f->d_ptr->focusScopeItem; + } // Update the scene's focus item. - if (d_ptr->scene) { - QGraphicsItem *p = panel(); - if ((!p && d_ptr->scene->isActive()) || (p && p->isActive())) { + if (scene) { + QGraphicsItem *p = q_ptr->panel(); + if ((!p && scene->isActive()) || (p && p->isActive())) { // Visible items immediately gain focus from scene. - d_ptr->scene->d_func()->setFocusItemHelper(f, focusReason); + scene->d_func()->setFocusItemHelper(f, focusReason); } } } @@ -2769,8 +2855,18 @@ void QGraphicsItem::setFocus(Qt::FocusReason focusReason) */ void QGraphicsItem::clearFocus() { + // Pass focus to the closest parent focus scope. + QGraphicsItem *p = d_ptr->parent; + while (p) { + if (p->flags() & ItemIsFocusScope) { + p->d_ptr->setFocusHelper(Qt::OtherFocusReason, /* climb = */ false); + return; + } + p = p->d_ptr->parent; + } + // Invisible items with focus must explicitly clear subfocus. - d_ptr->clearSubFocus(); + d_ptr->clearSubFocus(this); if (hasFocus()) { // If this item has the scene's input focus, clear it. @@ -2854,6 +2950,16 @@ QGraphicsItem *QGraphicsItem::focusItem() const } /*! + \internal + + Returns this item's focus scope item. +*/ +QGraphicsItem *QGraphicsItem::focusScopeItem() const +{ + return d_ptr->focusScopeItem; +} + +/*! \since 4.4 Grabs the mouse input. @@ -4842,11 +4948,11 @@ void QGraphicsItemPrivate::ensureSceneTransformRecursive(QGraphicsItem **topMost /*! \internal */ -void QGraphicsItemPrivate::setSubFocus() +void QGraphicsItemPrivate::setSubFocus(QGraphicsItem *rootItem) { // Update focus child chain. Stop at panels, or if this item // is hidden, stop at the first item with a visible parent. - QGraphicsItem *parent = q_ptr; + QGraphicsItem *parent = rootItem ? rootItem : q_ptr; do { // Clear any existing ancestor's subFocusItem. if (parent != q_ptr && parent->d_ptr->subFocusItem) { @@ -4855,23 +4961,25 @@ void QGraphicsItemPrivate::setSubFocus() parent->d_ptr->subFocusItem->d_ptr->clearSubFocus(); } parent->d_ptr->subFocusItem = q_ptr; + parent->d_ptr->subFocusItemChange(); } while (!parent->isPanel() && (parent = parent->d_ptr->parent) && (visible || !parent->d_ptr->visible)); - if (!parent && scene && !scene->isActive()) - scene->d_func()->lastFocusItem = q_ptr; + if (scene && !scene->isActive()) + scene->d_func()->lastFocusItem = subFocusItem; } /*! \internal */ -void QGraphicsItemPrivate::clearSubFocus() +void QGraphicsItemPrivate::clearSubFocus(QGraphicsItem *rootItem) { // Reset sub focus chain. - QGraphicsItem *parent = q_ptr; + QGraphicsItem *parent = rootItem ? rootItem : q_ptr; do { if (parent->d_ptr->subFocusItem != q_ptr) break; parent->d_ptr->subFocusItem = 0; + subFocusItemChange(); } while (!parent->isPanel() && (parent = parent->d_ptr->parent)); } @@ -4891,6 +4999,16 @@ void QGraphicsItemPrivate::resetFocusProxy() /*! \internal + Subclasses can reimplement this function to be notified when subFocusItem + changes. +*/ +void QGraphicsItemPrivate::subFocusItemChange() +{ +} + +/*! + \internal + Tells us if it is a proxy widget */ bool QGraphicsItemPrivate::isProxyWidget() const @@ -5799,6 +5917,8 @@ bool QGraphicsItem::isAncestorOf(const QGraphicsItem *child) const { if (!child || child == this) return false; + if (child->d_ptr->depth() < d_ptr->depth()) + return false; const QGraphicsItem *ancestor = child; while ((ancestor = ancestor->d_ptr->parent)) { if (ancestor == this) @@ -10547,6 +10667,9 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) case QGraphicsItem::ItemIsPanel: str = "ItemIsPanel"; break; + case QGraphicsItem::ItemIsFocusScope: + str = "ItemIsFocusScope"; + break; } debug << str; return debug; diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 1c969ba..bc0f30f 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -104,7 +104,8 @@ public: ItemSendsGeometryChanges = 0x800, ItemAcceptsInputMethod = 0x1000, ItemNegativeZStacksBehindParent = 0x2000, - ItemIsPanel = 0x4000 + ItemIsPanel = 0x4000, + ItemIsFocusScope = 0x8000 // internal // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag. }; Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag) @@ -244,6 +245,7 @@ public: void setFocusProxy(QGraphicsItem *item); QGraphicsItem *focusItem() const; + QGraphicsItem *focusScopeItem() const; void grabMouse(); void ungrabMouse(); @@ -549,6 +551,7 @@ Q_SIGNALS: void zChanged(); void rotationChanged(); void scaleChanged(); + void focusChanged(); protected: QGraphicsObject(QGraphicsItemPrivate &dd, QGraphicsItem *parent, QGraphicsScene *scene); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 0b58ad3..fd2ff34 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -129,6 +129,7 @@ public: itemDepth(-1), focusProxy(0), subFocusItem(0), + focusScopeItem(0), imHints(Qt::ImhNone), acceptedMouseButtons(0x1f), visible(1), @@ -412,9 +413,11 @@ public: || (childrenCombineOpacity() && isFullyTransparent()); } - void setSubFocus(); - void clearSubFocus(); + void setFocusHelper(Qt::FocusReason focusReason, bool climb); + void setSubFocus(QGraphicsItem *rootItem = 0); + void clearSubFocus(QGraphicsItem *rootItem = 0); void resetFocusProxy(); + virtual void subFocusItemChange(); inline QTransform transformToParent() const; inline void ensureSortedChildren(); @@ -439,6 +442,7 @@ public: QGraphicsItem *focusProxy; QList<QGraphicsItem **> focusProxyRefs; QGraphicsItem *subFocusItem; + QGraphicsItem *focusScopeItem; Qt::InputMethodHints imHints; // Packed 32 bits diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 97bc010..f6e0aaf 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -2434,8 +2434,17 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // Ensure that newly added items that have subfocus set, gain // focus automatically if there isn't a focus item already. - if (!d->focusItem && item->focusItem()) - item->focusItem()->setFocus(); + if (!d->focusItem) { + if (item->focusItem() == item && item != d->lastFocusItem) { + QGraphicsItem *fi = item->focusItem() ? item->focusItem() : item->focusScopeItem(); + if (fi) { + QGraphicsItem *fsi; + while ((fsi = fi->focusScopeItem()) && fsi->isVisible()) + fi = fsi; + fi->setFocus(); + } + } + } d->updateInputMethodSensitivityInViews(); } diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 1ea0a33..b0829c5 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -1537,6 +1537,9 @@ void QGraphicsView::setScene(QGraphicsScene *scene) } d->updateInputMethodSensitivity(); + + if (d->scene && hasFocus()) + d->scene->setFocus(); } /*! @@ -2607,7 +2610,6 @@ bool QGraphicsView::event(QEvent *event) bool QGraphicsView::viewportEvent(QEvent *event) { Q_D(QGraphicsView); - if (!d->scene) return QAbstractScrollArea::viewportEvent(event); diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index 6b5e4bb..0744fa5 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -294,6 +294,7 @@ private slots: void activationOnShowHide(); void moveWhileDeleting(); void ensureDirtySceneTransform(); + void focusScope(); // task specific tests below me void task141694_textItemEnsureVisible(); @@ -8245,5 +8246,93 @@ void tst_QGraphicsItem::ensureDirtySceneTransform() QCOMPARE(child3->sceneTransform(), QTransform::fromTranslate(-80, -80)); } +void tst_QGraphicsItem::focusScope() +{ + // ItemIsFocusScope is an internal feature (for now). + QGraphicsScene scene; + + QGraphicsRectItem *scope3 = new QGraphicsRectItem; + scope3->setData(0, "scope3"); + scope3->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope3->setFocus(); + QVERIFY(!scope3->focusScopeItem()); + QCOMPARE(scope3->focusItem(), (QGraphicsItem *)scope3); + + QGraphicsRectItem *scope2 = new QGraphicsRectItem; + scope2->setData(0, "scope2"); + scope2->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope2->setFocus(); + QVERIFY(!scope2->focusScopeItem()); + scope3->setParentItem(scope2); + QCOMPARE(scope2->focusScopeItem(), (QGraphicsItem *)scope3); + QCOMPARE(scope2->focusItem(), (QGraphicsItem *)scope3); + + QGraphicsRectItem *scope1 = new QGraphicsRectItem; + scope1->setData(0, "scope1"); + scope1->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + scope1->setFocus(); + QVERIFY(!scope1->focusScopeItem()); + scope2->setParentItem(scope1); + + 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); + + scene.addItem(scope1); + + QEvent windowActivate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &windowActivate); + scene.setFocus(); + + 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); + + QVERIFY(scope3->hasFocus()); + + scope3->hide(); + QVERIFY(scope2->hasFocus()); + scope2->hide(); + QVERIFY(scope1->hasFocus()); + scope2->show(); + QVERIFY(scope2->hasFocus()); + scope3->show(); + QVERIFY(scope3->hasFocus()); + scope1->hide(); + QVERIFY(!scope3->hasFocus()); + scope1->show(); + QVERIFY(scope3->hasFocus()); + scope3->clearFocus(); + QVERIFY(scope2->hasFocus()); + scope2->clearFocus(); + QVERIFY(scope1->hasFocus()); + scope2->hide(); + scope2->show(); + QVERIFY(!scope2->hasFocus()); + QVERIFY(scope3->hasFocus()); + + QGraphicsRectItem *rect4 = new QGraphicsRectItem; + rect4->setData(0, "rect4"); + rect4->setParentItem(scope3); + + QGraphicsRectItem *rect5 = new QGraphicsRectItem; + rect5->setData(0, "rect5"); + rect5->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemIsFocusScope); + rect5->setFocus(); + rect5->setParentItem(rect4); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)rect5); + QVERIFY(rect5->hasFocus()); + + rect4->setParentItem(0); + QCOMPARE(scope3->focusScopeItem(), (QGraphicsItem *)0); + QVERIFY(!scope3->hasFocus()); +} + QTEST_MAIN(tst_QGraphicsItem) #include "tst_qgraphicsitem.moc" |