From 64f6664f5cafad92134b1a7412831704091aa949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 31 Jan 2024 16:26:18 +0100 Subject: [PATCH] Don't set ExplicitShowHide on children when showing parent widget As a result of using QWidget::setVisible to show the child widgets we would end up also setting ExplicitShowHide. This is not in line with the intent of ExplicitShowHide, which is to flag a widget as explicitly shown/hidden by the developer, which in turn prevents Qt Widgets from toggling WState_Hidden when the widget is reparented. By using QWidgetPrivate::setVisible instead, we can show the child without setting ExplicitShowHide. As side effect of this is that we no longer reset WA_WState_Hidden from QWidgetWindowPrivate::setVisible(). This is an issue when the setVisible call comes as a result of destroying the QWidgetWindow, as that is an implicit hide, and should not result in the widget having WA_WState_Hidden. QWidget handles this case in hideChildren by not calling QWidgetPrivate::setVisible -- instead doing its own reset of WA_WState_Visible. We don't want to untangle this just yet, so as a workaround we detect that the widget is already !isVisible(), thanks to hideChildren having hidden it, and then skip the call to QWidgetPrivate::setVisible that results from QWindow::destroy(). Task-number: QTBUG-121398 Change-Id: Ib5b4d9c84f0569124c5f3ca2169cabff18934e2d Reviewed-by: Qt CI Bot Reviewed-by: Axel Spoerl (cherry picked from commit 5ba0982b2879a1a4c13bf97467b8e5ad296e57a2) Reviewed-by: Qt Cherry-pick Bot --- src/widgets/kernel/qwidget.cpp | 2 +- src/widgets/kernel/qwidgetwindow.cpp | 52 ++++++++++--------- .../widgets/kernel/qwidget/tst_qwidget.cpp | 3 +- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index b8f38f0fc6d..2a4a9c0a5bb 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -8494,7 +8494,7 @@ void QWidgetPrivate::showChildren(bool spontaneous) if (widget->testAttribute(Qt::WA_WState_ExplicitShowHide)) widget->d_func()->show_recursive(); else - widget->show(); + widget->d_func()->setVisible(true); } } } diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 26f1afbca61..d80eb3346b1 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -41,32 +41,36 @@ public: Q_Q(QWidgetWindow); qCDebug(lcWidgetShowHide) << "Setting visibility of" << q->widget() << "to" << visible << "via QWidgetWindowPrivate"; - if (QWidget *widget = q->widget()) { - // Check if the widget was already hidden, as this indicates it was done - // explicitly and not because the parent window in this case made it hidden. - // In which case do not automatically show the widget when the parent - // window is shown. - const bool wasExplicitShowHide = widget->testAttribute(Qt::WA_WState_ExplicitShowHide); - const bool wasHidden = widget->testAttribute(Qt::WA_WState_Hidden); - QWidgetPrivate::get(widget)->setVisible(visible); - if (wasExplicitShowHide) { - 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); + if (QWidget *widget = q->widget()) { + // If the widget's visible state is already matching the new QWindow + // visible state we assume the widget has already synced up. + if (visible != widget->isVisible()) { + // Check if the widget was already hidden, as this indicates it was done + // explicitly and not because the parent window in this case made it hidden. + // In which case do not automatically show the widget when the parent + // window is shown. + const bool wasExplicitShowHide = widget->testAttribute(Qt::WA_WState_ExplicitShowHide); + const bool wasHidden = widget->testAttribute(Qt::WA_WState_Hidden); + + QWidgetPrivate::get(widget)->setVisible(visible); + + if (wasExplicitShowHide) { + widget->setAttribute(Qt::WA_WState_ExplicitShowHide, wasExplicitShowHide); + widget->setAttribute(Qt::WA_WState_Hidden, wasHidden); + } + } } + + // If we end up calling QWidgetPrivate::setVisible() above, we will + // in most cases recurse back into 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 handle this case, as + // well as the cases where we don't call QWidgetPrivate::setVisible(), + // by syncing up the QWindow state here if needed. + if (q->isVisible() != visible) + QWindowPrivate::setVisible(visible); } QWindow *eventReceiver() override { diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index dc4751704a6..4774e98a197 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -13523,8 +13523,7 @@ void tst_QWidget::explicitShowHide() child.setAttribute(Qt::WA_WState_ExplicitShowHide, false); QCOMPARE(child.close(), true); - QEXPECT_FAIL("", "Closing a non-native child is treated as an explicit hide", Continue); - QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), true); QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), true); child.show();