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)
This commit is contained in:
Tor Arne Vestbø 2023-12-19 22:07:56 +01:00
parent 8c11609f44
commit 0251fe61b2
2 changed files with 39 additions and 0 deletions

View File

@ -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);
}

View File

@ -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"