diff options
Diffstat (limited to 'src/widgets/dialogs/qmessagebox.cpp')
-rw-r--r-- | src/widgets/dialogs/qmessagebox.cpp | 419 |
1 files changed, 312 insertions, 107 deletions
diff --git a/src/widgets/dialogs/qmessagebox.cpp b/src/widgets/dialogs/qmessagebox.cpp index cb985ff145..bf56b17f55 100644 --- a/src/widgets/dialogs/qmessagebox.cpp +++ b/src/widgets/dialogs/qmessagebox.cpp @@ -30,11 +30,18 @@ #include "private/qabstractbutton_p.h" #include <QtGui/qpa/qplatformtheme.h> +#include <QtCore/qanystringview.h> +#include <QtCore/qdebug.h> +#include <QtCore/qpointer.h> +#include <QtCore/qversionnumber.h> + #ifdef Q_OS_WIN # include <QtCore/qt_windows.h> #include <qpa/qplatformnativeinterface.h> #endif +#include <optional> + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -49,6 +56,20 @@ HMENU qt_getWindowsSystemMenu(const QWidget *w) } #endif +static_assert(qToUnderlying(QMessageBox::ButtonRole::NRoles) == + qToUnderlying(QDialogButtonBox::ButtonRole::NRoles), + "QMessageBox::ButtonRole and QDialogButtonBox::ButtonRole out of sync!"); + +static_assert(std::is_same_v<std::underlying_type_t<QMessageBox::ButtonRole>, + std::underlying_type_t<QDialogButtonBox::ButtonRole>>); + +// StandardButton enums have different underlying types +// => qToUnderlying and std::is_same_v won't work +// ### Qt 7: make them have the same underlying type +static_assert(static_cast<int>(QMessageBox::StandardButton::LastButton) == + static_cast<int>(QDialogButtonBox::StandardButton::LastButton), + "QMessageBox::StandardButton and QDialogButtonBox::StandardButton out of sync!"); + enum Button { Old_Ok = 1, Old_Cancel = 2, Old_Yes = 3, Old_No = 4, Old_Abort = 5, Old_Retry = 6, Old_Ignore = 7, Old_YesAll = 8, Old_NoAll = 9, Old_ButtonMask = 0xFF, NewButtonMask = 0xFFFFFC00 }; @@ -91,8 +112,8 @@ public: layout->addWidget(textEdit); setLayout(layout); - connect(textEdit, SIGNAL(copyAvailable(bool)), - this, SLOT(textCopyAvailable(bool))); + connect(textEdit, &TextEdit::copyAvailable, + this, &QMessageBoxDetailsText::textCopyAvailable); } void setText(const QString &text) { textEdit->setPlainText(text); } QString text() const { return textEdit->toPlainText(); } @@ -171,8 +192,8 @@ public: void init(const QString &title = QString(), const QString &text = QString()); void setupLayout(); - void _q_buttonClicked(QAbstractButton *); - void _q_clicked(QPlatformDialogHelper::StandardButton button, QPlatformDialogHelper::ButtonRole role); + void buttonClicked(QAbstractButton *); + void helperClicked(QPlatformDialogHelper::StandardButton button, QPlatformDialogHelper::ButtonRole role); void setClickedButton(QAbstractButton *button); QAbstractButton *findButton(int button0, int button1, int button2, int flags); @@ -181,14 +202,13 @@ public: QAbstractButton *abstractButtonForId(int id) const; int execReturnCode(QAbstractButton *button); - int dialogCodeForButton(QAbstractButton *button) const; - void detectEscapeButton(); void updateSize(); int layoutMinimumWidth(); void retranslateStrings(); void setVisible(bool visible) override; + bool canBeNativeDialog() const override; static int showOldMessageBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &text, @@ -232,7 +252,7 @@ public: private: void initHelper(QPlatformDialogHelper *) override; void helperPrepareShow(QPlatformDialogHelper *) override; - void helperDone(QDialog::DialogCode, QPlatformDialogHelper *) override; + int dialogCode() const override; }; void QMessageBoxPrivate::init(const QString &title, const QString &text) @@ -251,8 +271,8 @@ void QMessageBoxPrivate::init(const QString &title, const QString &text) buttonBox = new QDialogButtonBox; buttonBox->setObjectName("qt_msgbox_buttonbox"_L1); buttonBox->setCenterButtons(q->style()->styleHint(QStyle::SH_MessageBox_CenterButtons, nullptr, q)); - QObject::connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), - q, SLOT(_q_buttonClicked(QAbstractButton*))); + QObjectPrivate::connect(buttonBox, &QDialogButtonBox::clicked, + this, &QMessageBoxPrivate::buttonClicked); setupLayout(); if (!title.isEmpty() || !text.isEmpty()) { q->setWindowTitle(title); @@ -427,37 +447,50 @@ static int oldButton(int button) int QMessageBoxPrivate::execReturnCode(QAbstractButton *button) { - int ret = buttonBox->standardButton(button); - if (ret == QMessageBox::NoButton) { - ret = customButtonList.indexOf(button); // if button == 0, correctly sets ret = -1 - } else if (compatMode) { - ret = oldButton(ret); + if (int standardButton = buttonBox->standardButton(button)) { + // When using a QMessageBox with standard buttons, the return + // code is a StandardButton value indicating the standard button + // that was clicked. + if (compatMode) + return oldButton(standardButton); + else + return standardButton; + } else { + // When using QMessageBox with custom buttons, the return code + // is an opaque value, and the user is expected to use clickedButton() + // to determine which button was clicked. We make sure to keep the opaque + // value out of the QDialog::DialogCode range, so we can distinguish them. + auto customButtonIndex = customButtonList.indexOf(button); + if (customButtonIndex >= 0) + return QDialog::DialogCode::Accepted + customButtonIndex + 1; + else + return customButtonIndex; // Not found, return -1 } - return ret; } -/*! - \internal - - Returns 0 for RejectedRole and NoRole, 1 for AcceptedRole and YesRole, -1 otherwise - */ -int QMessageBoxPrivate::dialogCodeForButton(QAbstractButton *button) const +int QMessageBoxPrivate::dialogCode() const { Q_Q(const QMessageBox); - switch (q->buttonRole(button)) { - case QMessageBox::AcceptRole: - case QMessageBox::YesRole: - return QDialog::Accepted; - case QMessageBox::RejectRole: - case QMessageBox::NoRole: - return QDialog::Rejected; - default: - return -1; + if (rescode <= QDialog::Accepted) { + return rescode; + } else if (clickedButton) { + switch (q->buttonRole(clickedButton)) { + case QMessageBox::AcceptRole: + case QMessageBox::YesRole: + return QDialog::Accepted; + case QMessageBox::RejectRole: + case QMessageBox::NoRole: + return QDialog::Rejected; + default: + ; + } } + + return QDialogPrivate::dialogCode(); } -void QMessageBoxPrivate::_q_buttonClicked(QAbstractButton *button) +void QMessageBoxPrivate::buttonClicked(QAbstractButton *button) { Q_Q(QMessageBox); #if QT_CONFIG(textedit) @@ -488,23 +521,26 @@ void QMessageBoxPrivate::setClickedButton(QAbstractButton *button) emit q->buttonClicked(clickedButton); auto resultCode = execReturnCode(button); - close(resultCode); - finalize(resultCode, dialogCodeForButton(button)); + q->done(resultCode); } -void QMessageBoxPrivate::_q_clicked(QPlatformDialogHelper::StandardButton button, QPlatformDialogHelper::ButtonRole role) +void QMessageBoxPrivate::helperClicked(QPlatformDialogHelper::StandardButton helperButton, QPlatformDialogHelper::ButtonRole role) { + Q_UNUSED(role); Q_Q(QMessageBox); - if (button > QPlatformDialogHelper::LastButton) { - // It's a custom button, and the QPushButton in options is just a proxy - // for the button on the platform dialog. Simulate the user clicking it. - clickedButton = static_cast<QAbstractButton *>(options->customButton(button)->button); - Q_ASSERT(clickedButton); - clickedButton->click(); - q->done(role); - } else { - q->done(button); - } + + // Map back to QAbstractButton, so that the message box behaves the same from + // the outside, regardless of whether it's backed by a native helper or not. + QAbstractButton *dialogButton = helperButton > QPlatformDialogHelper::LastButton ? + static_cast<QAbstractButton *>(options->customButton(helperButton)->button) : + q->button(QMessageBox::StandardButton(helperButton)); + + Q_ASSERT(dialogButton); + + // Simulate click by explicitly clicking the button. This will ensure that + // any logic of the button that responds to the click is respected, including + // plumbing back to buttonClicked above based on the clicked() signal. + dialogButton->click(); } /*! @@ -714,7 +750,7 @@ void QMessageBoxPrivate::_q_clicked(QPlatformDialogHelper::StandardButton button When an escape button can't be determined using these rules, pressing \uicontrol Esc has no effect. - \sa QDialogButtonBox, {Standard Dialogs Example}, {Qt Widgets - Application Example} + \sa QDialogButtonBox, {Standard Dialogs Example} */ /*! @@ -770,6 +806,12 @@ void QMessageBoxPrivate::_q_clicked(QPlatformDialogHelper::StandardButton button */ /*! + \enum QMessageBox::Option + \since 6.6 + \value DontUseNativeDialog Don't use the native message dialog. +*/ + +/*! \fn void QMessageBox::buttonClicked(QAbstractButton *button) This signal is emitted whenever a button is clicked inside the QMessageBox. @@ -864,12 +906,6 @@ void QMessageBox::addButton(QAbstractButton *button, ButtonRole role) } } - // Add button to native dialog helper, unless it's the details button, - // since we don't do any plumbing for the button's action in that case. - if (button != d->detailsButton) { - d->options->addButton(button->text(), - static_cast<QPlatformDialogHelper::ButtonRole>(role), button); - } d->buttonBox->addButton(button, (QDialogButtonBox::ButtonRole)role); d->customButtonList.append(button); d->autoAddOkButton = false; @@ -994,6 +1030,12 @@ QMessageBox::StandardButton QMessageBox::standardButton(QAbstractButton *button) Returns a pointer corresponding to the standard button \a which, or \nullptr if the standard button doesn't exist in this message box. + \note Modifying the properties of the returned button may not be reflected + in native implementations of the message dialog. To customize dialog + buttons add a \l{addButton(QAbstractButton *button, QMessageBox::ButtonRole role)} + {custom button} or \l{addButton(const QString &text, QMessageBox::ButtonRole role)} + {button title} instead, or set the \l Option::DontUseNativeDialog option. + \sa standardButtons, standardButton() */ QAbstractButton *QMessageBox::button(StandardButton which) const @@ -1225,6 +1267,78 @@ QCheckBox* QMessageBox::checkBox() const } /*! + \since 6.6 + Sets the given \a option to be enabled if \a on is true; otherwise, + clears the given \a option. + + Options (particularly the \l Option::DontUseNativeDialog option) should be set + before showing the dialog. + + Setting options while the dialog is visible is not guaranteed to have + an immediate effect on the dialog. + + Setting options after changing other properties may cause these + values to have no effect. + + \sa options, testOption() +*/ +void QMessageBox::setOption(QMessageBox::Option option, bool on) +{ + const QMessageBox::Options previousOptions = options(); + if (!(previousOptions & option) != !on) + setOptions(previousOptions ^ option); +} + +/*! + \since 6.6 + + Returns \c true if the given \a option is enabled; otherwise, returns + false. + + \sa options, setOption() +*/ +bool QMessageBox::testOption(QMessageBox::Option option) const +{ + Q_D(const QMessageBox); + return d->options->testOption(static_cast<QMessageDialogOptions::Option>(option)); +} + +void QMessageBox::setOptions(QMessageBox::Options options) +{ + Q_D(QMessageBox); + + if (QMessageBox::options() == options) + return; + + d->options->setOptions(QMessageDialogOptions::Option(int(options))); +} + +QMessageBox::Options QMessageBox::options() const +{ + Q_D(const QMessageBox); + return QMessageBox::Options(int(d->options->options())); +} + +/*! + \property QMessageBox::options + \brief Options that affect the look and feel of the dialog. + \since 6.6 + + By default, these options are disabled. + + The option \l Option::DontUseNativeDialog should be set + before changing dialog properties or showing the dialog. + + Setting options while the dialog is visible is not guaranteed to have + an immediate effect on the dialog. + + Setting options after changing other properties may cause these + values to have no effect. + + \sa setOption(), testOption() +*/ + +/*! \property QMessageBox::text \brief the message box text to be displayed. @@ -1363,6 +1477,8 @@ void QMessageBox::setTextFormat(Qt::TextFormat format) d->label->setTextFormat(format); d->label->setWordWrap(format == Qt::RichText || (format == Qt::AutoText && Qt::mightBeRichText(d->label->text()))); + if (d->informativeLabel) + d->informativeLabel->setTextFormat(format); d->updateSize(); } @@ -1568,8 +1684,11 @@ void QMessageBox::open(QObject *receiver, const char *member) void QMessageBoxPrivate::setVisible(bool visible) { Q_Q(QMessageBox); - if (q->testAttribute(Qt::WA_WState_ExplicitShowHide) && q->testAttribute(Qt::WA_WState_Hidden) != visible) - return; + + // Last minute setup + if (autoAddOkButton) + q->addButton(QMessageBox::Ok); + detectEscapeButton(); if (canBeNativeDialog()) setNativeDialogVisible(visible); @@ -1615,13 +1734,7 @@ QMessageBox::ButtonRole QMessageBox::buttonRole(QAbstractButton *button) const void QMessageBox::showEvent(QShowEvent *e) { Q_D(QMessageBox); - if (d->autoAddOkButton) { - addButton(Ok); - } - if (d->detailsButton) - addButton(d->detailsButton, QMessageBox::ActionRole); d->clickedButton = nullptr; - d->detectEscapeButton(); d->updateSize(); #if QT_CONFIG(accessibility) @@ -1646,11 +1759,14 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, { // necessary for source compatibility with Qt 4.0 and 4.1 // handles (Yes, No) and (Yes|Default, No) - if (defaultButton && !(buttons & defaultButton)) - return (QMessageBox::StandardButton) - QMessageBoxPrivate::showOldMessageBox(parent, icon, title, - text, int(buttons), - int(defaultButton), 0); + if (defaultButton && !(buttons & defaultButton)) { + const int defaultButtons = defaultButton | QMessageBox::Default; + const int otherButtons = buttons.toInt(); + const int ret = QMessageBoxPrivate::showOldMessageBox(parent, icon, title, + text, otherButtons, + defaultButtons, 0); + return static_cast<QMessageBox::StandardButton>(ret); + } QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent); QDialogButtonBox *buttonBox = msgBox.findChild<QDialogButtonBox*>(); @@ -1819,9 +1935,10 @@ QMessageBox::StandardButton QMessageBox::critical(QWidget *parent, const QString \li As a last resort it uses the Information icon. \endlist - The about box has a single button labelled "OK". On \macos, the - about box is popped up as a modeless window; on other platforms, - it is currently application modal. + The about box has a single button labelled "OK". + + On \macos, the about box is popped up as a modeless window; on + other platforms, it is currently application modal. \sa QWidget::windowIcon(), QApplication::activeWindow() */ @@ -1851,13 +1968,13 @@ void QMessageBox::about(QWidget *parent, const QString &title, const QString &te // should perhaps be a style hint #ifdef Q_OS_MAC oldMsgBox = msgBox; -#if 0 - // ### doesn't work until close button is enabled in title bar - msgBox->d_func()->autoAddOkButton = false; + auto *d = msgBox->d_func(); + d->buttonBox->setCenterButtons(true); +#ifdef Q_OS_IOS + msgBox->setModal(true); #else - msgBox->d_func()->buttonBox->setCenterButtons(true); -#endif msgBox->setModal(false); +#endif msgBox->show(); #else msgBox->exec(); @@ -1874,7 +1991,7 @@ void QMessageBox::about(QWidget *parent, const QString &title, const QString &te QApplication provides this functionality as a slot. - On \macos, the about box is popped up as a modeless window; on + On \macos, the aboutQt box is popped up as a modeless window; on other platforms, it is currently application modal. \sa QApplication::aboutQt() @@ -1914,15 +2031,14 @@ void QMessageBox::aboutQt(QWidget *parent, const QString &title) "<p>Qt licensed under GNU (L)GPL is appropriate for the " "development of Qt applications provided you can comply with the terms " "and conditions of the respective licenses.</p>" - "<p>Please see <a href=\"http://%2/\">%2</a> " + "<p>Please see <a href=\"https://%2/\">%2</a> " "for an overview of Qt licensing.</p>" - "<p>Copyright (C) %1 The Qt Company Ltd and other " + "<p>Copyright (C) The Qt Company Ltd. and other " "contributors.</p>" "<p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p>" - "<p>Qt is The Qt Company Ltd product developed as an open source " - "project. See <a href=\"http://%3/\">%3</a> for more information.</p>" - ).arg(QLatin1String(QT_COPYRIGHT_YEAR), - QStringLiteral("qt.io/licensing"), + "<p>Qt is The Qt Company Ltd. product developed as an open source " + "project. See <a href=\"https://%3/\">%3</a> for more information.</p>" + ).arg(QStringLiteral("qt.io/licensing"), QStringLiteral("qt.io")); QMessageBox *msgBox = new QMessageBox(parent); msgBox->setAttribute(Qt::WA_DeleteOnClose); @@ -1937,13 +2053,13 @@ void QMessageBox::aboutQt(QWidget *parent, const QString &title) // should perhaps be a style hint #ifdef Q_OS_MAC oldMsgBox = msgBox; -#if 0 - // ### doesn't work until close button is enabled in title bar - msgBox->d_func()->autoAddOkButton = false; + auto *d = msgBox->d_func(); + d->buttonBox->setCenterButtons(true); +#ifdef Q_OS_IOS + msgBox->setModal(true); #else - msgBox->d_func()->buttonBox->setCenterButtons(true); -#endif msgBox->setModal(false); +#endif msgBox->show(); #else msgBox->exec(); @@ -2165,7 +2281,7 @@ int QMessageBox::information(QWidget *parent, const QString &title, const QStrin } /*! - \deprecated since 6.2. Use the overload taking StandardButtons instead. + \deprecated [6.2] Use the overload taking StandardButtons instead. \overload Displays an information message box with the given \a title and @@ -2464,7 +2580,7 @@ int QMessageBox::critical(QWidget *parent, const QString &title, const QString& /*! - \deprecated + \deprecated [6.2] Returns the text of the message box button \a button, or an empty string if the message box does not contain the button. @@ -2485,7 +2601,7 @@ QString QMessageBox::buttonText(int button) const } /*! - \deprecated + \deprecated [6.2] Sets the text of the message box button \a button to \a text. Setting the text of a button that is not in the message box is @@ -2568,9 +2684,14 @@ void QMessageBox::setDetailedText(const QString &text) information to the user, for example describing the consequences of the situation, or suggestion alternative solutions. + The text will be interpreted either as a plain text or as rich text, + depending on the text format setting (\l QMessageBox::textFormat). + The default setting is Qt::AutoText, i.e., the message box will try + to auto-detect the format of the text. + By default, this property contains an empty string. - \sa QMessageBox::text, QMessageBox::detailedText + \sa textFormat, QMessageBox::text, QMessageBox::detailedText */ QString QMessageBox::informativeText() const { @@ -2594,12 +2715,12 @@ void QMessageBox::setInformativeText(const QString &text) label->setTextInteractionFlags(Qt::TextInteractionFlags(style()->styleHint(QStyle::SH_MessageBox_TextInteractionFlags, nullptr, this))); label->setAlignment(Qt::AlignTop | Qt::AlignLeft); label->setOpenExternalLinks(true); - label->setWordWrap(true); #ifdef Q_OS_MAC // apply a smaller font the information label on the mac label->setFont(qt_app_fonts_hash()->value("QTipLabel")); #endif label->setWordWrap(true); + label->setTextFormat(d->label->textFormat()); d->informativeLabel = label; } d->informativeLabel->setText(text); @@ -2667,6 +2788,7 @@ QPixmap QMessageBoxPrivate::standardIcon(QMessageBox::Icon icon, QMessageBox *mb break; case QMessageBox::Question: tmpIcon = style->standardIcon(QStyle::SP_MessageBoxQuestion, nullptr, mb); + break; default: break; } @@ -2679,19 +2801,18 @@ QPixmap QMessageBoxPrivate::standardIcon(QMessageBox::Icon icon, QMessageBox *mb void QMessageBoxPrivate::initHelper(QPlatformDialogHelper *h) { - Q_Q(QMessageBox); - QObject::connect(h, SIGNAL(clicked(QPlatformDialogHelper::StandardButton,QPlatformDialogHelper::ButtonRole)), - q, SLOT(_q_clicked(QPlatformDialogHelper::StandardButton,QPlatformDialogHelper::ButtonRole))); - auto *messageDialogHelper = static_cast<QPlatformMessageDialogHelper *>(h); - QObject::connect(messageDialogHelper, &QPlatformMessageDialogHelper::checkBoxStateChanged, q, - [this](Qt::CheckState state) { + QObjectPrivate::connect(messageDialogHelper, &QPlatformMessageDialogHelper::clicked, + this, &QMessageBoxPrivate::helperClicked); + // Forward state via lambda, so that we can handle addition and removal + // of checkbox via setCheckBox() after initializing helper. + QObject::connect(messageDialogHelper, &QPlatformMessageDialogHelper::checkBoxStateChanged, + q_ptr, [this](Qt::CheckState state) { if (checkbox) checkbox->setCheckState(state); } ); - - static_cast<QPlatformMessageDialogHelper *>(h)->setOptions(options); + messageDialogHelper->setOptions(options); } static QMessageDialogOptions::StandardIcon helperIcon(QMessageBox::Icon i) @@ -2717,6 +2838,36 @@ static QPlatformDialogHelper::StandardButtons helperStandardButtons(QMessageBox return buttons; } +bool QMessageBoxPrivate::canBeNativeDialog() const +{ + // Don't use Q_Q here! This function is called from ~QDialog, + // so Q_Q calling q_func() invokes undefined behavior (invalid cast in q_func()). + const QDialog * const q = static_cast<const QMessageBox*>(q_ptr); + if (nativeDialogInUse) + return true; + if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs) + || q->testAttribute(Qt::WA_DontShowOnScreen) + || q->testAttribute(Qt::WA_StyleSheet) + || (options->options() & QMessageDialogOptions::Option::DontUseNativeDialog)) { + return false; + } + + if (strcmp(QMessageBox::staticMetaObject.className(), q->metaObject()->className()) != 0) + return false; + +#if QT_CONFIG(menu) + for (auto *customButton : buttonBox->buttons()) { + if (QPushButton *pushButton = qobject_cast<QPushButton *>(customButton)) { + // We can't support buttons with menus in native dialogs (yet) + if (pushButton->menu()) + return false; + } + } +#endif + + return QDialogPrivate::canBeNativeDialog(); +} + void QMessageBoxPrivate::helperPrepareShow(QPlatformDialogHelper *) { Q_Q(QMessageBox); @@ -2728,24 +2879,78 @@ void QMessageBoxPrivate::helperPrepareShow(QPlatformDialogHelper *) #endif options->setStandardIcon(helperIcon(q->icon())); options->setIconPixmap(q->iconPixmap()); - options->setStandardButtons(helperStandardButtons(q)); + + // Clear up front, since we might have prepared earlier + options->clearCustomButtons(); + + // Add standard buttons and resolve default/escape button + auto standardButtons = helperStandardButtons(q); + for (int button = QDialogButtonBox::StandardButton::FirstButton; + button <= QDialogButtonBox::StandardButton::LastButton; button <<= 1) { + auto *standardButton = buttonBox->button(QDialogButtonBox::StandardButton(button)); + if (!standardButton) + continue; + + if (auto *platformTheme = QGuiApplicationPrivate::platformTheme()) { + if (standardButton->text() != platformTheme->standardButtonText(button)) { + // The standard button has been customized, so add it as + // a custom button instead. + const auto buttonRole = buttonBox->buttonRole(standardButton); + options->addButton(standardButton->text(), + static_cast<QPlatformDialogHelper::ButtonRole>(buttonRole), + standardButton, button); + standardButtons &= ~QPlatformDialogHelper::StandardButton(button); + } + } + + if (standardButton == defaultButton) + options->setDefaultButton(button); + else if (standardButton == detectedEscapeButton) + options->setEscapeButton(button); + } + options->setStandardButtons(standardButtons); + + // Add custom buttons and resolve default/escape button + for (auto *customButton : customButtonList) { + // Unless it's the details button, since we don't do any + // plumbing for the button's action in that case. + if (customButton == detailsButton) + continue; + + const auto buttonRole = buttonBox->buttonRole(customButton); + const int buttonId = options->addButton(customButton->text(), + static_cast<QPlatformDialogHelper::ButtonRole>(buttonRole), + customButton); + + if (customButton == defaultButton) + options->setDefaultButton(buttonId); + else if (customButton == detectedEscapeButton) + options->setEscapeButton(buttonId); + } + if (checkbox) options->setCheckBox(checkbox->text(), checkbox->checkState()); } -void QMessageBoxPrivate::helperDone(QDialog::DialogCode code, QPlatformDialogHelper *) +void qRequireVersion(int argc, char *argv[], QAnyStringView req) { - Q_Q(QMessageBox); - QAbstractButton *button = q->button(QMessageBox::StandardButton(code)); - // If it was a custom button, a custom ID was used, so we won't get a valid pointer here. - // In that case, clickedButton has already been set in _q_buttonClicked. - if (button) - clickedButton = button; + const auto required = QVersionNumber::fromString(req).normalized(); + const auto current = QVersionNumber::fromString(qVersion()).normalized(); + if (current >= required) + return; + std::optional<QApplication> application; + if (!qApp) + application.emplace(argc, argv); + const QString message = QApplication::tr("Application \"%1\" requires Qt %2, found Qt %3.") + .arg(qAppName(), required.toString(), current.toString()); + QMessageBox::critical(nullptr, QApplication::tr("Incompatible Qt Library Error"), + message, QMessageBox::Abort); + qFatal("%ls", qUtf16Printable(message)); } #if QT_DEPRECATED_SINCE(6,2) /*! - \deprecated + \deprecated [6.2] Returns the pixmap used for a standard icon. This allows the pixmaps to be used in more complex message boxes. \a icon @@ -2811,7 +3016,7 @@ QPixmap QMessageBox::standardIcon(Icon icon) /*! \macro QT_REQUIRE_VERSION(int argc, char **argv, const char *version) - \relates <QMessageBox> + \relates QMessageBox This macro can be used to ensure that the application is run with a recent enough version of Qt. This is especially useful |