diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2023-12-19 22:07:56 +0100 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2024-01-18 10:46:05 +0000 |
commit | 0251fe61b25b2f475ce46ac04bd02de73f0d0f6c (patch) | |
tree | 8ec9652ccc055f53da78f149a1066085f0ddf6e2 | |
parent | 8c11609f441450414a3c3e240f91834a8a043efa (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.cpp | 11 | ||||
-rw-r--r-- | tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp | 28 |
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" |