From 18f9eb797bffe8626f1edeca3c88f80dae0da8d7 Mon Sep 17 00:00:00 2001 From: Jan Arve Saether Date: Tue, 19 Feb 2013 15:02:24 +0100 Subject: 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 --- src/widgets/kernel/qstackedlayout.cpp | 27 ++++--- .../widgets/qstackedwidget/tst_qstackedwidget.cpp | 88 ++++++++++++++++++++++ 2 files changed, 103 insertions(+), 12 deletions(-) diff --git a/src/widgets/kernel/qstackedlayout.cpp b/src/widgets/kernel/qstackedlayout.cpp index cb2711c636..f38f0a6f08 100644 --- a/src/widgets/kernel/qstackedlayout.cpp +++ b/src/widgets/kernel/qstackedlayout.cpp @@ -300,7 +300,9 @@ void QStackedLayout::setCurrentIndex(int index) parent->setUpdatesEnabled(false); } - QWidget *fw = parent ? parent->window()->focusWidget() : 0; + QPointer 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(); } } } diff --git a/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp b/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp index 5c9f46095c..c17db4c7f3 100644 --- a/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp +++ b/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp @@ -47,6 +47,7 @@ #include #include #include +#include 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 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(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(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" -- cgit v1.2.3