QStackedLayout: Fix crash when focus widget is destroyed in hide()

We also have to make sure that when moving back to a page
that has a focusWidget(), the focus should go to the focusWidget()

Task-number: QTBUG-18242

Change-Id: Ibfa7d6361c1a456480b2f1584a88ef4c4f405709
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
bb10
Jan Arve Saether 2013-02-19 15:02:24 +01:00 committed by The Qt Project
parent 64106705e7
commit 18f9eb797b
2 changed files with 103 additions and 12 deletions

View File

@ -300,7 +300,9 @@ void QStackedLayout::setCurrentIndex(int index)
parent->setUpdatesEnabled(false);
}
QWidget *fw = parent ? parent->window()->focusWidget() : 0;
QPointer<QWidget> fw = parent ? parent->window()->focusWidget() : 0;
const bool focusWasOnOldPage = fw && (prev && prev->isAncestorOf(fw));
if (prev) {
prev->clearFocus();
if (d->stackingMode == StackOne)
@ -315,24 +317,25 @@ void QStackedLayout::setCurrentIndex(int index)
// was somewhere on the outgoing widget.
if (parent) {
if (fw && (prev && prev->isAncestorOf(fw))) { // focus was on old page
if (focusWasOnOldPage) {
// look for the best focus widget we can find
if (QWidget *nfw = next->focusWidget())
nfw->setFocus();
else {
// second best: first child widget in the focus chain
QWidget *i = fw;
while ((i = i->nextInFocusChain()) != fw) {
if (((i->focusPolicy() & Qt::TabFocus) == Qt::TabFocus)
&& !i->focusProxy() && i->isVisibleTo(next) && i->isEnabled()
&& next->isAncestorOf(i)) {
i->setFocus();
break;
if (QWidget *i = fw) {
while ((i = i->nextInFocusChain()) != fw) {
if (((i->focusPolicy() & Qt::TabFocus) == Qt::TabFocus)
&& !i->focusProxy() && i->isVisibleTo(next) && i->isEnabled()
&& next->isAncestorOf(i)) {
i->setFocus();
break;
}
}
// third best: incoming widget
if (i == fw )
next->setFocus();
}
// third best: incoming widget
if (i == fw )
next->setFocus();
}
}
}

View File

@ -47,6 +47,7 @@
#include <qstackedwidget.h>
#include <qpushbutton.h>
#include <QHBoxLayout>
#include <qlineedit.h>
class tst_QStackedWidget : public QObject
{
@ -59,6 +60,7 @@ public:
private slots:
void getSetCheck();
void testMinimumSize();
void dynamicPages();
};
tst_QStackedWidget::tst_QStackedWidget()
@ -117,5 +119,91 @@ void tst_QStackedWidget::getSetCheck()
delete var2;
}
// QTBUG-18242, a widget that deletes its children in hideEvent().
// This caused a crash in QStackedLayout::setCurrentIndex() since
// the focus widget was destroyed while hiding the previous page.
class TestPage : public QWidget
{
public:
TestPage (bool staticWidgets = false) : QWidget(0), m_staticWidgets(staticWidgets)
{
new QVBoxLayout (this);
}
~TestPage() {
destroyWidgets();
}
void setN(int n)
{
m_n = n;
if (m_staticWidgets)
createWidgets();
}
virtual void showEvent (QShowEvent *)
{
if (!m_staticWidgets)
createWidgets();
}
virtual void hideEvent (QHideEvent *)
{
if (!m_staticWidgets)
destroyWidgets();
}
private:
void createWidgets() {
for (int i = 0; i < m_n; ++i) {
QLineEdit *le = new QLineEdit(this);
le->setObjectName(QString::fromLatin1("lineEdit%1").arg(i));
layout ()->addWidget(le);
m_les << le;
}
}
void destroyWidgets()
{
qDeleteAll(m_les);
m_les.clear ();
}
int m_n;
const bool m_staticWidgets;
QList<QLineEdit*> m_les;
};
void tst_QStackedWidget::dynamicPages()
{
QStackedWidget *sw = new QStackedWidget;
TestPage *w1 = new TestPage(true);
w1->setN(3);
TestPage *w2 = new TestPage;
w2->setN(3);
sw->addWidget(w1);
sw->addWidget(w2);
QLineEdit *le11 = w1->findChild<QLineEdit*>(QLatin1String("lineEdit1"));
le11->setFocus(); // set focus to second widget in the page
sw->resize(200, 200);
sw->show();
qApp->setActiveWindow(sw);
QTest::qWaitForWindowActive(sw);
QTRY_COMPARE(QApplication::focusWidget(), le11);
sw->setCurrentIndex(1);
QLineEdit *le22 = w2->findChild<QLineEdit*>(QLatin1String("lineEdit2"));
le22->setFocus();
QTRY_COMPARE(QApplication::focusWidget(), le22);
// Going back should move focus back to le11
sw->setCurrentIndex(0);
QTRY_COMPARE(QApplication::focusWidget(), le11);
}
QTEST_MAIN(tst_QStackedWidget)
#include "tst_qstackedwidget.moc"