diff options
-rw-r--r-- | src/widgets/graphicsview/qgraphicsitem.cpp | 49 | ||||
-rw-r--r-- | src/widgets/graphicsview/qgraphicsscene.cpp | 21 | ||||
-rw-r--r-- | src/widgets/graphicsview/qgraphicswidget_p.cpp | 104 | ||||
-rw-r--r-- | tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp | 103 |
4 files changed, 198 insertions, 79 deletions
diff --git a/src/widgets/graphicsview/qgraphicsitem.cpp b/src/widgets/graphicsview/qgraphicsitem.cpp index c30435e56a..cd379e1b05 100644 --- a/src/widgets/graphicsview/qgraphicsitem.cpp +++ b/src/widgets/graphicsview/qgraphicsitem.cpp @@ -1884,15 +1884,46 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) d_ptr->scene->d_func()->updateInputMethodSensitivityInViews(); } + if ((flags & ItemIsPanel) != (oldFlags & ItemIsPanel)) { + bool becomesPanel = (flags & ItemIsPanel); + if ((d_ptr->panelModality != NonModal) && d_ptr->scene) { + // update the panel's modal state + if (becomesPanel) + d_ptr->scene->d_func()->enterModal(this); + else + d_ptr->scene->d_func()->leaveModal(this); + } + if (d_ptr->isWidget && (becomesPanel || parentWidget())) { + QGraphicsWidget *w = static_cast<QGraphicsWidget *>(this); + QGraphicsWidget *focusFirst = w; + QGraphicsWidget *focusLast = w; + for (;;) { + QGraphicsWidget *test = focusLast->d_func()->focusNext; + if (!w->isAncestorOf(test) || test == w) + break; + focusLast = test; + } - if ((d_ptr->panelModality != NonModal) - && d_ptr->scene - && (flags & ItemIsPanel) != (oldFlags & ItemIsPanel)) { - // update the panel's modal state - if (flags & ItemIsPanel) - d_ptr->scene->d_func()->enterModal(this); - else - d_ptr->scene->d_func()->leaveModal(this); + if (becomesPanel) { + // unlink own widgets from focus chain + QGraphicsWidget *beforeMe = w->d_func()->focusPrev; + QGraphicsWidget *afterMe = focusLast->d_func()->focusNext; + beforeMe->d_func()->focusNext = afterMe; + afterMe->d_func()->focusPrev = beforeMe; + focusFirst->d_func()->focusPrev = focusLast; + focusLast->d_func()->focusNext = focusFirst; + if (!isAncestorOf(focusFirst->d_func()->focusNext)) + focusFirst->d_func()->focusNext = w; + } else if (QGraphicsWidget *pw = parentWidget()) { + // link up own widgets to focus chain + QGraphicsWidget *beforeMe = pw; + QGraphicsWidget *afterMe = pw->d_func()->focusNext; + beforeMe->d_func()->focusNext = w; + afterMe->d_func()->focusPrev = focusLast; + w->d_func()->focusPrev = beforeMe; + focusLast->d_func()->focusNext = afterMe; + } + } } if (d_ptr->scene) { @@ -2274,7 +2305,7 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, scene->d_func()->leaveModal(q_ptr); } if (hasFocus && scene) { - // Hiding the closest non-panel ancestor of the focus item + // Hiding the focus item or the closest non-panel ancestor of the focus item QGraphicsItem *focusItem = scene->focusItem(); bool clear = true; if (isWidget && !focusItem->isPanel()) { diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index ee65fb8fa6..769b488f39 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -607,7 +607,7 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) q->removeItem(item->d_ptr->children.at(i)); } - if (!item->d_ptr->inDestructor && item == tabFocusFirst) { + if (!item->d_ptr->inDestructor && !item->parentItem() && item->isWidget()) { QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); widget->d_func()->fixFocusChainBeforeReparenting(0, oldScene, 0); } @@ -2528,14 +2528,13 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // No first tab focus widget - make this the first tab focus // widget. d->tabFocusFirst = widget; - } else if (!widget->parentWidget()) { + } else if (!widget->parentWidget() && !widget->isPanel()) { // Adding a widget that is not part of a tab focus chain. - QGraphicsWidget *last = d->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; + QGraphicsWidget *myNewPrev = d->tabFocusFirst->d_func()->focusPrev; + myNewPrev->d_func()->focusNext = widget; + widget->d_func()->focusPrev->d_func()->focusNext = d->tabFocusFirst; + d->tabFocusFirst->d_func()->focusPrev = widget->d_func()->focusPrev; + widget->d_func()->focusPrev = myNewPrev; } } @@ -5330,7 +5329,7 @@ bool QGraphicsScene::focusNextPrevChild(bool next) return true; } } - if (!d->tabFocusFirst) { + if (!item && !d->tabFocusFirst) { // No widgets... return false; } @@ -5342,8 +5341,10 @@ bool QGraphicsScene::focusNextPrevChild(bool next) } else { QGraphicsWidget *test = static_cast<QGraphicsWidget *>(item); widget = next ? test->d_func()->focusNext : test->d_func()->focusPrev; - if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev)) + if (!widget->panel() && ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev))) { + // Tab out of the scene. return false; + } } QGraphicsWidget *widgetThatHadFocus = widget; diff --git a/src/widgets/graphicsview/qgraphicswidget_p.cpp b/src/widgets/graphicsview/qgraphicswidget_p.cpp index 4ec215a720..dfebbb5036 100644 --- a/src/widgets/graphicsview/qgraphicswidget_p.cpp +++ b/src/widgets/graphicsview/qgraphicswidget_p.cpp @@ -764,73 +764,59 @@ bool QGraphicsWidgetPrivate::hasDecoration() const void QGraphicsWidgetPrivate::fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *oldScene, QGraphicsScene *newScene) { Q_Q(QGraphicsWidget); - Q_ASSERT(focusNext && focusPrev); - QGraphicsWidget *n = q; //last one in 'new' list - QGraphicsWidget *o = 0; //last one in 'old' list - - QGraphicsWidget *w = focusNext; - - QGraphicsWidget *firstOld = 0; - bool wasPreviousNew = true; - - while (w != q) { - bool isCurrentNew = q->isAncestorOf(w); - if (isCurrentNew) { - if (!wasPreviousNew) { - n->d_func()->focusNext = w; - w->d_func()->focusPrev = n; - } - n = w; - } else /*if (!isCurrentNew)*/ { - if (wasPreviousNew) { - if (o) { - o->d_func()->focusNext = w; - w->d_func()->focusPrev = o; - } else { - firstOld = w; - } - } - o = w; - } - w = w->d_func()->focusNext; - wasPreviousNew = isCurrentNew; - } - - // repair the 'old' chain - if (firstOld) { - o->d_func()->focusNext = firstOld; - firstOld->d_func()->focusPrev = o; + if (q_ptr->isPanel()) { + // panels are never a part of their parent's or ancestors' focus + // chains. so reparenting a panel is easy; there's nothing to + // do. + return; } - // update tabFocusFirst for oldScene if the item is going to be removed from oldScene - if (newParent) - newScene = newParent->scene(); - - if (oldScene && newScene != oldScene) - oldScene->d_func()->tabFocusFirst = (firstOld && firstOld->scene() == oldScene) ? firstOld : 0; - - QGraphicsItem *topLevelItem = newParent ? newParent->topLevelItem() : 0; - QGraphicsWidget *topLevel = 0; - if (topLevelItem && topLevelItem->isWidget()) - topLevel = static_cast<QGraphicsWidget *>(topLevelItem); + // we're not a panel, so find the first widget in the focus chain + // (this), and the last (this, or the last widget that is still + // a descendent of this). also find the widgets that currently / + // before reparenting point to this widgets' focus chain. + QGraphicsWidget *focusFirst = q; + QGraphicsWidget *focusBefore = focusPrev; + QGraphicsWidget *focusLast = focusFirst; + QGraphicsWidget *focusAfter = focusNext; + do { + if (!q->isAncestorOf(focusAfter)) + break; + focusLast = focusAfter; + } while ((focusAfter = focusAfter->d_func()->focusNext)); - if (topLevel && newParent) { - QGraphicsWidget *last = topLevel->d_func()->focusPrev; - // link last with new chain - last->d_func()->focusNext = q; - focusPrev = last; + if (!parent && oldScene && oldScene != newScene && oldScene->d_func()->tabFocusFirst == q) { + // detach from old scene's top level focus chain. + oldScene->d_func()->tabFocusFirst = (focusAfter != q) ? focusAfter : 0; + } - // link last in chain with - topLevel->d_func()->focusPrev = n; - n->d_func()->focusNext = topLevel; + // detach from current focus chain; skip this widget subtree. + focusBefore->d_func()->focusNext = focusAfter; + focusAfter->d_func()->focusPrev = focusBefore; + + if (newParent) { + // attach to new parent's focus chain as the last element + // in its chain. + QGraphicsWidget *newFocusFirst = newParent; + QGraphicsWidget *newFocusLast = newFocusFirst; + QGraphicsWidget *newFocusAfter = newFocusFirst->d_func()->focusNext; + do { + if (!newParent->isAncestorOf(newFocusAfter)) + break; + newFocusLast = newFocusAfter; + } while ((newFocusAfter = newFocusAfter->d_func()->focusNext)); + + newFocusLast->d_func()->focusNext = q; + focusLast->d_func()->focusNext = newFocusAfter; + newFocusAfter->d_func()->focusPrev = focusLast; + focusPrev = newFocusLast; } else { - // q is the start of the focus chain - n->d_func()->focusNext = q; - focusPrev = n; + // no new parent, so just link up our own prev->last widgets. + focusPrev = focusLast; + focusLast->d_func()->focusNext = q; } - } void QGraphicsWidgetPrivate::setLayout_helper(QGraphicsLayout *l) diff --git a/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp index 901bb7c38a..ad8fa3f241 100644 --- a/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp @@ -172,12 +172,12 @@ private slots: void initialShow2(); void itemChangeEvents(); void itemSendGeometryPosChangesDeactivated(); - void fontPropagatesResolveToChildren(); void fontPropagatesResolveToGrandChildren(); void fontPropagatesResolveInParentChange(); void fontPropagatesResolveViaNonWidget(); void fontPropagatesResolveFromScene(); + void tabFocus(); // Task fixes void task236127_bspTreeIndexFails(); @@ -3303,6 +3303,107 @@ void tst_QGraphicsWidget::itemSendGeometryPosChangesDeactivated() QCOMPARE(item->geometry(), QRectF(10, 10, 60, 60)); } +class TabFocusWidget : public QGraphicsWidget +{ + Q_OBJECT +public: + TabFocusWidget(const QString &name, QGraphicsItem *parent = 0) + : QGraphicsWidget(parent) + { setFocusPolicy(Qt::TabFocus); setData(0, name); } +}; + +void verifyTabFocus(QGraphicsScene *scene, const QList<QGraphicsWidget *> &chain, bool wrapsAround) +{ + QKeyEvent tabEvent(QEvent::KeyPress, Qt::Key_Tab, 0); + QKeyEvent backtabEvent(QEvent::KeyPress, Qt::Key_Backtab, 0); + + for (int i = 0; i < chain.size(); ++i) + chain.at(i)->clearFocus(); + + int n = chain.size() * (wrapsAround ? 3 : 1); + for (int i = 0; i < n; ++i) + { + if (i == 0) + chain.at(0)->setFocus(); + else + qApp->sendEvent(scene, &tabEvent); + QVERIFY(chain.at(i % chain.size())->hasFocus()); + QCOMPARE(scene->focusItem(), chain.at(i % chain.size())); + } + for (int i = n - 2; i >= 0; --i) + { + qApp->sendEvent(scene, &backtabEvent); + QVERIFY(chain.at(i % chain.size())->hasFocus()); + QCOMPARE(scene->focusItem(), chain.at(i % chain.size())); + } +} + +void tst_QGraphicsWidget::tabFocus() +{ + QGraphicsScene scene; + scene.setFocus(); + + QEvent activate(QEvent::WindowActivate); + qApp->sendEvent(&scene, &activate); + + TabFocusWidget *widget = new TabFocusWidget("1"); + scene.addItem(widget); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget, false); + + TabFocusWidget *widget2 = new TabFocusWidget("2"); + scene.addItem(widget2); + scene.setFocusItem(0); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget << widget2, false); + + TabFocusWidget *widget3 = new TabFocusWidget("3"); + widget3->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(widget3); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)widget3); + scene.setActivePanel(0); + scene.setFocusItem(0); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget << widget2, false); + + scene.setActivePanel(widget3); + widget3->setFocus(); + QCOMPARE(scene.focusItem(), (QGraphicsItem *)widget3); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3, true); + + TabFocusWidget *widget4 = new TabFocusWidget("4"); + widget4->setParentItem(widget3); + QVERIFY(widget3->hasFocus()); + widget3->clearFocus(); + QVERIFY(!widget3->focusItem()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)widget3); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3 << widget4, true); + + QGraphicsWidget *widget5 = new QGraphicsWidget; widget5->setData(0, QLatin1String("5")); + widget5->setParentItem(widget3); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3 << widget4, true); + + widget5->setFocusPolicy(Qt::TabFocus); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3 << widget4 << widget5, true); + + TabFocusWidget *widget6 = new TabFocusWidget("6"); + widget6->setParentItem(widget4); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3 << widget4 << widget6 << widget5, true); + + TabFocusWidget *widget7 = new TabFocusWidget("7", widget6); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3 << widget4 << widget6 << widget7 << widget5, true); + + TabFocusWidget *widget8 = new TabFocusWidget("8", widget6); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3 << widget4 << widget6 << widget7 << widget8 << widget5, true); + widget6->setFlag(QGraphicsItem::ItemIsPanel); + widget6->setActive(true); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget6 << widget7 << widget8, true); + widget3->setActive(true); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3 << widget4 << widget5, true); + widget6->setFlag(QGraphicsItem::ItemIsPanel, false); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3 << widget4 << widget6 << widget7 << widget8 << widget5, true); + scene.removeItem(widget6); + verifyTabFocus(&scene, QList<QGraphicsWidget *>() << widget3 << widget4 << widget5, true); + delete widget6; +} + void tst_QGraphicsWidget::QT_BUG_6544_tabFocusFirstUnsetWhenRemovingItems() { QGraphicsScene scene; |