diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index 33e9c8f3e88..aa4b4441859 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -1359,6 +1359,8 @@ QDockWidget::QDockWidget(const QString &title, QWidget *parent, Qt::WindowFlags */ QDockWidget::~QDockWidget() { + Q_D(QDockWidget); + d->inDestructor = true; // Do all the unregistering while we're still a QDockWidget. Otherwise, it // would be ~QObject() which does that and then QDockAreaLayout::takeAt(), // acting on QEvent::ChildRemoved, will try to access our QWidget-ness when @@ -1645,8 +1647,12 @@ bool QDockWidget::event(QEvent *event) case QEvent::Hide: if (layout != nullptr) layout->keepSize(this); - d->toggleViewAction->setChecked(false); - emit visibilityChanged(false); + // If we are in the destructor, don't emit any signals, as those might + // be handled by a slot that requires this dock widget to still be alive. + if (!d->inDestructor) { + d->toggleViewAction->setChecked(false); + emit visibilityChanged(false); + } break; case QEvent::Show: { d->toggleViewAction->setChecked(true); diff --git a/src/widgets/widgets/qdockwidget_p.h b/src/widgets/widgets/qdockwidget_p.h index 15544280669..477f1a4b83e 100644 --- a/src/widgets/widgets/qdockwidget_p.h +++ b/src/widgets/widgets/qdockwidget_p.h @@ -94,6 +94,7 @@ public: QRect undockedGeometry; QString fixedWindowTitle; QString dockedWindowTitle; + bool inDestructor = false; bool mousePressEvent(QMouseEvent *event); bool mouseDoubleClickEvent(QMouseEvent *event); diff --git a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp index e7c6bc1cff5..9fe4dcec7f0 100644 --- a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp +++ b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp @@ -46,6 +46,8 @@ private slots: void allowedAreas(); void toggleViewAction(); void visibilityChanged(); + void visibilityChangedOnDestruction_data(); + void visibilityChangedOnDestruction(); void updateTabBarOnVisibilityChanged(); void dockLocationChanged(); void setTitleBarWidget(); @@ -717,6 +719,45 @@ void tst_QDockWidget::visibilityChanged() QCOMPARE(spy.at(0).at(0).toBool(), true); } +// QTBUG-136485 - QDockWidget didn't emit visibilityChanged when getting +// destroyed until 6.9.0; it did in 6.9.0, causing regressions in applications. +// So make sure we don't emit that signal when a QDockWidget gets destroyed. +// When implicitly destroyed as a child of a QMainWindow, it gets hidden first, +// so it emits the signal. +void tst_QDockWidget::visibilityChangedOnDestruction_data() +{ + QTest::addColumn("explicitDestroy"); + QTest::addColumn("floating"); + QTest::addColumn("signalCount"); + + QTest::addRow("Explicit, docked") << true << false << 0; + QTest::addRow("Explicit, floating") << true << true << 0; + QTest::addRow("Implicit, docked") << false << false << 1; + QTest::addRow("Implicit, floating") << false << true << 0; +} + +void tst_QDockWidget::visibilityChangedOnDestruction() +{ + QFETCH(const bool, explicitDestroy); + QFETCH(const bool, floating); + QFETCH(const int, signalCount); + + std::unique_ptr mw(new QMainWindow); + QDockWidget *dw = new QDockWidget; + mw->addDockWidget(Qt::LeftDockWidgetArea, dw); + if (floating) + dw->setFloating(true); + mw->show(); + QVERIFY(QTest::qWaitForWindowExposed(mw.get())); + + QSignalSpy spy(dw, &QDockWidget::visibilityChanged); + if (explicitDestroy) + delete dw; + else + mw.reset(); + QCOMPARE(spy.count(), signalCount); +} + void tst_QDockWidget::updateTabBarOnVisibilityChanged() { // QTBUG49045: Populate tabified dock area with 4 widgets, set the tab