Ensure QWidget::destroy() events and attributes match hiding the widget
When destroying a widget via QWidget::destroy(), we clear WA_WState_Created, and then delete the QWidgetWindow, which in turn hides the widget. But QWidgetPrivate::setVisible(false) skips hide_helper() if the widget has not been created, which leaves out important events such as the hide event, and even more important, fails to clear WA_WState_Visible. As a result, the widget is left visible (and mapped), even if it has been destroyed. This is normally not a big issue for the main use of destroy(), namely destructing the widget, but for cases where destroy() and create() is used to recreate the widget this is important. We now unconditionally call hide_helper() if the widget is not already hidden. As a result, the widget will correctly be visible=false after a destroy(). This in turn means we need to re-apply the visible state after recreating the widget when we detect a mismatch in RHI configuration. Due to our meddling of the Hidden and ExplicitShowHide attributes in QWidgetWindow private, a QWidet::show() will not have any effect after being destroy(). This is okey for now, as destroy() is internal to a widget, and we make sure to either update WA_WState_Visible and WA_WState_Hidden (in QWidgetPrivate::setParent_sys), or use the QWidgetPrivate::setVisible() code path directly, which doesn't have that issue. The root problem will be fixed in a follow up. Change-Id: I77cb88d75e57f0d9a31741161fb14d618a653291 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io> (cherry picked from commit 371d7ea19a8075a1ad2c2011433b40e8eb1f6816) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
fe434acac6
commit
0531e444a7
@ -8430,8 +8430,7 @@ void QWidgetPrivate::setVisible(bool visible)
|
||||
|
||||
if (!q->testAttribute(Qt::WA_WState_Hidden)) {
|
||||
q->setAttribute(Qt::WA_WState_Hidden);
|
||||
if (q->testAttribute(Qt::WA_WState_Created))
|
||||
hide_helper();
|
||||
hide_helper();
|
||||
}
|
||||
|
||||
// invalidate layout similar to updateGeometry()
|
||||
@ -10899,10 +10898,12 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
|
||||
}
|
||||
if (recreate) {
|
||||
const auto windowStateBeforeDestroy = newtlw->windowState();
|
||||
const auto visibilityBeforeDestroy = newtlw->isVisible();
|
||||
newtlw->destroy();
|
||||
newtlw->create();
|
||||
Q_ASSERT(newtlw->windowHandle());
|
||||
newtlw->windowHandle()->setWindowStates(windowStateBeforeDestroy);
|
||||
QWidgetPrivate::get(newtlw)->setVisible(visibilityBeforeDestroy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +122,34 @@ static QByteArray msgComparisonFailed(T v1, const char *op, T v2)
|
||||
return s.toLocal8Bit();
|
||||
}
|
||||
|
||||
template<class T> class EventSpy : public QObject
|
||||
{
|
||||
public:
|
||||
EventSpy(T *widget, QEvent::Type event)
|
||||
: m_widget(widget), eventToSpy(event)
|
||||
{
|
||||
if (m_widget)
|
||||
m_widget->installEventFilter(this);
|
||||
}
|
||||
|
||||
T *widget() const { return m_widget; }
|
||||
int count() const { return m_count; }
|
||||
void clear() { m_count = 0; }
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override
|
||||
{
|
||||
if (event->type() == eventToSpy)
|
||||
++m_count;
|
||||
return QObject::eventFilter(object, event);
|
||||
}
|
||||
|
||||
private:
|
||||
T *m_widget;
|
||||
const QEvent::Type eventToSpy;
|
||||
int m_count = 0;
|
||||
};
|
||||
|
||||
Q_LOGGING_CATEGORY(lcTests, "qt.widgets.tests")
|
||||
|
||||
class tst_QWidget : public QObject
|
||||
@ -227,6 +255,7 @@ private slots:
|
||||
|
||||
void ensureCreated();
|
||||
void createAndDestroy();
|
||||
void eventsAndAttributesOnDestroy();
|
||||
void winIdChangeEvent();
|
||||
void persistentWinId();
|
||||
void showNativeChild();
|
||||
@ -5262,6 +5291,84 @@ void tst_QWidget::createAndDestroy()
|
||||
QVERIFY(widget.internalWinId());
|
||||
}
|
||||
|
||||
void tst_QWidget::eventsAndAttributesOnDestroy()
|
||||
{
|
||||
// The events and attributes when destroying a widget should
|
||||
// include those of hiding the widget.
|
||||
|
||||
CreateDestroyWidget widget;
|
||||
EventSpy<QWidget> showEventSpy(&widget, QEvent::Show);
|
||||
EventSpy<QWidget> hideEventSpy(&widget, QEvent::Hide);
|
||||
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), false);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false);
|
||||
|
||||
widget.show();
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true);
|
||||
QTRY_COMPARE(widget.testAttribute(Qt::WA_Mapped), true);
|
||||
QCOMPARE(showEventSpy.count(), 1);
|
||||
QCOMPARE(hideEventSpy.count(), 0);
|
||||
|
||||
widget.hide();
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false);
|
||||
QCOMPARE(showEventSpy.count(), 1);
|
||||
QCOMPARE(hideEventSpy.count(), 1);
|
||||
|
||||
widget.show();
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true);
|
||||
QTRY_COMPARE(widget.testAttribute(Qt::WA_Mapped), true);
|
||||
QCOMPARE(showEventSpy.count(), 2);
|
||||
QCOMPARE(hideEventSpy.count(), 1);
|
||||
|
||||
widget.destroy();
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), false);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false);
|
||||
QCOMPARE(showEventSpy.count(), 2);
|
||||
QCOMPARE(hideEventSpy.count(), 2);
|
||||
|
||||
const int hideEventsAfterDestroy = hideEventSpy.count();
|
||||
|
||||
widget.create();
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false);
|
||||
QCOMPARE(showEventSpy.count(), 2);
|
||||
QCOMPARE(hideEventSpy.count(), hideEventsAfterDestroy);
|
||||
|
||||
QWidgetPrivate::get(&widget)->setVisible(true);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
|
||||
QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true);
|
||||
QTRY_COMPARE(widget.testAttribute(Qt::WA_Mapped), true);
|
||||
QCOMPARE(showEventSpy.count(), 3);
|
||||
QCOMPARE(hideEventSpy.count(), hideEventsAfterDestroy);
|
||||
|
||||
// Make sure the destroy that happens when a top level
|
||||
// is moved to being a child does not prevent the child
|
||||
// being shown again.
|
||||
|
||||
QWidget parent;
|
||||
QWidget child;
|
||||
parent.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&parent));
|
||||
child.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&child));
|
||||
|
||||
child.setParent(&parent);
|
||||
QCOMPARE(child.testAttribute(Qt::WA_WState_Created), false);
|
||||
QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false);
|
||||
|
||||
child.show();
|
||||
QCOMPARE(child.testAttribute(Qt::WA_WState_Created), true);
|
||||
QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), true);
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&child));
|
||||
}
|
||||
|
||||
void tst_QWidget::winIdChangeEvent()
|
||||
{
|
||||
{
|
||||
@ -7060,34 +7167,6 @@ void tst_QWidget::setFocus()
|
||||
}
|
||||
}
|
||||
|
||||
template<class T> class EventSpy : public QObject
|
||||
{
|
||||
public:
|
||||
EventSpy(T *widget, QEvent::Type event)
|
||||
: m_widget(widget), eventToSpy(event)
|
||||
{
|
||||
if (m_widget)
|
||||
m_widget->installEventFilter(this);
|
||||
}
|
||||
|
||||
T *widget() const { return m_widget; }
|
||||
int count() const { return m_count; }
|
||||
void clear() { m_count = 0; }
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override
|
||||
{
|
||||
if (event->type() == eventToSpy)
|
||||
++m_count;
|
||||
return QObject::eventFilter(object, event);
|
||||
}
|
||||
|
||||
private:
|
||||
T *m_widget;
|
||||
const QEvent::Type eventToSpy;
|
||||
int m_count = 0;
|
||||
};
|
||||
|
||||
#ifndef QT_NO_CURSOR
|
||||
void tst_QWidget::setCursor()
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user