summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp183
-rw-r--r--src/gui/graphicsview/qgraphicsitem.h5
-rw-r--r--src/gui/graphicsview/qgraphicsitem_p.h8
-rw-r--r--src/gui/graphicsview/qgraphicsscene.cpp13
-rw-r--r--src/gui/graphicsview/qgraphicsview.cpp4
-rw-r--r--tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp89
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"