summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolker Hilsheimer <[email protected]>2025-05-15 13:56:06 +0300
committerQt Cherry-pick Bot <[email protected]>2025-05-16 07:36:31 +0000
commit31f1c78b0b57ea3c0c9d401a678c2a264ba581d0 (patch)
treebb171b326e7e8dbe03996df6faf1c880a41940f6
parentf10a8dbf6cbb729d7fba80ac5e5a01d873b3a96b (diff)
QDockWidget: don't emit visibilityChanged while in destructor
Amends 2c67d47ea15c6dc34cc20d8fbdb406efb19f11d7, after which we emitted the signal, as the setParent(nullptr) call in the destructor would call back into the QDockWidget::event override. That change was correct, but we are now emitted signals while in the destructor, after a potential subclass destructor was already completed. This crashed applications that had slots connected to those signals. While arguably an application problem (PMF connections need to be disconnected explicitly), we can avoid this regression by blocking the emission of that signal when already in the destructor. Fixes: QTBUG-136485 Change-Id: I6d5e98136beedc94c22287ccfd1198dd80f4f95e Reviewed-by: Axel Spoerl <[email protected]> (cherry picked from commit 7869593119ffaea6002e6668814af159a2077398) Reviewed-by: Qt Cherry-pick Bot <[email protected]> (cherry picked from commit e30e358ce6766e231d2a5528b8f7d9dc86092e0c)
-rw-r--r--src/widgets/widgets/qdockwidget.cpp10
-rw-r--r--src/widgets/widgets/qdockwidget_p.h1
-rw-r--r--tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp41
3 files changed, 50 insertions, 2 deletions
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<bool>("explicitDestroy");
+ QTest::addColumn<bool>("floating");
+ QTest::addColumn<int>("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<QMainWindow> 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