summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2023-12-19 22:07:56 +0100
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2024-01-18 10:46:05 +0000
commit0251fe61b25b2f475ce46ac04bd02de73f0d0f6c (patch)
tree8ec9652ccc055f53da78f149a1066085f0ddf6e2
parent8c11609f441450414a3c3e240f91834a8a043efa (diff)
Sync QWindow visible state during QWidget destruction
A call to QWidget::destroy() will end up in QWindow::destroy(), which calls QWindow::setVisible(false). A call to QWindow::setVisible(false) on a widget's window will under normal circumstances end up in QWidgetPrivate::setVisible(), which in turn recurses back into QWindowPrivate::setVisible(), via QWidgetPrivate::hide_helper(), ensuring that the QWindow internal state is updated, visibleChanged emitted, and show/hide events sent. Durin QWidget::destroy() we end up in QWindow::destroy(), which calls QWindow::setVisible(false), but in this case the widget no longer has Qt::WA_WState_Created, so the hide_helper() call is skipped, and the corresponding QWindow is not kept in the loop. To work around this we could have checked for windowHandle() instead of Qt::WA_WState_Created before calling hide_helper(), but that had many other side effects, so we opt for a more targeted fix. Pick-to: 6.5 Change-Id: I68f80e5f7df9ee811afcd274a7ee4de31a110da5 Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io> (cherry picked from commit 5b09d9e6d694045a6cef15f8984bab63cf86402c) Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> (cherry picked from commit 3bc3cf3ba95819c85fc94cb4bcd65ef6d6748efa)
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp11
-rw-r--r--tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp28
2 files changed, 39 insertions, 0 deletions
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index d86af94a9c..99c54b9d98 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -51,6 +51,17 @@ public:
widget->setAttribute(Qt::WA_WState_ExplicitShowHide, wasExplicitShowHide);
widget->setAttribute(Qt::WA_WState_Hidden, wasHidden);
}
+
+ // The call to QWidgetPrivate::setVisible() above will normally
+ // recurse back into QWidgetWindow::setNativeWindowVisibility()
+ // to update the QWindow state, but during QWidget::destroy()
+ // this is not the case, as Qt::WA_WState_Created has been
+ // unset by the time we check if we should call hide_helper().
+ // We don't want to change the QWidget logic, as that has
+ // other side effects, so as a targeted fix we sync up the
+ // visibility here if needed.
+ if (q->isVisible() != visible)
+ QWindowPrivate::setVisible(visible);
} else {
QWindowPrivate::setVisible(visible);
}
diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
index 549206875c..5d19aa3c75 100644
--- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
+++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
@@ -433,6 +433,8 @@ private slots:
void showFullscreenAndroid();
#endif
+ void setVisibleDuringDestruction();
+
private:
const QString m_platform;
QSize m_testWidgetSize;
@@ -13403,5 +13405,31 @@ void tst_QWidget::showFullscreenAndroid()
}
#endif // Q_OS_ANDROID
+void tst_QWidget::setVisibleDuringDestruction()
+{
+ CreateDestroyWidget widget;
+ widget.create();
+ QVERIFY(widget.windowHandle());
+
+ QSignalSpy signalSpy(widget.windowHandle(), &QWindow::visibleChanged);
+ EventSpy<QWindow> showEventSpy(widget.windowHandle(), QEvent::Show);
+ widget.show();
+ QTRY_COMPARE(showEventSpy.count(), 1);
+ QTRY_COMPARE(signalSpy.count(), 1);
+
+ EventSpy<QWindow> hideEventSpy(widget.windowHandle(), QEvent::Hide);
+ widget.hide();
+ QTRY_COMPARE(hideEventSpy.count(), 1);
+ QTRY_COMPARE(signalSpy.count(), 2);
+
+ widget.show();
+ QTRY_COMPARE(showEventSpy.count(), 2);
+ QTRY_COMPARE(signalSpy.count(), 3);
+
+ widget.destroy();
+ QTRY_COMPARE(hideEventSpy.count(), 2);
+ QTRY_COMPARE(signalSpy.count(), 4);
+}
+
QTEST_MAIN(tst_QWidget)
#include "tst_qwidget.moc"