summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp120
-rw-r--r--src/gui/graphicsview/qgraphicsitem.h5
-rw-r--r--src/gui/graphicsview/qgraphicsitem_p.h9
-rw-r--r--src/gui/graphicsview/qgraphicsscene.cpp29
-rw-r--r--src/gui/graphicsview/qgraphicswidget.cpp8
-rw-r--r--src/gui/graphicsview/qgraphicswidget_p.cpp34
-rw-r--r--src/gui/graphicsview/qgraphicswidget_p.h4
-rw-r--r--tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp107
8 files changed, 243 insertions, 73 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index 0ff3685..2425354 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -325,6 +325,10 @@
\value ItemAcceptsInputMethod The item supports input methods typically
used for Asian languages.
This flag was introduced in Qt 4.6.
+
+ \value ItemAutoDetectsFocusProxy The item will assign any child that
+ gains input focus as its focus proxy. See also focusProxy().
+ This flag was introduced in Qt 4.6.
*/
/*!
@@ -907,12 +911,12 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent)
scene->d_func()->index->itemChange(q, QGraphicsItem::ItemParentChange, newParentVariant);
}
- if (QGraphicsWidget *w = isWidget ? static_cast<QGraphicsWidget *>(q) : q->parentWidget()) {
- // Update the child focus chain; when reparenting a widget that has a
+ 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.
- if (QGraphicsWidget *focusChild = w->focusWidget())
- focusChild->clearFocus();
+ subFocusItem->clearFocus();
}
// We anticipate geometry changes. If the item is deleted, it will be
@@ -1000,6 +1004,21 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent)
resolveDepth(parent ? parent->d_ptr->depth : -1);
dirtySceneTransform = 1;
+ // Restore the sub focus chain.
+ if (lastSubFocusItem)
+ lastSubFocusItem->d_ptr->setSubFocus();
+
+ // Auto-update focus proxy. The closest parent that detects
+ // focus proxies is updated as the proxy gains or loses focus.
+ QGraphicsItem *p = newParent;
+ while (p) {
+ if (p->d_ptr->flags & QGraphicsItem::ItemAutoDetectsFocusProxy) {
+ p->setFocusProxy(q);
+ break;
+ }
+ p = p->d_ptr->parent;
+ }
+
// Deliver post-change notification
q->itemChange(QGraphicsItem::ItemParentHasChanged, newParentVariant);
@@ -2463,33 +2482,45 @@ bool QGraphicsItem::hasFocus() const
be passed into any focus event generated by this function; it is used to
give an explanation of what caused the item to get focus.
- Only items that set the ItemIsFocusable flag can accept keyboard focus.
+ Only enabled items that set the ItemIsFocusable flag can accept keyboard
+ focus.
- If this item is not visible (i.e., isVisible() returns false), not
- enabled, not associated with a scene, or if it already has input focus,
- this function will do nothing.
+ If this item is not visible, or not associated with a scene, it will not
+ gain immediate input focus. However, it will be registered as the preferred
+ focus item for its subtree of items, should it later become visible.
As a result of calling this function, this item will receive a focus in
event with \a focusReason. If another item already has focus, that item
will first receive a focus out event indicating that it has lost input
focus.
- \sa clearFocus(), hasFocus()
+ \sa clearFocus(), hasFocus(), focusItem()
*/
void QGraphicsItem::setFocus(Qt::FocusReason focusReason)
{
- if (!d_ptr->scene || !isEnabled() || hasFocus() || !(d_ptr->flags & ItemIsFocusable))
+ // Disabled / unfocusable items cannot accept focus.
+ if (!isEnabled() || !(d_ptr->flags & QGraphicsItem::ItemIsFocusable))
+ return;
+
+ // Find focus proxy.
+ QGraphicsItem *f = this;
+ 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)
return;
- QGraphicsItem *item = this;
- QGraphicsItem *f;
- while ((f = item->d_ptr->focusProxy))
- item = f;
- if (item->isVisible()) {
- // Visible items immediately gain focus from scene.
- d_ptr->scene->d_func()->setFocusItemHelper(item, focusReason);
- } else if (item->d_ptr->isWidget) {
- // Just set up subfocus.
- static_cast<QGraphicsWidget *>(item)->d_func()->setFocusWidget();
+
+ // Update the child focus chain.
+ d_ptr->setSubFocus();
+
+ // Update the scene's focus item.
+ if (d_ptr->scene) {
+ QGraphicsWidget *w = window();
+ if (!w || w->isActiveWindow()) {
+ // Visible items immediately gain focus from scene.
+ d_ptr->scene->d_func()->setFocusItemHelper(f, focusReason);
+ }
}
}
@@ -2508,10 +2539,8 @@ void QGraphicsItem::clearFocus()
{
if (!d_ptr->scene)
return;
- if (d_ptr->isWidget) {
- // Invisible widget items with focus must explicitly clear subfocus.
- static_cast<QGraphicsWidget *>(this)->d_func()->clearFocusWidget();
- }
+ // Invisible items with focus must explicitly clear subfocus.
+ d_ptr->clearSubFocus();
if (hasFocus()) {
// If this item has the scene's input focus, clear it.
d_ptr->scene->setFocusItem(0);
@@ -2579,6 +2608,18 @@ void QGraphicsItem::setFocusProxy(QGraphicsItem *item)
}
/*!
+ If this item, a child or descendant of this item currently has input
+ focus, this function will return a pointer to that item. If
+ no descendant has input focus, 0 is returned.
+
+ \sa QWidget::focusWidget()
+*/
+QGraphicsItem *QGraphicsItem::focusItem() const
+{
+ return d_ptr->subFocusItem;
+}
+
+/*!
\since 4.4
Grabs the mouse input.
@@ -4668,6 +4709,34 @@ void QGraphicsItemPrivate::ensureSceneTransform()
/*!
\internal
+*/
+void QGraphicsItemPrivate::setSubFocus()
+{
+ // Update focus child chain.
+ QGraphicsItem *item = q_ptr;
+ QGraphicsItem *parent = item;
+ bool hidden = !visible;
+ do {
+ parent->d_func()->subFocusItem = item;
+ } while (!parent->isWindow() && (parent = parent->d_ptr->parent) && (!hidden || !parent->d_func()->visible));
+}
+
+/*!
+ \internal
+*/
+void QGraphicsItemPrivate::clearSubFocus()
+{
+ // Reset focus child chain.
+ QGraphicsItem *parent = q_ptr;
+ do {
+ if (parent->d_ptr->subFocusItem != q_ptr)
+ break;
+ parent->d_ptr->subFocusItem = 0;
+ } while (!parent->isWindow() && (parent = parent->d_ptr->parent));
+}
+
+/*!
+ \internal
Tells us if it is a proxy widget
*/
@@ -10038,6 +10107,9 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag)
case QGraphicsItem::ItemAcceptsInputMethod:
str = "ItemAcceptsInputMethod";
break;
+ case QGraphicsItem::ItemAutoDetectsFocusProxy:
+ str = "ItemAutoDetectsFocusProxy";
+ break;
}
debug << str;
return debug;
diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h
index 5c9297f..0e21a47 100644
--- a/src/gui/graphicsview/qgraphicsitem.h
+++ b/src/gui/graphicsview/qgraphicsitem.h
@@ -99,7 +99,8 @@ public:
ItemUsesExtendedStyleOption = 0x200,
ItemHasNoContents = 0x400,
ItemSendsGeometryChanges = 0x800,
- ItemAcceptsInputMethod = 0x1000
+ ItemAcceptsInputMethod = 0x1000,
+ ItemAutoDetectsFocusProxy = 0x2000
// NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag.
};
Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag)
@@ -229,6 +230,8 @@ public:
QGraphicsItem *focusProxy() const;
void setFocusProxy(QGraphicsItem *item);
+ QGraphicsItem *focusItem() const;
+
void grabMouse();
void ungrabMouse();
void grabKeyboard();
diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h
index 1cf188e..c208b99 100644
--- a/src/gui/graphicsview/qgraphicsitem_p.h
+++ b/src/gui/graphicsview/qgraphicsitem_p.h
@@ -123,6 +123,7 @@ public:
siblingIndex(-1),
depth(0),
focusProxy(0),
+ subFocusItem(0),
acceptedMouseButtons(0x1f),
visible(1),
explicitlyHidden(0),
@@ -391,6 +392,9 @@ public:
|| (childrenCombineOpacity() && isFullyTransparent());
}
+ void setSubFocus();
+ void clearSubFocus();
+
inline QTransform transformToParent() const;
inline void ensureSortedChildren();
@@ -411,6 +415,7 @@ public:
int siblingIndex;
int depth;
QGraphicsItem *focusProxy;
+ QGraphicsItem *subFocusItem;
// Packed 32 bytes
quint32 acceptedMouseButtons : 5;
@@ -440,7 +445,7 @@ public:
// New 32 bits
quint32 fullUpdatePending : 1;
- quint32 flags : 13;
+ quint32 flags : 14;
quint32 dirtyChildrenBoundingRect : 1;
quint32 paintedViewBoundingRectsNeedRepaint : 1;
quint32 dirtySceneTransform : 1;
@@ -453,7 +458,7 @@ public:
quint32 acceptedTouchBeginEvent : 1;
quint32 filtersDescendantEvents : 1;
quint32 sceneTransformTranslateOnly : 1;
- quint32 unused : 6; // feel free to use
+ quint32 unused : 5; // feel free to use
// Optional stacking order
int globalStackingOrder;
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp
index 0dce26b..f223cbe 100644
--- a/src/gui/graphicsview/qgraphicsscene.cpp
+++ b/src/gui/graphicsview/qgraphicsscene.cpp
@@ -484,6 +484,8 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item)
index->removeItem(item);
}
+ item->d_ptr->clearSubFocus();
+
if (!item->d_ptr->inDestructor && item == tabFocusFirst) {
QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
widget->d_func()->fixFocusChainBeforeReparenting(0, 0);
@@ -572,17 +574,34 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item,
Q_Q(QGraphicsScene);
if (item == focusItem)
return;
+
+ // Clear focus if asked to set focus on something that can't
+ // accept input focus.
if (item && (!(item->flags() & QGraphicsItem::ItemIsFocusable)
|| !item->isVisible() || !item->isEnabled())) {
item = 0;
}
+ // Set focus on the scene if an item requests focus.
if (item) {
q->setFocus(focusReason);
if (item == focusItem)
return;
}
+ // Auto-update focus proxy. The closest parent that detects
+ // focus proxies is updated as the proxy gains or loses focus.
+ if (item) {
+ QGraphicsItem *p = item->d_ptr->parent;
+ while (p) {
+ if (p->d_ptr->flags & QGraphicsItem::ItemAutoDetectsFocusProxy) {
+ p->setFocusProxy(item);
+ break;
+ }
+ p = p->d_ptr->parent;
+ }
+ }
+
if (focusItem) {
QFocusEvent event(QEvent::FocusOut, focusReason);
lastFocusItem = focusItem;
@@ -602,11 +621,6 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item,
}
if (item) {
- if (item->isWidget()) {
- // Update focus child chain.
- static_cast<QGraphicsWidget *>(item)->d_func()->setFocusWidget();
- }
-
focusItem = item;
QFocusEvent event(QEvent::FocusIn, focusReason);
sendEvent(item, &event);
@@ -2342,6 +2356,11 @@ void QGraphicsScene::addItem(QGraphicsItem *item)
// Deliver post-change notification
item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant);
+ // 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();
+
d->updateInputMethodSensitivityInViews();
}
diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp
index 86c0b48..6937584 100644
--- a/src/gui/graphicsview/qgraphicswidget.cpp
+++ b/src/gui/graphicsview/qgraphicswidget.cpp
@@ -1716,14 +1716,16 @@ void QGraphicsWidget::setFocusPolicy(Qt::FocusPolicy policy)
/*!
If this widget, a child or descendant of this widget currently has input
focus, this function will return a pointer to that widget. If
- no descendant has input focus, 0 is returned.
+ no descendant widget has input focus, 0 is returned.
- \sa QWidget::focusWidget()
+ \sa QGraphicsItem::focusItem(), QWidget::focusWidget()
*/
QGraphicsWidget *QGraphicsWidget::focusWidget() const
{
Q_D(const QGraphicsWidget);
- return d->focusChild;
+ if (d->subFocusItem && d->subFocusItem->d_ptr->isWidget)
+ return static_cast<QGraphicsWidget *>(d->subFocusItem);
+ return 0;
}
/*! \property QGraphicsWidget::horizontalShear
diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp
index c9212f7..8eac063 100644
--- a/src/gui/graphicsview/qgraphicswidget_p.cpp
+++ b/src/gui/graphicsview/qgraphicswidget_p.cpp
@@ -626,34 +626,6 @@ bool QGraphicsWidgetPrivate::hasDecoration() const
return (windowFlags & Qt::Window) && (windowFlags & Qt::WindowTitleHint);
}
-/*!
- \internal
-*/
-void QGraphicsWidgetPrivate::setFocusWidget()
-{
- // Update focus child chain.
- QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr);
- QGraphicsWidget *parent = widget;
- bool hidden = !visible;
- do {
- parent->d_func()->focusChild = widget;
- } while (!parent->isWindow() && (parent = parent->parentWidget()) && (!hidden || !parent->d_func()->visible));
-}
-
-/*!
- \internal
-*/
-void QGraphicsWidgetPrivate::clearFocusWidget()
-{
- // Reset focus child chain.
- QGraphicsWidget *parent = static_cast<QGraphicsWidget *>(q_ptr);
- do {
- if (parent->d_func()->focusChild != q_ptr)
- break;
- parent->d_func()->focusChild = 0;
- } while (!parent->isWindow() && (parent = parent->parentWidget()));
-}
-
/**
* is called after a reparent has taken place to fix up the focus chain(s)
*/
@@ -670,12 +642,6 @@ void QGraphicsWidgetPrivate::fixFocusChainBeforeReparenting(QGraphicsWidget *new
QGraphicsWidget *firstOld = 0;
bool wasPreviousNew = true;
-
- if (focusChild) {
- // Ensure that the current focus child doesn't leave pointers around
- // before reparenting.
- focusChild->clearFocus();
- }
while (w != q) {
bool isCurrentNew = q->isAncestorOf(w);
diff --git a/src/gui/graphicsview/qgraphicswidget_p.h b/src/gui/graphicsview/qgraphicswidget_p.h
index 0c34baa..92e520a 100644
--- a/src/gui/graphicsview/qgraphicswidget_p.h
+++ b/src/gui/graphicsview/qgraphicswidget_p.h
@@ -83,7 +83,6 @@ public:
focusPolicy(Qt::NoFocus),
focusNext(0),
focusPrev(0),
- focusChild(0),
windowFlags(0),
windowData(0),
setWindowFrameMargins(false),
@@ -178,9 +177,6 @@ public:
Qt::FocusPolicy focusPolicy;
QGraphicsWidget *focusNext;
QGraphicsWidget *focusPrev;
- QGraphicsWidget *focusChild;
- void setFocusWidget();
- void clearFocusWidget();
// Windows
Qt::WindowFlags windowFlags;
diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp
index 011e480..9f1693d 100644
--- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp
+++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp
@@ -275,6 +275,9 @@ private slots:
void itemHasNoContents();
void hitTestUntransformableItem();
void focusProxy();
+ void autoDetectFocusProxy();
+ void subFocus();
+ void reverseCreateAutoFocusProxy();
// task specific tests below me
void task141694_textItemEnsureVisible();
@@ -7464,5 +7467,109 @@ void tst_QGraphicsItem::focusProxy()
QCOMPARE(item3->focusProxy(), (QGraphicsItem *)0);
}
+void tst_QGraphicsItem::autoDetectFocusProxy()
+{
+ QGraphicsScene scene;
+ QGraphicsItem *item = scene.addRect(0, 0, 10, 10);
+ item->setFlag(QGraphicsItem::ItemIsFocusable);
+
+ QGraphicsItem *item2 = scene.addRect(0, 0, 10, 10);
+ item2->setParentItem(item);
+ item2->setFlag(QGraphicsItem::ItemIsFocusable);
+
+ QGraphicsItem *item3 = scene.addRect(0, 0, 10, 10);
+ item3->setParentItem(item2);
+ item3->setFlag(QGraphicsItem::ItemIsFocusable);
+
+ item->setFlag(QGraphicsItem::ItemAutoDetectsFocusProxy);
+ QCOMPARE(item->focusProxy(), (QGraphicsItem *)0);
+
+ item2->setFocus();
+ QCOMPARE(item->focusProxy(), item2);
+ item3->setFocus();
+ QCOMPARE(item->focusProxy(), item3);
+ item3->clearFocus();
+ QCOMPARE(item->focusProxy(), item3);
+}
+
+void tst_QGraphicsItem::subFocus()
+{
+ // Construct a text item that's not part of a scene (yet)
+ // and has no parent. Setting focus on it will not make
+ // the item gain input focus; that requires a scene. But
+ // it does set subfocus, indicating that the item wishes
+ // to gain focus later.
+ QGraphicsTextItem *text = new QGraphicsTextItem("Hello");
+ text->setTextInteractionFlags(Qt::TextEditorInteraction);
+ QVERIFY(!text->hasFocus());
+ text->setFocus();
+ QVERIFY(!text->hasFocus());
+ QCOMPARE(text->focusItem(), (QGraphicsItem *)text);
+
+ // Add a sibling.
+ QGraphicsTextItem *text2 = new QGraphicsTextItem("Hi");
+ text2->setTextInteractionFlags(Qt::TextEditorInteraction);
+ text2->setPos(30, 30);
+
+ // Add both items to a scene and check that it's text that
+ // got input focus.
+ QGraphicsScene scene;
+ scene.addItem(text);
+ scene.addItem(text2);
+ QVERIFY(text->hasFocus());
+
+ text->setData(0, "text");
+ text2->setData(0, "text2");
+
+ // Remove text2 and set subfocus on it. Then readd. Reparent it onto the
+ // other item and see that although it becomes text's focus
+ // item, it does not immediately gain input focus.
+ scene.removeItem(text2);
+ text2->setFocus();
+ scene.addItem(text2);
+ QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2);
+ text2->setParentItem(text);
+ qDebug() << text->data(0).toString() << (void*)(QGraphicsItem *)text;
+ qDebug() << text2->data(0).toString() << (void*)(QGraphicsItem *)text2;
+ QCOMPARE(text->focusItem(), (QGraphicsItem *)text2);
+ QVERIFY(text->hasFocus());
+ QVERIFY(!text2->hasFocus());
+
+ // Remove both items from the scene, restore subfocus and
+ // readd them. Now the subfocus should kick in and give
+ // text2 focus.
+ scene.removeItem(text);
+ QCOMPARE(text->focusItem(), (QGraphicsItem *)0);
+ QCOMPARE(text2->focusItem(), (QGraphicsItem *)0);
+ text2->setFocus();
+ QCOMPARE(text->focusItem(), (QGraphicsItem *)text2);
+ QCOMPARE(text2->focusItem(), (QGraphicsItem *)text2);
+ scene.addItem(text);
+
+ // Hiding and showing text should pass focus to text2.
+ QCOMPARE(text->focusItem(), (QGraphicsItem *)text2);
+ QVERIFY(text2->hasFocus());
+}
+
+void tst_QGraphicsItem::reverseCreateAutoFocusProxy()
+{
+ QGraphicsTextItem *text = new QGraphicsTextItem;
+ text->setTextInteractionFlags(Qt::TextEditorInteraction);
+ text->setFlag(QGraphicsItem::ItemAutoDetectsFocusProxy);
+
+ QGraphicsTextItem *text2 = new QGraphicsTextItem;
+ text2->setTextInteractionFlags(Qt::TextEditorInteraction);
+ text2->setFocus();
+ QVERIFY(!text2->hasFocus());
+ QCOMPARE(text->focusProxy(), (QGraphicsItem *)0);
+ text2->setParentItem(text);
+ QCOMPARE(text->focusProxy(), (QGraphicsItem *)text2);
+ QCOMPARE(text->focusItem(), (QGraphicsItem *)text2);
+
+ QGraphicsScene scene;
+ scene.addItem(text);
+ QVERIFY(text2->hasFocus());
+}
+
QTEST_MAIN(tst_QGraphicsItem)
#include "tst_qgraphicsitem.moc"