Close QDialog via QWidget::close()

By going via QWidget::close() we ensure that if there's a QWidgetWindow
backing the dialog (which is almost always the case), we will plumb down
to QWindow::close(), resulting in QEvent::Close events to the QWindow.

Since we don't want QDialog subclasses to receive a call to a closeEvent
override that they didn't receive before (and which they might interpret
as rejection or cancellation), install a temporary event filter that
eats the QCloseEvent resulting from the call to close().

Task-number: QTBUG-53286
Change-Id: Ie8f6f0cb3160acfd5865dc74f0a7b6d87f838724
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Tor Arne Vestbø 2021-09-03 15:07:21 +02:00
parent 8aa1fc6f12
commit 0246bfd40a
4 changed files with 31 additions and 10 deletions

View File

@ -146,16 +146,33 @@ bool QDialogPrivate::canBeNativeDialog() const
/*! /*!
\internal \internal
Properly hides dialog and sets the \a resultCode. Properly closes dialog and sets the \a resultCode.
*/ */
void QDialogPrivate::hide(int resultCode) void QDialogPrivate::close(int resultCode)
{ {
Q_Q(QDialog); Q_Q(QDialog);
q->setResult(resultCode); q->setResult(resultCode);
q->hide(); q->hide();
if (!data.is_closing) {
// Until Qt 6.3 we didn't close dialogs, so they didn't receive a QCloseEvent.
// It is likely that subclasses implement closeEvent and handle them as rejection
// (like QMessageBox and QProgressDialog do), so eat those events.
struct CloseEventEater : QObject
{
using QObject::QObject;
protected:
bool eventFilter(QObject *o, QEvent *e) override
{
if (e->type() == QEvent::Close)
return true;
return QObject::eventFilter(o, e);
}
} closeEventEater;
q->installEventFilter(&closeEventEater);
QWidgetPrivate::close();
}
handleClose(QWidgetPrivate::CloseNoEvent);
resetModalitySetByOpen(); resetModalitySetByOpen();
} }
@ -632,7 +649,7 @@ int QDialog::exec()
void QDialog::done(int r) void QDialog::done(int r)
{ {
Q_D(QDialog); Q_D(QDialog);
d->hide(r); d->close(r);
d->finalize(r, r); d->finalize(r, r);
} }

View File

@ -122,7 +122,7 @@ public:
QPlatformDialogHelper *platformHelper() const; QPlatformDialogHelper *platformHelper() const;
virtual bool canBeNativeDialog() const; virtual bool canBeNativeDialog() const;
void hide(int resultCode); void close(int resultCode);
void finalize(int resultCode, int dialogCode); void finalize(int resultCode, int dialogCode);
private: private:

View File

@ -518,7 +518,7 @@ void QMessageBoxPrivate::setClickedButton(QAbstractButton *button)
emit q->buttonClicked(clickedButton); emit q->buttonClicked(clickedButton);
auto resultCode = execReturnCode(button); auto resultCode = execReturnCode(button);
hide(resultCode); close(resultCode);
finalize(resultCode, dialogCodeForButton(button)); finalize(resultCode, dialogCodeForButton(button));
} }
@ -1420,8 +1420,10 @@ void QMessageBox::closeEvent(QCloseEvent *e)
return; return;
} }
QDialog::closeEvent(e); QDialog::closeEvent(e);
d->clickedButton = d->detectedEscapeButton; if (!d->clickedButton) {
setResult(d->execReturnCode(d->detectedEscapeButton)); d->clickedButton = d->detectedEscapeButton;
setResult(d->execReturnCode(d->detectedEscapeButton));
}
} }
/*! /*!

View File

@ -615,7 +615,9 @@ void tst_QDialog::virtualsOnClose()
dialog.show(); dialog.show();
QVERIFY(QTest::qWaitForWindowExposed(&dialog)); QVERIFY(QTest::qWaitForWindowExposed(&dialog));
dialog.accept(); dialog.accept();
QCOMPARE(dialog.closeEventCount, 0); // we only hide the dialog // we used to only hide the dialog, and we still don't want a
// closeEvent call for application-triggered calls to QDialog::done
QCOMPARE(dialog.closeEventCount, 0);
QCOMPARE(dialog.acceptCount, 1); QCOMPARE(dialog.acceptCount, 1);
QCOMPARE(dialog.rejectCount, 0); QCOMPARE(dialog.rejectCount, 0);
QCOMPARE(dialog.doneCount, 1); QCOMPARE(dialog.doneCount, 1);
@ -626,7 +628,7 @@ void tst_QDialog::virtualsOnClose()
dialog.show(); dialog.show();
QVERIFY(QTest::qWaitForWindowExposed(&dialog)); QVERIFY(QTest::qWaitForWindowExposed(&dialog));
dialog.reject(); dialog.reject();
QCOMPARE(dialog.closeEventCount, 0); // we only hide the dialog QCOMPARE(dialog.closeEventCount, 0);
QCOMPARE(dialog.acceptCount, 0); QCOMPARE(dialog.acceptCount, 0);
QCOMPARE(dialog.rejectCount, 1); QCOMPARE(dialog.rejectCount, 1);
QCOMPARE(dialog.doneCount, 1); QCOMPARE(dialog.doneCount, 1);