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.cpp669
1 files changed, 454 insertions, 215 deletions
diff --git a/src/widgets/dialogs/qmessagebox.cpp b/src/widgets/dialogs/qmessagebox.cpp
index e1bf141079..bf56b17f55 100644
--- a/src/widgets/dialogs/qmessagebox.cpp
+++ b/src/widgets/dialogs/qmessagebox.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWidgets module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtWidgets/qmessagebox.h>
@@ -64,14 +28,24 @@
#include <QtGui/qfontmetrics.h>
#include <QtGui/qclipboard.h>
#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;
+
#if defined(Q_OS_WIN)
HMENU qt_getWindowsSystemMenu(const QWidget *w)
{
@@ -82,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 };
@@ -124,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(); }
@@ -204,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);
@@ -214,13 +202,14 @@ 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,
int button0, int button1, int button2);
@@ -237,6 +226,7 @@ public:
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton);
static QPixmap standardIcon(QMessageBox::Icon icon, QMessageBox *mb);
+ static QMessageBox::StandardButton standardButtonForRole(QMessageBox::ButtonRole role);
QLabel *label;
QMessageBox::Icon icon;
@@ -262,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)
@@ -270,19 +260,19 @@ void QMessageBoxPrivate::init(const QString &title, const QString &text)
Q_Q(QMessageBox);
label = new QLabel;
- label->setObjectName(QLatin1String("qt_msgbox_label"));
+ label->setObjectName("qt_msgbox_label"_L1);
label->setTextInteractionFlags(Qt::TextInteractionFlags(q->style()->styleHint(QStyle::SH_MessageBox_TextInteractionFlags, nullptr, q)));
label->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
label->setOpenExternalLinks(true);
iconLabel = new QLabel(q);
- iconLabel->setObjectName(QLatin1String("qt_msgboxex_icon_label"));
+ iconLabel->setObjectName("qt_msgboxex_icon_label"_L1);
iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
buttonBox = new QDialogButtonBox;
- buttonBox->setObjectName(QLatin1String("qt_msgbox_buttonbox"));
+ 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);
@@ -302,7 +292,7 @@ void QMessageBoxPrivate::setupLayout()
Q_Q(QMessageBox);
delete q->layout();
QGridLayout *grid = new QGridLayout;
- bool hasIcon = !iconLabel->pixmap(Qt::ReturnByValue).isNull();
+ const bool hasIcon = !iconLabel->pixmap().isNull();
if (hasIcon)
grid->addWidget(iconLabel, 0, 0, 2, 1, Qt::AlignTop);
@@ -363,7 +353,7 @@ void QMessageBoxPrivate::updateSize()
if (!q->isVisible())
return;
- const QSize screenSize = QGuiApplication::screenAt(QCursor::pos())->availableGeometry().size();
+ const QSize screenSize = q->screen()->availableGeometry().size();
int hardLimit = qMin(screenSize.width() - 480, 1000); // can never get bigger than this
// on small screens allows the messagebox be the same size as the screen
if (screenSize.width() <= 1024)
@@ -457,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)
@@ -518,23 +521,26 @@ void QMessageBoxPrivate::setClickedButton(QAbstractButton *button)
emit q->buttonClicked(clickedButton);
auto resultCode = execReturnCode(button);
- hide(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();
}
/*!
@@ -548,10 +554,11 @@ void QMessageBoxPrivate::_q_clicked(QPlatformDialogHelper::StandardButton button
A message box displays a primary \l{QMessageBox::text}{text} to
alert the user to a situation, an \l{QMessageBox::informativeText}
- {informative text} to further explain the alert or to ask the user
- a question, and an optional \l{QMessageBox::detailedText}
- {detailed text} to provide even more data if the user requests
- it. A message box can also display an \l{QMessageBox::icon} {icon}
+ {informative text} to further explain the situation, and an optional
+ \l{QMessageBox::detailedText} {detailed text} to provide even more data
+ if the user requests it.
+
+ A message box can also display an \l{QMessageBox::icon} {icon}
and \l{QMessageBox::standardButtons} {standard buttons} for
accepting a user response.
@@ -577,27 +584,21 @@ void QMessageBoxPrivate::_q_clicked(QPlatformDialogHelper::StandardButton button
\image msgbox1.png
A better approach than just alerting the user to an event is to
- also ask the user what to do about it. Store the question in the
- \l{QMessageBox::informativeText} {informative text} property, and
- set the \l{QMessageBox::standardButtons} {standard buttons}
+ also ask the user what to do about it.
+
+ Set the \l{QMessageBox::standardButtons} {standard buttons}
property to the set of buttons you want as the set of user
responses. The buttons are specified by combining values from
StandardButtons using the bitwise OR operator. The display order
for the buttons is platform-dependent. For example, on Windows,
\uicontrol{Save} is displayed to the left of \uicontrol{Cancel}, whereas on
- Mac OS, the order is reversed.
-
- Mark one of your standard buttons to be your
+ \macos, the order is reversed. Mark one of your standard buttons to be your
\l{QMessageBox::defaultButton()} {default button}.
- \snippet code/src_gui_dialogs_qmessagebox.cpp 6
+ The \l{QMessageBox::informativeText} {informative text} property can
+ be used to add additional context to help the user choose the appropriate action.
- This is the approach recommended in the
- \l{http://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AppleHIGuidelines/Windows/Windows.html#//apple_ref/doc/uid/20000961-BABCAJID}
- {\macos Guidelines}. Similar guidelines apply for the other
- platforms, but note the different ways the
- \l{QMessageBox::informativeText} {informative text} is handled for
- different platforms.
+ \snippet code/src_gui_dialogs_qmessagebox.cpp 6
\image msgbox2.png
@@ -606,10 +607,10 @@ void QMessageBoxPrivate::_q_clicked(QPlatformDialogHelper::StandardButton button
\snippet code/src_gui_dialogs_qmessagebox.cpp 7
- To give the user more information to help him answer the question,
- set the \l{QMessageBox::detailedText} {detailed text} property. If
- the \l{QMessageBox::detailedText} {detailed text} property is set,
- the \uicontrol{Show Details...} button will be shown.
+ To give the user more information to help them choose the appropriate,
+ action, set the \l{QMessageBox::detailedText} {detailed text} property.
+ Depending on the platform the \l{QMessageBox::detailedText} {detailed text},
+ may require the user to click a \uicontrol{Show Details...} button to be shown.
\image msgbox3.png
@@ -749,8 +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, {fowler}{GUI Design Handbook: Message Box}, {Standard Dialogs Example},
- {Qt Widgets - Application Example}
+ \sa QDialogButtonBox, {Standard Dialogs Example}
*/
/*!
@@ -806,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.
@@ -813,14 +819,21 @@ void QMessageBoxPrivate::_q_clicked(QPlatformDialogHelper::StandardButton button
*/
/*!
- Constructs a message box with no text and no buttons. \a parent is
- passed to the QDialog constructor.
+ Constructs an \l{Qt::ApplicationModal} {application modal} message box with no text and no buttons.
+ \a parent is passed to the QDialog constructor.
+
+ The window modality can be overridden via setWindowModality() before calling show().
+
+ \note Using open() or exec() to show the message box affects the window modality.
+ Please see the detailed documentation for each function for more information.
On \macos, if you want your message box to appear
as a Qt::Sheet of its \a parent, set the message box's
\l{setWindowModality()} {window modality} to Qt::WindowModal or use open().
Otherwise, the message box will be a standard dialog.
+ \sa setWindowTitle(), setText(), setIcon(), setStandardButtons(), setWindowModality()
+
*/
QMessageBox::QMessageBox(QWidget *parent)
: QDialog(*new QMessageBoxPrivate, parent, Qt::MSWindowsFixedSizeDialogHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
@@ -830,20 +843,22 @@ QMessageBox::QMessageBox(QWidget *parent)
}
/*!
- Constructs a message box with the given \a icon, \a title, \a
- text, and standard \a buttons. Standard or custom buttons can be
+ Constructs an \l{Qt::ApplicationModal} {application modal} message box with the given \a icon,
+ \a title, \a text, and standard \a buttons. Standard or custom buttons can be
added at any time using addButton(). The \a parent and \a f
arguments are passed to the QDialog constructor.
- The message box is an \l{Qt::ApplicationModal} {application modal}
- dialog box.
+ The window modality can be overridden via setWindowModality() before calling show().
+
+ \note Using open() or exec() to show the message box affects the window modality.
+ Please see the detailed documentation for each function for more information.
On \macos, if \a parent is not \nullptr and you want your message box
to appear as a Qt::Sheet of that parent, set the message box's
\l{setWindowModality()} {window modality} to Qt::WindowModal
(default). Otherwise, the message box will be a standard dialog.
- \sa setWindowTitle(), setText(), setIcon(), setStandardButtons()
+ \sa setWindowTitle(), setText(), setIcon(), setStandardButtons(), setWindowModality()
*/
QMessageBox::QMessageBox(Icon icon, const QString &title, const QString &text,
StandardButtons buttons, QWidget *parent,
@@ -878,13 +893,39 @@ void QMessageBox::addButton(QAbstractButton *button, ButtonRole role)
if (!button)
return;
removeButton(button);
- d->options->addButton(button->text(), static_cast<QPlatformDialogHelper::ButtonRole>(role),
- button);
+
+ if (button->text().isEmpty()) {
+ if (auto *platformTheme = QGuiApplicationPrivate::platformTheme()) {
+ if (auto standardButton = QMessageBoxPrivate::standardButtonForRole(role))
+ button->setText(platformTheme->standardButtonText(standardButton));
+ }
+
+ if (button->text().isEmpty()) {
+ qWarning() << "Cannot add" << button << "without title";
+ return;
+ }
+ }
+
d->buttonBox->addButton(button, (QDialogButtonBox::ButtonRole)role);
d->customButtonList.append(button);
d->autoAddOkButton = false;
}
+QMessageBox::StandardButton QMessageBoxPrivate::standardButtonForRole(QMessageBox::ButtonRole role)
+{
+ switch (role) {
+ case QMessageBox::AcceptRole: return QMessageBox::Ok;
+ case QMessageBox::RejectRole: return QMessageBox::Cancel;
+ case QMessageBox::DestructiveRole: return QMessageBox::Discard;
+ case QMessageBox::HelpRole: return QMessageBox::Help;
+ case QMessageBox::ApplyRole: return QMessageBox::Apply;
+ case QMessageBox::YesRole: return QMessageBox::Yes;
+ case QMessageBox::NoRole: return QMessageBox::No;
+ case QMessageBox::ResetRole: return QMessageBox::Reset;
+ default: return QMessageBox::NoButton;
+ }
+}
+
/*!
\since 4.2
\overload
@@ -989,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
@@ -1066,14 +1113,14 @@ void QMessageBoxPrivate::detectEscapeButton()
// If there is only one button, make it the escape button
const QList<QAbstractButton *> buttons = buttonBox->buttons();
- if (buttons.count() == 1) {
+ if (buttons.size() == 1) {
detectedEscapeButton = buttons.first();
return;
}
// If there are two buttons and one of them is the "Show Details..."
// button, then make the other one the escape button
- if (buttons.count() == 2 && detailsButton) {
+ if (buttons.size() == 2 && detailsButton) {
auto idx = buttons.indexOf(detailsButton);
if (idx != -1) {
detectedEscapeButton = buttons.at(1 - idx);
@@ -1220,9 +1267,84 @@ 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.
+ The text should be a brief sentence or phrase that describes the situation,
+ ideally formulated as a neutral statement, or a call-to-action question.
+
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
@@ -1321,7 +1443,7 @@ void QMessageBox::setIcon(Icon icon)
QPixmap QMessageBox::iconPixmap() const
{
Q_D(const QMessageBox);
- return d->iconLabel->pixmap(Qt::ReturnByValue);
+ return d->iconLabel->pixmap();
}
void QMessageBox::setIconPixmap(const QPixmap &pixmap)
@@ -1355,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();
}
@@ -1420,8 +1544,10 @@ void QMessageBox::closeEvent(QCloseEvent *e)
return;
}
QDialog::closeEvent(e);
- d->clickedButton = d->detectedEscapeButton;
- setResult(d->execReturnCode(d->detectedEscapeButton));
+ if (!d->clickedButton) {
+ d->clickedButton = d->detectedEscapeButton;
+ setResult(d->execReturnCode(d->detectedEscapeButton));
+ }
}
/*!
@@ -1494,21 +1620,21 @@ void QMessageBox::keyPressEvent(QKeyEvent *e)
#if defined(Q_OS_WIN)
if (e == QKeySequence::Copy) {
- const QLatin1String separator("---------------------------\n");
+ const auto separator = "---------------------------\n"_L1;
QString textToCopy;
- textToCopy += separator + windowTitle() + QLatin1Char('\n') + separator // title
- + d->label->text() + QLatin1Char('\n') + separator; // text
+ textToCopy += separator + windowTitle() + u'\n' + separator // title
+ + d->label->text() + u'\n' + separator; // text
if (d->informativeLabel)
- textToCopy += d->informativeLabel->text() + QLatin1Char('\n') + separator;
+ textToCopy += d->informativeLabel->text() + u'\n' + separator;
const QList<QAbstractButton *> buttons = d->buttonBox->buttons();
for (const auto *button : buttons)
- textToCopy += button->text() + QLatin1String(" ");
- textToCopy += QLatin1Char('\n') + separator;
+ textToCopy += button->text() + " "_L1;
+ textToCopy += u'\n' + separator;
#if QT_CONFIG(textedit)
if (d->detailsText)
- textToCopy += d->detailsText->text() + QLatin1Char('\n') + separator;
+ textToCopy += d->detailsText->text() + u'\n' + separator;
#endif
QGuiApplication::clipboard()->setText(textToCopy);
return;
@@ -1555,6 +1681,26 @@ void QMessageBox::open(QObject *receiver, const char *member)
QDialog::open();
}
+void QMessageBoxPrivate::setVisible(bool visible)
+{
+ Q_Q(QMessageBox);
+
+ // Last minute setup
+ if (autoAddOkButton)
+ q->addButton(QMessageBox::Ok);
+ detectEscapeButton();
+
+ if (canBeNativeDialog())
+ setNativeDialogVisible(visible);
+
+ // Update WA_DontShowOnScreen based on whether the native dialog was shown,
+ // so that QDialog::setVisible(visible) below updates the QWidget state correctly,
+ // but skips showing the non-native version.
+ q->setAttribute(Qt::WA_DontShowOnScreen, nativeDialogInUse);
+
+ QDialogPrivate::setVisible(visible);
+}
+
/*!
\since 4.5
@@ -1588,15 +1734,10 @@ 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->detectEscapeButton();
+ d->clickedButton = nullptr;
d->updateSize();
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
QAccessibleEvent event(this, QAccessible::Alert);
QAccessible::updateAccessibility(&event);
#endif
@@ -1618,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*>();
@@ -1791,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()
*/
@@ -1810,7 +1955,7 @@ void QMessageBox::about(QWidget *parent, const QString &title, const QString &te
}
#endif
- QMessageBox *msgBox = new QMessageBox(title, text, Information, 0, 0, 0, parent
+ QMessageBox *msgBox = new QMessageBox(Information, title, text, NoButton, parent
#ifdef Q_OS_MAC
, Qt::WindowTitleHint | Qt::WindowSystemMenuHint
#endif
@@ -1823,11 +1968,12 @@ 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);
+ msgBox->setModal(false);
#endif
msgBox->show();
#else
@@ -1845,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()
@@ -1867,7 +2013,7 @@ void QMessageBox::aboutQt(QWidget *parent, const QString &title)
translatedTextAboutQtCaption = QMessageBox::tr(
"<h3>About Qt</h3>"
"<p>This program uses Qt version %1.</p>"
- ).arg(QLatin1String(QT_VERSION_STR));
+ ).arg(QT_VERSION_STR ""_L1);
//: Leave this text untranslated or include a verbatim copy of it below
//: and note that it is the authoritative version in case of doubt.
const QString translatedTextAboutQtText = QMessageBox::tr(
@@ -1885,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(QStringLiteral("2021"),
- 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);
@@ -1901,18 +2046,19 @@ void QMessageBox::aboutQt(QWidget *parent, const QString &title)
msgBox->setText(translatedTextAboutQtCaption);
msgBox->setInformativeText(translatedTextAboutQtText);
- QPixmap pm(QLatin1String(":/qt-project.org/qmessagebox/images/qtlogo-64.png"));
+ QPixmap pm(":/qt-project.org/qmessagebox/images/qtlogo-64.png"_L1);
if (!pm.isNull())
msgBox->setIconPixmap(pm);
// 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);
+ msgBox->setModal(false);
#endif
msgBox->show();
#else
@@ -1929,33 +2075,7 @@ static QMessageBox::StandardButton newButton(int button)
if (button == QMessageBox::NoButton || (button & NewButtonMask))
return QMessageBox::StandardButton(button & QMessageBox::ButtonMask);
-#if QT_VERSION < 0x050000
- // this is needed for binary compatibility with Qt 4.0 and 4.1
- switch (button & Old_ButtonMask) {
- case Old_Ok:
- return QMessageBox::Ok;
- case Old_Cancel:
- return QMessageBox::Cancel;
- case Old_Yes:
- return QMessageBox::Yes;
- case Old_No:
- return QMessageBox::No;
- case Old_Abort:
- return QMessageBox::Abort;
- case Old_Retry:
- return QMessageBox::Retry;
- case Old_Ignore:
- return QMessageBox::Ignore;
- case Old_YesAll:
- return QMessageBox::YesToAll;
- case Old_NoAll:
- return QMessageBox::NoToAll;
- default:
- return QMessageBox::NoButton;
- }
-#else
return QMessageBox::NoButton;
-#endif
}
static bool detectedCompat(int button0, int button1, int button2)
@@ -2161,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
@@ -2460,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.
@@ -2481,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
@@ -2560,14 +2680,18 @@ void QMessageBox::setDetailedText(const QString &text)
\since 4.2
- Infromative text can be used to expand upon the text() to give more
- information to the user. On the Mac, this text appears in small
- system font below the text(). On other platforms, it is simply
- appended to the existing text.
+ Informative text can be used to expand upon the text() to give more
+ 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
{
@@ -2587,16 +2711,16 @@ void QMessageBox::setInformativeText(const QString &text)
} else {
if (!d->informativeLabel) {
QLabel *label = new QLabel;
- label->setObjectName(QLatin1String("qt_msgbox_informativelabel"));
+ label->setObjectName("qt_msgbox_informativelabel"_L1);
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);
@@ -2664,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;
}
@@ -2676,13 +2801,21 @@ 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)));
- static_cast<QPlatformMessageDialogHelper *>(h)->setOptions(options);
+ auto *messageDialogHelper = static_cast<QPlatformMessageDialogHelper *>(h);
+ 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);
+ }
+ );
+ messageDialogHelper->setOptions(options);
}
-static QMessageDialogOptions::Icon helperIcon(QMessageBox::Icon i)
+static QMessageDialogOptions::StandardIcon helperIcon(QMessageBox::Icon i)
{
switch (i) {
case QMessageBox::NoIcon:
@@ -2705,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);
@@ -2714,23 +2877,80 @@ void QMessageBoxPrivate::helperPrepareShow(QPlatformDialogHelper *)
#if QT_CONFIG(textedit)
options->setDetailedText(q->detailedText());
#endif
- options->setIcon(helperIcon(q->icon()));
- options->setStandardButtons(helperStandardButtons(q));
+ options->setStandardIcon(helperIcon(q->icon()));
+ options->setIconPixmap(q->iconPixmap());
+
+ // 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
@@ -2794,6 +3014,25 @@ QPixmap QMessageBox::standardIcon(Icon icon)
\sa show(), result()
*/
+/*!
+ \macro QT_REQUIRE_VERSION(int argc, char **argv, const char *version)
+ \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
+ if your application depends on a specific bug fix introduced in a
+ bug-fix release (for example, 6.1.2).
+
+ The \a argc and \a argv parameters are the \c main() function's
+ \c argc and \c argv parameters. The \a version parameter is a
+ string literal that specifies which version of Qt the application
+ requires (for example, "6.1.2").
+
+ Example:
+
+ \snippet code/src_gui_dialogs_qmessagebox.cpp 4
+*/
+
QT_END_NAMESPACE
#include "moc_qmessagebox.cpp"