diff options
Diffstat (limited to 'src/widgets/dialogs/qprogressdialog.cpp')
-rw-r--r-- | src/widgets/dialogs/qprogressdialog.cpp | 224 |
1 files changed, 90 insertions, 134 deletions
diff --git a/src/widgets/dialogs/qprogressdialog.cpp b/src/widgets/dialogs/qprogressdialog.cpp index e1fb1055ae..9761bcc828 100644 --- a/src/widgets/dialogs/qprogressdialog.cpp +++ b/src/widgets/dialogs/qprogressdialog.cpp @@ -1,45 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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) 2016 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 "qprogressdialog.h" -#include "qshortcut.h" +#if QT_CONFIG(shortcut) +# include "qshortcut.h" +#endif #include "qpainter.h" #include "qdrawutil.h" #include "qlabel.h" @@ -47,36 +13,31 @@ #include "qapplication.h" #include "qstyle.h" #include "qpushbutton.h" -#include "qcursor.h" #include "qtimer.h" #include "qelapsedtimer.h" +#include "qscopedvaluerollback.h" #include <private/qdialog_p.h> + +#include <QtCore/qpointer.h> + #include <limits.h> +using namespace std::chrono_literals; + QT_BEGIN_NAMESPACE // If the operation is expected to take this long (as predicted by // progress time), show the progress dialog. -static const int defaultShowTime = 4000; +static constexpr auto defaultShowTime = 4000ms; // Wait at least this long before attempting to make a prediction. -static const int minWaitTime = 50; +static constexpr auto minWaitTime = 50ms; class QProgressDialogPrivate : public QDialogPrivate { Q_DECLARE_PUBLIC(QProgressDialog) public: - QProgressDialogPrivate() : label(0), cancel(0), bar(0), - shown_once(false), - cancellation_flag(false), - setValue_called(false), - showTime(defaultShowTime), -#ifndef QT_NO_SHORTCUT - escapeShortcut(0), -#endif - useDefaultCancelText(false) - { - } + QProgressDialogPrivate() = default; void init(const QString &labelText, const QString &cancelText, int min, int max); void layout(); @@ -86,27 +47,25 @@ public: void ensureSizeIsAtLeastSizeHint(); void _q_disconnectOnClose(); - QLabel *label; - QPushButton *cancel; - QProgressBar *bar; - QTimer *forceTimer; - bool shown_once; - bool cancellation_flag; - bool setValue_called; - QElapsedTimer starttime; -#ifndef QT_NO_CURSOR - QCursor parentCursor; -#endif - int showTime; - bool autoClose; - bool autoReset; - bool forceHide; + QLabel *label = nullptr; + QPushButton *cancel = nullptr; + QProgressBar *bar = nullptr; + QTimer *forceTimer = nullptr; #ifndef QT_NO_SHORTCUT - QShortcut *escapeShortcut; + QShortcut *escapeShortcut = nullptr; #endif - bool useDefaultCancelText; QPointer<QObject> receiverToDisconnectOnClose; + QElapsedTimer starttime; QByteArray memberToDisconnectOnClose; + std::chrono::milliseconds showTime = defaultShowTime; + bool processingEvents = false; + bool shownOnce = false; + bool autoClose = true; + bool autoReset = true; + bool forceHide = false; + bool cancellationFlag = false; + bool setValueCalled = false; + bool useDefaultCancelText = false; }; void QProgressDialogPrivate::init(const QString &labelText, const QString &cancelText, @@ -116,11 +75,8 @@ void QProgressDialogPrivate::init(const QString &labelText, const QString &cance label = new QLabel(labelText, q); bar = new QProgressBar(q); bar->setRange(min, max); - int align = q->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, 0, q); + int align = q->style()->styleHint(QStyle::SH_ProgressDialog_TextLabelAlignment, nullptr, q); label->setAlignment(Qt::Alignment(align)); - autoClose = true; - autoReset = true; - forceHide = false; QObject::connect(q, SIGNAL(canceled()), q, SLOT(cancel())); forceTimer = new QTimer(q); QObject::connect(forceTimer, SIGNAL(timeout()), q, SLOT(forceShow())); @@ -136,12 +92,12 @@ void QProgressDialogPrivate::init(const QString &labelText, const QString &cance void QProgressDialogPrivate::layout() { Q_Q(QProgressDialog); - int sp = q->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing, 0, q); - int mb = q->style()->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, q); - int ml = qMin(q->width() / 10, q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, q)); - int mr = qMin(q->width() / 10, q->style()->pixelMetric(QStyle::PM_LayoutRightMargin, 0, q)); + int sp = q->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing, nullptr, q); + int mb = q->style()->pixelMetric(QStyle::PM_LayoutBottomMargin, nullptr, q); + int ml = qMin(q->width() / 10, q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin, nullptr, q)); + int mr = qMin(q->width() / 10, q->style()->pixelMetric(QStyle::PM_LayoutRightMargin, nullptr, q)); const bool centered = - bool(q->style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, 0, q)); + bool(q->style()->styleHint(QStyle::SH_ProgressDialog_CenterCancelButton, nullptr, q)); int additionalSpacing = 0; QSize cs = cancel ? cancel->sizeHint() : QSize(0,0); @@ -192,7 +148,7 @@ void QProgressDialogPrivate::_q_disconnectOnClose() if (receiverToDisconnectOnClose) { QObject::disconnect(q, SIGNAL(canceled()), receiverToDisconnectOnClose, memberToDisconnectOnClose); - receiverToDisconnectOnClose = 0; + receiverToDisconnectOnClose = nullptr; } memberToDisconnectOnClose.clear(); } @@ -241,10 +197,11 @@ void QProgressDialogPrivate::_q_disconnectOnClose() A modeless progress dialog is suitable for operations that take place in the background, where the user is able to interact with the - application. Such operations are typically based on QTimer (or - QObject::timerEvent()) or QSocketNotifier; or performed - in a separate thread. A QProgressBar in the status bar of your main window - is often an alternative to a modeless progress dialog. + application. Such operations are typically based on a timer class, + such as QChronoTimer (or the more low-level QObject::timerEvent()) or + QSocketNotifier; or performed in a separate thread. A QProgressBar in + the status bar of your main window is often an alternative to a modeless + progress dialog. You need to have an event loop to be running, connect the canceled() signal to a slot that stops the operation, and call \l @@ -264,8 +221,7 @@ void QProgressDialogPrivate::_q_disconnectOnClose() \image fusion-progressdialog.png A progress dialog shown in the Fusion widget style. - \sa QDialog, QProgressBar, {fowler}{GUI Design Handbook: Progress Indicator}, - {Find Files Example}, {Pixelator Example} + \sa QDialog, QProgressBar */ @@ -416,13 +372,12 @@ void QProgressDialog::setCancelButton(QPushButton *cancelButton) if (cancelButton) { connect(d->cancel, SIGNAL(clicked()), this, SIGNAL(canceled())); #ifndef QT_NO_SHORTCUT - // FIXME: This only registers the primary key sequence of the cancel action d->escapeShortcut = new QShortcut(QKeySequence::Cancel, this, SIGNAL(canceled())); #endif } else { #ifndef QT_NO_SHORTCUT delete d->escapeShortcut; - d->escapeShortcut = 0; + d->escapeShortcut = nullptr; #endif } d->adoptChildWidget(cancelButton); @@ -454,7 +409,7 @@ void QProgressDialogPrivate::setCancelButtonText(const QString &cancelButtonText q->setCancelButton(new QPushButton(cancelButtonText, q)); } } else { - q->setCancelButton(0); + q->setCancelButton(nullptr); } ensureSizeIsAtLeastSizeHint(); } @@ -496,9 +451,11 @@ void QProgressDialogPrivate::adoptChildWidget(QWidget *c) if (c->parentWidget() == q) c->hide(); // until after ensureSizeIsAtLeastSizeHint() else - c->setParent(q, 0); + c->setParent(q, { }); } ensureSizeIsAtLeastSizeHint(); + //The layout should be updated again to prevent layout errors when the new 'widget' is replaced + layout(); if (c) c->show(); } @@ -522,7 +479,7 @@ void QProgressDialogPrivate::ensureSizeIsAtLeastSizeHint() bool QProgressDialog::wasCanceled() const { Q_D(const QProgressDialog); - return d->cancellation_flag; + return d->cancellationFlag; } @@ -597,18 +554,12 @@ void QProgressDialog::setRange(int minimum, int maximum) void QProgressDialog::reset() { Q_D(QProgressDialog); -#ifndef QT_NO_CURSOR - if (value() >= 0) { - if (parentWidget()) - parentWidget()->setCursor(d->parentCursor); - } -#endif if (d->autoClose || d->forceHide) hide(); d->bar->reset(); - d->cancellation_flag = false; - d->shown_once = false; - d->setValue_called = false; + d->cancellationFlag = false; + d->shownOnce = false; + d->setValueCalled = false; d->forceTimer->stop(); /* @@ -632,7 +583,7 @@ void QProgressDialog::cancel() d->forceHide = true; reset(); d->forceHide = false; - d->cancellation_flag = true; + d->cancellationFlag = true; } @@ -653,7 +604,7 @@ int QProgressDialog::value() const \warning If the progress dialog is modal (see QProgressDialog::QProgressDialog()), - setValue() calls QApplication::processEvents(), so take care that + setValue() calls QCoreApplication::processEvents(), so take care that this does not cause undesirable re-entrancy in your code. For example, don't use a QProgressDialog inside a paintEvent()! @@ -662,45 +613,46 @@ int QProgressDialog::value() const void QProgressDialog::setValue(int progress) { Q_D(QProgressDialog); - if (d->setValue_called && progress == d->bar->value()) + if (d->setValueCalled && progress == d->bar->value()) return; d->bar->setValue(progress); - if (d->shown_once) { - if (isModal()) - QApplication::processEvents(); + if (d->shownOnce) { + if (isModal() && !d->processingEvents) { + const QScopedValueRollback guard(d->processingEvents, true); + QCoreApplication::processEvents(); + } } else { - if ((!d->setValue_called && progress == 0 /* for compat with Qt < 5.4 */) || progress == minimum()) { + if ((!d->setValueCalled && progress == 0 /* for compat with Qt < 5.4 */) || progress == minimum()) { d->starttime.start(); d->forceTimer->start(d->showTime); - d->setValue_called = true; + d->setValueCalled = true; return; } else { - d->setValue_called = true; - bool need_show; - int elapsed = d->starttime.elapsed(); + d->setValueCalled = true; + bool need_show = false; + using namespace std::chrono; + nanoseconds elapsed = d->starttime.durationElapsed(); if (elapsed >= d->showTime) { need_show = true; } else { if (elapsed > minWaitTime) { - int estimate; - int totalSteps = maximum() - minimum(); - int myprogress = progress - minimum(); - if (myprogress == 0) myprogress = 1; - if ((totalSteps - myprogress) >= INT_MAX / elapsed) - estimate = (totalSteps - myprogress) / myprogress * elapsed; + const int totalSteps = maximum() - minimum(); + const int myprogress = std::max(progress - minimum(), 1); + const int remainingSteps = totalSteps - myprogress; + nanoseconds estimate; + if (remainingSteps >= INT_MAX / elapsed.count()) + estimate = (remainingSteps / myprogress) * elapsed; else - estimate = elapsed * (totalSteps - myprogress) / myprogress; + estimate = (elapsed * remainingSteps) / myprogress; need_show = estimate >= d->showTime; - } else { - need_show = false; } } if (need_show) { d->ensureSizeIsAtLeastSizeHint(); show(); - d->shown_once = true; + d->shownOnce = true; } } } @@ -718,14 +670,17 @@ void QProgressDialog::setValue(int progress) QSize QProgressDialog::sizeHint() const { Q_D(const QProgressDialog); - QSize sh = d->label ? d->label->sizeHint() : QSize(0, 0); - QSize bh = d->bar->sizeHint(); - int margin = style()->pixelMetric(QStyle::PM_DefaultTopLevelMargin); - int spacing = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); - int h = margin * 2 + bh.height() + sh.height() + spacing; + QSize labelSize = d->label ? d->label->sizeHint() : QSize(0, 0); + QSize barSize = d->bar->sizeHint(); + int marginBottom = style()->pixelMetric(QStyle::PM_LayoutBottomMargin, 0, this); + int spacing = style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing, 0, this); + int marginLeft = style()->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, this); + int marginRight = style()->pixelMetric(QStyle::PM_LayoutRightMargin, 0, this); + + int height = marginBottom * 2 + barSize.height() + labelSize.height() + spacing; if (d->cancel) - h += d->cancel->sizeHint().height() + spacing; - return QSize(qMax(200, sh.width() + 2 * margin), h); + height += d->cancel->sizeHint().height() + spacing; + return QSize(qMax(200, labelSize.width() + marginLeft + marginRight), height); } /*!\reimp @@ -767,17 +722,18 @@ void QProgressDialog::changeEvent(QEvent *ev) void QProgressDialog::setMinimumDuration(int ms) { Q_D(QProgressDialog); - d->showTime = ms; + std::chrono::milliseconds msecs{ms}; + d->showTime = msecs; if (d->bar->value() == d->bar->minimum()) { d->forceTimer->stop(); - d->forceTimer->start(ms); + d->forceTimer->start(msecs); } } int QProgressDialog::minimumDuration() const { Q_D(const QProgressDialog); - return d->showTime; + return int(d->showTime.count()); } @@ -793,7 +749,7 @@ void QProgressDialog::closeEvent(QCloseEvent *e) /*! \property QProgressDialog::autoReset - \brief whether the progress dialog calls reset() as soon as value() equals maximum() + \brief whether the progress dialog calls reset() as soon as value() equals maximum(). The default is true. @@ -856,11 +812,11 @@ void QProgressDialog::forceShow() { Q_D(QProgressDialog); d->forceTimer->stop(); - if (d->shown_once || d->cancellation_flag) + if (d->shownOnce || d->cancellationFlag) return; show(); - d->shown_once = true; + d->shownOnce = true; } /*! |