summaryrefslogtreecommitdiffstats
path: root/src/widgets/dialogs/qmessagebox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/dialogs/qmessagebox.cpp')
-rw-r--r--src/widgets/dialogs/qmessagebox.cpp419
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&nbsp;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