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 <qt_ci_bot@qt-project.org>
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
(cherry picked from commit 5ba0982b2879a1a4c13bf97467b8e5ad296e57a2)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Tor Arne Vestbø 2024-01-31 16:26:18 +01:00 committed by Qt Cherry-pick Bot
parent c8a543e3ec
commit 64f6664f5c
3 changed files with 30 additions and 27 deletions

View File

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

View File

@ -41,32 +41,36 @@ public:
Q_Q(QWidgetWindow);
qCDebug(lcWidgetShowHide) << "Setting visibility of" << q->widget()
<< "to" << visible << "via QWidgetWindowPrivate";
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);
}
}
}
// 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 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);
} else {
QWindowPrivate::setVisible(visible);
}
}
QWindow *eventReceiver() override {

View File

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