Repair QGraphicsWidget focus chain when used with ItemIsPanel.
Add handling of the focus chain to QGraphicsItem::setFlags(), so that the focus chain is repaired (panels pop out of the chain and non-panels merge back in) when the ItemIsPanel flag is toggled. Add handling focus chain to QGraphicsWidgetPrivate::fixFocusChainBeforeReparenting for panels. Before this fix, you must enable the ItemIsPanel flag before adding the item as a child to a parent panel, and you lose focus when using the tab key to focus around a panel after it has been reparented into another panel. Task-number: QTBUG-28187 Change-Id: I1d0d81a90697eaf715a8a337c8bf6c2159329e68 Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
parent
9c02a28552
commit
4a9c3b2433
@ -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()) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
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;
|
||||
}
|
||||
|
||||
// repair the 'old' chain
|
||||
if (firstOld) {
|
||||
o->d_func()->focusNext = firstOld;
|
||||
firstOld->d_func()->focusPrev = o;
|
||||
// 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 (!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;
|
||||
}
|
||||
|
||||
// update tabFocusFirst for oldScene if the item is going to be removed from oldScene
|
||||
if (newParent)
|
||||
newScene = newParent->scene();
|
||||
// detach from current focus chain; skip this widget subtree.
|
||||
focusBefore->d_func()->focusNext = focusAfter;
|
||||
focusAfter->d_func()->focusPrev = focusBefore;
|
||||
|
||||
if (oldScene && newScene != oldScene)
|
||||
oldScene->d_func()->tabFocusFirst = (firstOld && firstOld->scene() == oldScene) ? firstOld : 0;
|
||||
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));
|
||||
|
||||
QGraphicsItem *topLevelItem = newParent ? newParent->topLevelItem() : 0;
|
||||
QGraphicsWidget *topLevel = 0;
|
||||
if (topLevelItem && topLevelItem->isWidget())
|
||||
topLevel = static_cast<QGraphicsWidget *>(topLevelItem);
|
||||
|
||||
if (topLevel && newParent) {
|
||||
QGraphicsWidget *last = topLevel->d_func()->focusPrev;
|
||||
// link last with new chain
|
||||
last->d_func()->focusNext = q;
|
||||
focusPrev = last;
|
||||
|
||||
// link last in chain with
|
||||
topLevel->d_func()->focusPrev = n;
|
||||
n->d_func()->focusNext = topLevel;
|
||||
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)
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user