From 928e81b68e0b695662c7ee3dd0bfa409a7ca1ffd Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 26 Aug 2009 21:48:18 +1000 Subject: Implement Focus Scope support in GraphicsView Focus Scopes are an additional focus paradigm, designed to be more suitable for use by declarative UI. With focus scopes, graphics view continues to have a single focused item - this is the single item to which key events are *actually* delivered. To simplify the description that follows, we will say that this element has "global focus". Focus scopes partitions focus into a heirarchy. Each focus scope may have one sub-focused item, which may itself be another focus scope. The sub-focused item is said to be have "scope focus" or its ancestor focus scope. Consequently, any given QGraphicsItem may be globally focused, scope focused (which means it is focused within its ancestor focus scope) or both. A focus scope corresponds to a QGraphicsItem with the ItemIsFocusScope flag set. As graphics view doesn't have a single root item, a "virtual" focus scope is modeled as being the ancestor of all the root items. With focus scopes, when QGraphicsItem::setFocus() is called, the item's ancestors are searched until a focus scope is found (remembering the "virtual" root focus scope is used if there is no *actual* ancestor scope). The item is set as the sub-focused item of the focus scope (becoming focus scoped) and any existing sub-focused item of that focus scope has scope focus removed from it. The item that receives global focus is found by beginning at the "virtual" root focus scope and recursively decending into each item that is scope focused until a leaf (non-focus scope) item is reached. The implementation takes shortcuts here to be slightly more optimal, but the end result should be the same as though this abstract algorithm was evaluated each time. Two manual examples of focus scope are found under examples/declarative/focusscope --- examples/declarative/focusscope/test.qml | 76 +++++++++++++++ examples/declarative/focusscope/test2.qml | 40 ++++++++ src/declarative/fx/qfxcomponentinstance.cpp | 2 - src/declarative/fx/qfxfocusscope.cpp | 2 +- src/declarative/fx/qfxgridview.cpp | 2 +- src/declarative/fx/qfxitem.cpp | 56 ++++------- src/declarative/fx/qfxitem_p.h | 3 + src/declarative/fx/qfxlistview.cpp | 2 +- src/declarative/fx/qfxpathview_p.h | 2 +- src/gui/graphicsview/qgraphicsitem.cpp | 79 ++++++++------- src/gui/graphicsview/qgraphicsitem.h | 4 +- src/gui/graphicsview/qgraphicsitem_p.h | 40 +++++++- src/gui/graphicsview/qgraphicsscene.cpp | 129 +++++++++++++++---------- src/gui/graphicsview/qgraphicsscene_p.h | 2 + tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp | 71 -------------- 15 files changed, 306 insertions(+), 204 deletions(-) create mode 100644 examples/declarative/focusscope/test.qml create mode 100644 examples/declarative/focusscope/test2.qml diff --git a/examples/declarative/focusscope/test.qml b/examples/declarative/focusscope/test.qml new file mode 100644 index 0000000..22ffc8d --- /dev/null +++ b/examples/declarative/focusscope/test.qml @@ -0,0 +1,76 @@ +import Qt 4.6 + +Rectangle { + color: "white" + width: 800 + height: 600 + + Keys.onDigit9Pressed: print("Error - Root") + + FocusScope { + id: MyScope + focus: true + + Keys.onDigit9Pressed: print("Error - FocusScope") + + Rectangle { + height: 120 + width: 420 + + color: "transparent" + border.width: 5 + border.color: MyScope.focus?"blue":"black" + + Rectangle { + id: Item1 + x: 10; y: 10 + width: 100; height: 100; color: "green" + border.width: 5 + border.color: focus?"blue":"black" + Keys.onDigit9Pressed: print("Top Left"); + KeyNavigation.right: Item2 + focus: true + + Rectangle { + width: 50; height: 50; anchors.centerIn: parent + color: parent.activeFocus?"red":"transparent" + } + } + + Rectangle { + id: Item2 + x: 310; y: 10 + width: 100; height: 100; color: "green" + border.width: 5 + border.color: focus?"blue":"black" + KeyNavigation.left: Item1 + Keys.onDigit9Pressed: print("Top Right"); + + Rectangle { + width: 50; height: 50; anchors.centerIn: parent + color: parent.activeFocus?"red":"transparent" + } + } + } + KeyNavigation.down: Item3 + } + + Text { x:100; y:170; text: "Blue border indicates scoped focus\nBlack border indicates NOT scoped focus\nRed box indicates active focus\nUse arrow keys to navigate\nPress \"9\" to print currently focused item" } + + Rectangle { + id: Item3 + x: 10; y: 300 + width: 100; height: 100; color: "green" + border.width: 5 + border.color: focus?"blue":"black" + + Keys.onDigit9Pressed: print("Bottom Left"); + KeyNavigation.up: MyScope + + Rectangle { + width: 50; height: 50; anchors.centerIn: parent + color: parent.activeFocus?"red":"transparent" + } + } + +} diff --git a/examples/declarative/focusscope/test2.qml b/examples/declarative/focusscope/test2.qml new file mode 100644 index 0000000..0ac0f5d --- /dev/null +++ b/examples/declarative/focusscope/test2.qml @@ -0,0 +1,40 @@ +import Qt 4.6 + +Rectangle { + color: "white" + width: 800 + height: 600 + + Text { text: "All five rectangles should be red" } + + FocusScope { + y: 100 + focus: true + Rectangle { width: 50; height: 50; color: parent.focus?"red":"blue" } + + FocusScope { + y: 100 + focus: true + Rectangle { width: 50; height: 50; color: parent.focus?"red":"blue" } + + FocusScope { + y: 100 + focus: true + Rectangle { width: 50; height: 50; color: parent.focus?"red":"blue" } + + FocusScope { + y: 100 + focus: true + Rectangle { width: 50; height: 50; color: parent.focus?"red":"blue" } + + FocusScope { + y: 100 + focus: true + Rectangle { width: 50; height: 50; color: parent.focus?"red":"blue" } + } + } + } + } + } + +} diff --git a/src/declarative/fx/qfxcomponentinstance.cpp b/src/declarative/fx/qfxcomponentinstance.cpp index def7a9f..7a712aa 100644 --- a/src/declarative/fx/qfxcomponentinstance.cpp +++ b/src/declarative/fx/qfxcomponentinstance.cpp @@ -73,13 +73,11 @@ QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,ComponentInstance,QFxComponentIn QFxComponentInstance::QFxComponentInstance(QFxItem *parent) : QFxItem(*(new QFxComponentInstancePrivate), parent) { - setFlag(QGraphicsItem::ItemAutoDetectsFocusProxy); } QFxComponentInstance::QFxComponentInstance(QFxComponentInstancePrivate &dd, QFxItem *parent) : QFxItem(dd, parent) { - setFlag(QGraphicsItem::ItemAutoDetectsFocusProxy); } /*! diff --git a/src/declarative/fx/qfxfocusscope.cpp b/src/declarative/fx/qfxfocusscope.cpp index 8981256..9bcc17e 100644 --- a/src/declarative/fx/qfxfocusscope.cpp +++ b/src/declarative/fx/qfxfocusscope.cpp @@ -63,7 +63,7 @@ QML_DEFINE_TYPE(Qt,4,6,(QT_VERSION&0x00ff00)>>8,FocusScope,QFxFocusScope) QFxFocusScope::QFxFocusScope(QFxItem *parent) : QFxItem(parent) { - setFlag(QGraphicsItem::ItemAutoDetectsFocusProxy); + setFlag(QGraphicsItem::ItemIsFocusScope); } QFxFocusScope::~QFxFocusScope() diff --git a/src/declarative/fx/qfxgridview.cpp b/src/declarative/fx/qfxgridview.cpp index e2c7fa3..b429895 100644 --- a/src/declarative/fx/qfxgridview.cpp +++ b/src/declarative/fx/qfxgridview.cpp @@ -325,7 +325,7 @@ public: void QFxGridViewPrivate::init() { Q_Q(QFxGridView); - q->setFlag(QGraphicsItem::ItemAutoDetectsFocusProxy); + q->setFlag(QGraphicsItem::ItemIsFocusScope); QObject::connect(q, SIGNAL(widthChanged()), q, SLOT(sizeChange())); QObject::connect(q, SIGNAL(heightChanged()), q, SLOT(sizeChange())); } diff --git a/src/declarative/fx/qfxitem.cpp b/src/declarative/fx/qfxitem.cpp index a8be81f..9b9355e 100644 --- a/src/declarative/fx/qfxitem.cpp +++ b/src/declarative/fx/qfxitem.cpp @@ -2363,7 +2363,14 @@ QPointF QFxItemPrivate::computeTransformOrigin() const */ bool QFxItem::sceneEvent(QEvent *event) { - return QGraphicsItem::sceneEvent(event); + bool rv = QGraphicsItem::sceneEvent(event); + + if (event->type() == QEvent::FocusIn || + event->type() == QEvent::FocusOut) { + activeFocusChanged(hasActiveFocus()); + } + + return rv; } /*! @@ -2548,49 +2555,20 @@ bool QFxItem::heightValid() const bool QFxItem::hasFocus() const { - const QGraphicsItem *current = this->parentItem(); - while (current && !(current->flags() & ItemAutoDetectsFocusProxy)) - current = current->parentItem(); - - if (current) - return current->focusProxy() == this; - else - return QGraphicsItem::hasFocus(); + Q_D(const QFxItem); + return d->itemIsFocusedInScope; } void QFxItem::setFocus(bool focus) { - QGraphicsScene *s = scene(); - if (!s) { - if (focus) QGraphicsItem::setFocus(Qt::OtherFocusReason); - else QGraphicsItem::clearFocus(); - focusChanged(focus); - return; - } - - QGraphicsItem *current = this->parentItem(); - while (current && !(current->flags() & ItemAutoDetectsFocusProxy)) - current = current->parentItem(); - - if (!current) { - if (focus) QGraphicsItem::setFocus(Qt::OtherFocusReason); - else QGraphicsItem::clearFocus(); - focusChanged(focus); - return; - } - - if (current->focusProxy() && current->focusProxy() != this) { - QFxItem *currentItem = qobject_cast(current->focusProxy()); - if (currentItem) - currentItem->setFocus(false); - } - - if (current->focusProxy() == this && !focus) - current->setFocusProxy(0); - else if (focus) - current->setFocusProxy(this); + if (focus) QGraphicsItem::setFocus(Qt::OtherFocusReason); + else QGraphicsItem::clearFocus(); +} - focusChanged(focus); +void QFxItemPrivate::focusedInScopeChanged() +{ + Q_Q(QFxItem); + q->focusChanged(q->hasFocus()); } /*! diff --git a/src/declarative/fx/qfxitem_p.h b/src/declarative/fx/qfxitem_p.h index d30e324..054bdc7 100644 --- a/src/declarative/fx/qfxitem_p.h +++ b/src/declarative/fx/qfxitem_p.h @@ -223,6 +223,9 @@ public: QGraphicsItemPrivate::setPosHelper(pos); q->geometryChanged(QRectF(this->pos.x(), this->pos.y(), width, height), oldGeometry); } + + // Inherited from QGraphcisItemPrivate + virtual void focusedInScopeChanged(); }; QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxlistview.cpp b/src/declarative/fx/qfxlistview.cpp index 048cb0f..9498854 100644 --- a/src/declarative/fx/qfxlistview.cpp +++ b/src/declarative/fx/qfxlistview.cpp @@ -396,7 +396,7 @@ public: void QFxListViewPrivate::init() { Q_Q(QFxListView); - q->setFlag(QGraphicsItem::ItemAutoDetectsFocusProxy); + q->setFlag(QGraphicsItem::ItemIsFocusScope); QObject::connect(q, SIGNAL(heightChanged()), q, SLOT(refill())); QObject::connect(q, SIGNAL(widthChanged()), q, SLOT(refill())); } diff --git a/src/declarative/fx/qfxpathview_p.h b/src/declarative/fx/qfxpathview_p.h index a69f75f..60bfdb3 100644 --- a/src/declarative/fx/qfxpathview_p.h +++ b/src/declarative/fx/qfxpathview_p.h @@ -87,7 +87,7 @@ public: Q_Q(QFxPathView); _offset = 0; q->setAcceptedMouseButtons(Qt::LeftButton); - q->setFlag(QGraphicsItem::ItemAutoDetectsFocusProxy); + q->setFlag(QGraphicsItem::ItemIsFocusScope); q->setFiltersChildEvents(true); q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked())); } diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 620f6f4..ead5aee 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -331,10 +331,6 @@ \value ItemNegativeZStacksBehindParent The item automatically stacks behind it's parent if it's z-value is negative. This flag enables setZValue() to toggle ItemStacksBehindParent. - - \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. */ /*! @@ -944,17 +940,6 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) parent->itemChange(QGraphicsItem::ItemChildRemovedChange, thisPointerVariant); } - // Auto-update focus proxy. Any ancestor that has this as focus proxy - //needs to be nulled. - QGraphicsItem *p = parent; - while (p) { - if ((p->d_ptr->flags & QGraphicsItem::ItemAutoDetectsFocusProxy) && - (p->focusProxy() == q)) { - p->setFocusProxy(0); - } - p = p->d_ptr->parent; - } - // Update toplevelitem list. If this item is being deleted, its parent // will be 0 but we don't want to register/unregister it in the TLI list. if (scene && !inDestructor) { @@ -969,7 +954,11 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) bool implicitUpdate = false; if (parent->d_func()->scene && parent->d_func()->scene != scene) { // Move this item to its new parent's scene - parent->d_func()->scene->addItem(q); + + QGraphicsItem *focusScope = newParent; + while (focusScope && !(focusScope->d_ptr->flags & QGraphicsItem::ItemIsFocusScope)) + focusScope = focusScope->d_ptr->parent; + parent->d_func()->scene->d_func()->addItem(q, focusScope); implicitUpdate = true; } else if (!parent->d_func()->scene && scene) { // Remove this item from its former scene @@ -1031,17 +1020,6 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) 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. - 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); @@ -2663,11 +2641,27 @@ void QGraphicsItem::setFocus(Qt::FocusReason focusReason) // Update the scene's focus item. if (d_ptr->scene) { + QGraphicsItem *s = d_ptr->focusScope(); + if (s) { + bool scopeHasFocus = + s->d_ptr->hasActiveFocus(d_ptr->scene->focusItem()); + if (s->d_ptr->focusScopeItem) + s->d_ptr->focusScopeItem->d_ptr->setItemFocusedInScope(false); + s->d_ptr->focusScopeItem = this; + d_ptr->setItemFocusedInScope(true); + if (scopeHasFocus) + d_ptr->scene->d_func()->setFocusItemHelper(s, focusReason); + return; + } + d_ptr->setItemFocusedInScope(false); + QGraphicsWidget *w = window(); if (!w || w->isActiveWindow()) { // Visible items immediately gain focus from scene. d_ptr->scene->d_func()->setFocusItemHelper(f, focusReason); } + } else { + d_ptr->setItemFocusedInScope(true); } } @@ -2684,12 +2678,31 @@ void QGraphicsItem::setFocus(Qt::FocusReason focusReason) */ void QGraphicsItem::clearFocus() { - if (!d_ptr->scene) + if (!d_ptr->scene) { + d_ptr->setItemFocusedInScope(false); return; + } // Invisible items with focus must explicitly clear subfocus. d_ptr->clearSubFocus(); + + if (d_ptr->itemIsFocusedInScope) { + d_ptr->setItemFocusedInScope(false); + QGraphicsItem *s = d_ptr->focusScope(); + if (s) { + if (s->d_ptr->focusScopeItem == this) { + bool scopeHasFocus = + s->d_ptr->hasActiveFocus(d_ptr->scene->focusItem()); + s->d_ptr->focusScopeItem = 0; + if (scopeHasFocus) + d_ptr->scene->setFocusItem(s); + return; + } + } + } + if (hasFocus()) { // If this item has the scene's input focus, clear it. + d_ptr->setItemFocusedInScope(false); d_ptr->scene->setFocusItem(0); } } @@ -2700,7 +2713,7 @@ void QGraphicsItem::clearFocus() Returns this item's focus proxy, or 0 if this item has no focus proxy. - \sa setFocusProxy(), ItemAutoDetectsFocusProxy, setFocus(), hasFocus() + \sa setFocusProxy(), setFocus(), hasFocus() */ QGraphicsItem *QGraphicsItem::focusProxy() const { @@ -2724,7 +2737,7 @@ QGraphicsItem *QGraphicsItem::focusProxy() const The focus proxy \a item must belong to the same scene as this item. - \sa focusProxy(), ItemAutoDetectsFocusProxy, setFocus(), hasFocus() + \sa focusProxy(), setFocus(), hasFocus() */ void QGraphicsItem::setFocusProxy(QGraphicsItem *item) { @@ -4770,7 +4783,7 @@ void QGraphicsItemPrivate::setSubFocus() bool hidden = !visible; do { parent->d_func()->subFocusItem = item; - } while (!parent->isWindow() && (parent = parent->d_ptr->parent) && (!hidden || !parent->d_func()->visible)); + } while (!parent->isWindow() && (parent = parent->d_ptr->parent) && (!hidden || !parent->d_func()->visible) && !(parent->d_ptr->flags & QGraphicsItem::ItemIsFocusScope)); } /*! @@ -10437,8 +10450,8 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) case QGraphicsItem::ItemNegativeZStacksBehindParent: str = "ItemNegativeZStacksBehindParent"; break; - case QGraphicsItem::ItemAutoDetectsFocusProxy: - str = "ItemAutoDetectsFocusProxy"; + case QGraphicsItem::ItemIsFocusScope: + str = "ItemIsFocusScope"; break; } debug << str; diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 7af7c2f..b76f5ac 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -103,8 +103,8 @@ public: ItemHasNoContents = 0x400, ItemSendsGeometryChanges = 0x800, ItemAcceptsInputMethod = 0x1000, - ItemAutoDetectsFocusProxy = 0x2000, - ItemNegativeZStacksBehindParent = 0x4000 + ItemNegativeZStacksBehindParent = 0x4000, + ItemIsFocusScope = 0x8000 // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag. }; Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag) diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 11f6f53..7abd7f5 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -97,6 +97,8 @@ public: void purge(); }; +#include + class Q_GUI_EXPORT QGraphicsItemPrivate { Q_DECLARE_PUBLIC(QGraphicsItem) @@ -128,6 +130,7 @@ public: siblingIndex(-1), itemDepth(-1), focusProxy(0), + focusScopeItem(0), subFocusItem(0), imHints(Qt::ImhNone), acceptedMouseButtons(0x1f), @@ -171,6 +174,7 @@ public: notifyBoundingRectChanged(0), notifyInvalidated(0), mouseSetsFocus(1), + itemIsFocusedInScope(0), globalStackingOrder(-1), q_ptr(0) { @@ -410,6 +414,38 @@ public: void clearSubFocus(); void resetFocusProxy(); + inline QGraphicsItem *rootLevelFocusItem() const { + QGraphicsItem *fi = q_ptr; + while(QGraphicsItem *i = fi->d_ptr->focusScope()) + fi = i; + return fi; + } + + inline QGraphicsItem *focusScope() const { + QGraphicsItem *item = parent; + while (item) { + if (item->isWindow()) + return 0; + if (item->d_ptr->flags & QGraphicsItem::ItemIsFocusScope) + return item; + item = item->d_ptr->parent; + } + return 0; + } + inline bool hasActiveFocus(QGraphicsItem *focusItem) const { + if (!(flags & QGraphicsItem::ItemIsFocusScope) || !focusScopeItem) + return focusItem == q_ptr; + else + return focusScopeItem->d_ptr->hasActiveFocus(focusItem); + } + inline void setItemFocusedInScope(bool f) { + if (itemIsFocusedInScope != f) { + itemIsFocusedInScope = f; + if (!inDestructor) focusedInScopeChanged(); + } + } + inline virtual void focusedInScopeChanged() {} + inline QTransform transformToParent() const; inline void ensureSortedChildren(); @@ -431,6 +467,7 @@ public: int siblingIndex; int itemDepth; // Lazily calculated when calling depth(). QGraphicsItem *focusProxy; + QGraphicsItem *focusScopeItem; // Only used if this is a focus scope QList focusProxyRefs; QGraphicsItem *subFocusItem; Qt::InputMethodHints imHints; @@ -463,7 +500,7 @@ public: // New 32 bits quint32 fullUpdatePending : 1; - quint32 flags : 15; + quint32 flags : 16; quint32 dirtyChildrenBoundingRect : 1; quint32 paintedViewBoundingRectsNeedRepaint : 1; quint32 dirtySceneTransform : 1; @@ -479,6 +516,7 @@ public: quint32 notifyBoundingRectChanged : 1; quint32 notifyInvalidated : 1; quint32 mouseSetsFocus : 1; + quint32 itemIsFocusedInScope : 1; quint32 unused : 1; // feel free to use // Optional stacking order diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index a819822..73dba7b 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -572,6 +572,10 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, Qt::FocusReason focusReason) { Q_Q(QGraphicsScene); + + while (item && item->d_ptr->focusScopeItem) + item = item->d_ptr->focusScopeItem; + if (item == focusItem) return; @@ -589,22 +593,18 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, 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; - } - } + QGraphicsItem *itemRootLevelFocusItem = + item?item->d_ptr->rootLevelFocusItem():0; if (focusItem) { QFocusEvent event(QEvent::FocusOut, focusReason); lastFocusItem = focusItem; + + QGraphicsItem *oldRootLevelFocusItem = + focusItem->d_ptr->rootLevelFocusItem(); + if (oldRootLevelFocusItem != itemRootLevelFocusItem) + oldRootLevelFocusItem->d_ptr->setItemFocusedInScope(false); + focusItem = 0; sendEvent(lastFocusItem, &event); @@ -622,6 +622,8 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, if (item) { focusItem = item; + itemRootLevelFocusItem->d_ptr->setItemFocusedInScope(true); + item->d_ptr->setItemFocusedInScope(true); QFocusEvent event(QEvent::FocusIn, focusReason); sendEvent(item, &event); } @@ -2240,11 +2242,19 @@ void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group) void QGraphicsScene::addItem(QGraphicsItem *item) { Q_D(QGraphicsScene); + d->addItem(item); +} + +void QGraphicsScenePrivate::addItem(QGraphicsItem *item, + QGraphicsItem *focusScope) +{ + Q_Q(QGraphicsScene); + if (!item) { qWarning("QGraphicsScene::addItem: cannot add null item"); return; } - if (item->scene() == this) { + if (item->scene() == q) { qWarning("QGraphicsScene::addItem: item has already been added to this scene"); return; } @@ -2255,9 +2265,9 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // Notify the item that its scene is changing, and allow the item to // react. const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange, - qVariantFromValue(this))); + qVariantFromValue(q))); QGraphicsScene *targetScene = qVariantValue(newSceneVariant); - if (targetScene != this) { + if (targetScene != q) { if (targetScene && item->scene() != targetScene) targetScene->addItem(item); return; @@ -2266,7 +2276,7 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // Detach this item from its parent if the parent's scene is different // from this scene. if (QGraphicsItem *itemParent = item->parentItem()) { - if (itemParent->scene() != this) + if (itemParent->scene() != q) item->setParentItem(0); } @@ -2274,98 +2284,113 @@ void QGraphicsScene::addItem(QGraphicsItem *item) item->d_func()->scene = targetScene; // Add the item in the index - d->index->addItem(item); + index->addItem(item); // Add to list of toplevels if this item is a toplevel. if (!item->d_ptr->parent) - d->registerTopLevelItem(item); + registerTopLevelItem(item); // Add to list of items that require an update. We cannot assume that the // item is fully constructed, so calling item->update() can lead to a pure // virtual function call to boundingRect(). - d->markDirty(item); - d->dirtyGrowingItemsBoundingRect = true; + markDirty(item); + dirtyGrowingItemsBoundingRect = true; // Disable selectionChanged() for individual items - ++d->selectionChanging; - int oldSelectedItemSize = d->selectedItems.size(); + ++selectionChanging; + int oldSelectedItemSize = selectedItems.size(); // Enable mouse tracking if the item accepts hover events or has a cursor set. - if (d->allItemsIgnoreHoverEvents && d->itemAcceptsHoverEvents_helper(item)) { - d->allItemsIgnoreHoverEvents = false; - d->enableMouseTrackingOnViews(); + if (allItemsIgnoreHoverEvents && itemAcceptsHoverEvents_helper(item)) { + allItemsIgnoreHoverEvents = false; + enableMouseTrackingOnViews(); } #ifndef QT_NO_CURSOR - if (d->allItemsUseDefaultCursor && item->hasCursor()) { - d->allItemsUseDefaultCursor = false; - if (d->allItemsIgnoreHoverEvents) // already enabled otherwise - d->enableMouseTrackingOnViews(); + if (allItemsUseDefaultCursor && item->hasCursor()) { + allItemsUseDefaultCursor = false; + if (allItemsIgnoreHoverEvents) // already enabled otherwise + enableMouseTrackingOnViews(); } #endif //QT_NO_CURSOR // Enable touch events if the item accepts touch events. - if (d->allItemsIgnoreTouchEvents && item->acceptTouchEvents()) { - d->allItemsIgnoreTouchEvents = false; - d->enableTouchEventsOnViews(); + if (allItemsIgnoreTouchEvents && item->acceptTouchEvents()) { + allItemsIgnoreTouchEvents = false; + enableTouchEventsOnViews(); } // Update selection lists if (item->isSelected()) - d->selectedItems << item; + selectedItems << item; if (item->isWidget() && item->isVisible() && static_cast(item)->windowType() == Qt::Popup) - d->addPopup(static_cast(item)); + addPopup(static_cast(item)); // Update creation order focus chain. Make sure to leave the widget's // internal tab order intact. if (item->isWidget()) { QGraphicsWidget *widget = static_cast(item); - if (!d->tabFocusFirst) { + if (!tabFocusFirst) { // No first tab focus widget - make this the first tab focus // widget. - d->tabFocusFirst = widget; + tabFocusFirst = widget; } else if (!widget->parentWidget()) { // Adding a widget that is not part of a tab focus chain. - QGraphicsWidget *last = d->tabFocusFirst->d_func()->focusPrev; + QGraphicsWidget *last = tabFocusFirst->d_func()->focusPrev; QGraphicsWidget *lastNew = widget->d_func()->focusPrev; last->d_func()->focusNext = widget; widget->d_func()->focusPrev = last; - d->tabFocusFirst->d_func()->focusPrev = lastNew; - lastNew->d_func()->focusNext = d->tabFocusFirst; + tabFocusFirst->d_func()->focusPrev = lastNew; + lastNew->d_func()->focusNext = tabFocusFirst; } } // Add all children recursively + QGraphicsItem *subFocusScope = + (item->d_ptr->flags & QGraphicsItem::ItemIsFocusScope)?item:focusScope; foreach (QGraphicsItem *child, item->children()) - addItem(child); + addItem(child, subFocusScope); // Resolve font and palette. - item->d_ptr->resolveFont(d->font.resolve()); - item->d_ptr->resolvePalette(d->palette.resolve()); + item->d_ptr->resolveFont(font.resolve()); + item->d_ptr->resolvePalette(palette.resolve()); if (!item->d_ptr->explicitlyHidden) { - if (d->unpolishedItems.isEmpty()) - QMetaObject::invokeMethod(this, "_q_polishItems", Qt::QueuedConnection); - d->unpolishedItems << item; + if (unpolishedItems.isEmpty()) + QMetaObject::invokeMethod(q, "_q_polishItems", Qt::QueuedConnection); + unpolishedItems << item; } // Reenable selectionChanged() for individual items - --d->selectionChanging; - if (!d->selectionChanging && d->selectedItems.size() != oldSelectedItemSize) - emit selectionChanged(); + --selectionChanging; + if (!selectionChanging && selectedItems.size() != oldSelectedItemSize) + emit q->selectionChanged(); // Deliver post-change notification item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant); // Auto-activate the first inactive window if the scene is active. - if (d->activationRefCount > 0 && !d->activeWindow && item->isWindow()) - setActiveWindow(static_cast(item)); + if (activationRefCount > 0 && !activeWindow && item->isWindow()) + q->setActiveWindow(static_cast(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()) + if (!focusItem && item->focusItem()) item->focusItem()->setFocus(); - d->updateInputMethodSensitivityInViews(); + // Ensure that focus scopes set themselves up at add time + if (item->d_ptr->itemIsFocusedInScope) { + if (focusScope && !focusScope->d_ptr->focusScopeItem) { + item->setFocus(); + } else if (!(focusScope && focusScope->d_ptr->focusScopeItem == item) && + !(focusItem && focusItem->d_ptr->rootLevelFocusItem() == item)) { + // We unset the focused in scope variable in this item if: + // It's not scope focused in the closest focus scope, and + // It's not the root-level item for the globally focused item + item->d_ptr->setItemFocusedInScope(false); + } + } + + updateInputMethodSensitivityInViews(); } /*! diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 4b8791e..9e62901 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -124,6 +124,8 @@ public: QBrush backgroundBrush; QBrush foregroundBrush; + void addItem(QGraphicsItem *, QGraphicsItem *focusScope = 0); + bool stickyFocus; bool hasFocus; QGraphicsItem *focusItem; diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index e9154d4..b1835c8 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -161,7 +161,6 @@ public slots: void init(); private slots: - void explicitDeleteAutoFocusProxy(); void construction(); void constructionWithParent(); void destruction(); @@ -281,9 +280,7 @@ private slots: void hitTestUntransformableItem(); void hitTestGraphicsEffectItem(); void focusProxy(); - void autoDetectFocusProxy(); void subFocus(); - void reverseCreateAutoFocusProxy(); void focusProxyDeletion(); void negativeZStacksBehindParent(); void setGraphicsEffect(); @@ -7540,31 +7537,6 @@ 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) @@ -7624,49 +7596,6 @@ void tst_QGraphicsItem::subFocus() 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()); -} - -void tst_QGraphicsItem::explicitDeleteAutoFocusProxy() -{ - 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()); - - delete text2; - QCOMPARE(text->focusProxy(), (QGraphicsItem *)0); -} - void tst_QGraphicsItem::focusProxyDeletion() { QGraphicsRectItem *rect = new QGraphicsRectItem; -- cgit v0.12