1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qprogressbar.h"
41
42#include <qlocale.h>
43#include <qevent.h>
44#include <qpainter.h>
45#include <qstylepainter.h>
46#include <qstyleoption.h>
47#include <private/qwidget_p.h>
48#ifndef QT_NO_ACCESSIBILITY
49#include <qaccessible.h>
50#endif
51#include <limits.h>
52
53QT_BEGIN_NAMESPACE
54
55class QProgressBarPrivate : public QWidgetPrivate
56{
57 Q_DECLARE_PUBLIC(QProgressBar)
58
59public:
60 QProgressBarPrivate();
61
62 void init();
63 void initDefaultFormat();
64 inline void resetLayoutItemMargins();
65
66 int minimum;
67 int maximum;
68 int value;
69 Qt::Alignment alignment;
70 uint textVisible : 1;
71 uint defaultFormat: 1;
72 int lastPaintedValue;
73 Qt::Orientation orientation;
74 bool invertedAppearance;
75 QProgressBar::Direction textDirection;
76 QString format;
77 inline int bound(int val) const { return qMax(a: minimum-1, b: qMin(a: maximum, b: val)); }
78 bool repaintRequired() const;
79};
80
81QProgressBarPrivate::QProgressBarPrivate()
82 : minimum(0), maximum(100), value(-1), alignment(Qt::AlignLeft), textVisible(true),
83 defaultFormat(true), lastPaintedValue(-1), orientation(Qt::Horizontal), invertedAppearance(false),
84 textDirection(QProgressBar::TopToBottom)
85{
86 initDefaultFormat();
87}
88
89void QProgressBarPrivate::initDefaultFormat()
90{
91 if (defaultFormat)
92 format = QLatin1String("%p") + locale.percent();
93}
94
95void QProgressBarPrivate::init()
96{
97 Q_Q(QProgressBar);
98 QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Fixed);
99 if (orientation == Qt::Vertical)
100 sp.transpose();
101 q->setSizePolicy(sp);
102 q->setAttribute(Qt::WA_WState_OwnSizePolicy, on: false);
103 resetLayoutItemMargins();
104}
105
106void QProgressBarPrivate::resetLayoutItemMargins()
107{
108 Q_Q(QProgressBar);
109 QStyleOptionProgressBar option;
110 q->initStyleOption(option: &option);
111 setLayoutItemMargins(element: QStyle::SE_ProgressBarLayoutItem, opt: &option);
112}
113
114/*!
115 Initialize \a option with the values from this QProgressBar. This method is useful
116 for subclasses when they need a QStyleOptionProgressBar,
117 but don't want to fill in all the information themselves.
118
119 \sa QStyleOption::initFrom()
120*/
121void QProgressBar::initStyleOption(QStyleOptionProgressBar *option) const
122{
123 if (!option)
124 return;
125 Q_D(const QProgressBar);
126 option->initFrom(w: this);
127
128 if (d->orientation == Qt::Horizontal)
129 option->state |= QStyle::State_Horizontal;
130 option->minimum = d->minimum;
131 option->maximum = d->maximum;
132 option->progress = d->value;
133 option->textAlignment = d->alignment;
134 option->textVisible = d->textVisible;
135 option->text = text();
136 option->orientation = d->orientation; // ### Qt 6: remove this member from QStyleOptionProgressBar
137 option->invertedAppearance = d->invertedAppearance;
138 option->bottomToTop = d->textDirection == QProgressBar::BottomToTop;
139}
140
141bool QProgressBarPrivate::repaintRequired() const
142{
143 Q_Q(const QProgressBar);
144 if (value == lastPaintedValue)
145 return false;
146
147 const auto valueDifference = qAbs(t: qint64(value) - lastPaintedValue);
148 // Check if the text needs to be repainted
149 if (value == minimum || value == maximum)
150 return true;
151
152 const auto totalSteps = qint64(maximum) - minimum;
153 if (textVisible) {
154 if ((format.contains(s: QLatin1String("%v"))))
155 return true;
156 if ((format.contains(s: QLatin1String("%p"))
157 && valueDifference >= qAbs(t: totalSteps / 100)))
158 return true;
159 }
160
161 // Check if the bar needs to be repainted
162 QStyleOptionProgressBar opt;
163 q->initStyleOption(option: &opt);
164 int cw = q->style()->pixelMetric(metric: QStyle::PM_ProgressBarChunkWidth, option: &opt, widget: q);
165 QRect groove = q->style()->subElementRect(subElement: QStyle::SE_ProgressBarGroove, option: &opt, widget: q);
166 // This expression is basically
167 // (valueDifference / (maximum - minimum) > cw / groove.width())
168 // transformed to avoid integer division.
169 int grooveBlock = (q->orientation() == Qt::Horizontal) ? groove.width() : groove.height();
170 return valueDifference * grooveBlock > cw * totalSteps;
171}
172
173/*!
174 \class QProgressBar
175 \brief The QProgressBar widget provides a horizontal or vertical progress bar.
176
177 \ingroup basicwidgets
178 \inmodule QtWidgets
179
180 \image windows-progressbar.png
181
182 A progress bar is used to give the user an indication of the
183 progress of an operation and to reassure them that the application
184 is still running.
185
186 The progress bar uses the concept of \e steps. You set it up by
187 specifying the minimum and maximum possible step values, and it
188 will display the percentage of steps that have been completed
189 when you later give it the current step value. The percentage is
190 calculated by dividing the progress (value() - minimum()) divided
191 by maximum() - minimum().
192
193 You can specify the minimum and maximum number of steps with
194 setMinimum() and setMaximum. The current number of steps is set
195 with setValue(). The progress bar can be rewound to the
196 beginning with reset().
197
198 If minimum and maximum both are set to 0, the bar shows a busy
199 indicator instead of a percentage of steps. This is useful, for
200 example, when using QNetworkAccessManager to download items when
201 they are unable to determine the size of the item being downloaded.
202
203 \sa QProgressDialog, {fowler}{GUI Design Handbook: Progress Indicator}
204*/
205
206/*!
207 \since 4.1
208 \enum QProgressBar::Direction
209 \brief Specifies the reading direction of the \l text for vertical progress bars.
210
211 \value TopToBottom The text is rotated 90 degrees clockwise.
212 \value BottomToTop The text is rotated 90 degrees counter-clockwise.
213
214 Note that whether or not the text is drawn is dependent on the style.
215 Currently CleanLooks and Plastique draw the text. Mac, Windows
216 and WindowsVista style do not.
217
218 \sa textDirection
219*/
220
221/*!
222 \fn void QProgressBar::valueChanged(int value)
223
224 This signal is emitted when the value shown in the progress bar changes.
225 \a value is the new value shown by the progress bar.
226*/
227
228/*!
229 Constructs a progress bar with the given \a parent.
230
231 By default, the minimum step value is set to 0, and the maximum to 100.
232
233 \sa setRange()
234*/
235
236QProgressBar::QProgressBar(QWidget *parent)
237 : QWidget(*(new QProgressBarPrivate), parent, { })
238{
239 d_func()->init();
240}
241
242/*!
243 Destructor.
244*/
245QProgressBar::~QProgressBar()
246{
247}
248
249/*!
250 Reset the progress bar. The progress bar "rewinds" and shows no
251 progress.
252*/
253
254void QProgressBar::reset()
255{
256 Q_D(QProgressBar);
257 if (d->minimum == INT_MIN)
258 d->value = INT_MIN;
259 else
260 d->value = d->minimum - 1;
261 repaint();
262}
263
264/*!
265 \property QProgressBar::minimum
266 \brief the progress bar's minimum value
267
268 When setting this property, the \l maximum is adjusted if
269 necessary to ensure that the range remains valid. If the
270 current value falls outside the new range, the progress bar is reset
271 with reset().
272*/
273void QProgressBar::setMinimum(int minimum)
274{
275 setRange(minimum, maximum: qMax(a: d_func()->maximum, b: minimum));
276}
277
278int QProgressBar::minimum() const
279{
280 return d_func()->minimum;
281}
282
283
284/*!
285 \property QProgressBar::maximum
286 \brief the progress bar's maximum value
287
288 When setting this property, the \l minimum is adjusted if
289 necessary to ensure that the range remains valid. If the
290 current value falls outside the new range, the progress bar is reset
291 with reset().
292*/
293
294void QProgressBar::setMaximum(int maximum)
295{
296 setRange(minimum: qMin(a: d_func()->minimum, b: maximum), maximum);
297}
298
299int QProgressBar::maximum() const
300{
301 return d_func()->maximum;
302}
303
304/*!
305 \property QProgressBar::value
306 \brief the progress bar's current value
307
308 Attempting to change the current value to one outside
309 the minimum-maximum range has no effect on the current value.
310*/
311void QProgressBar::setValue(int value)
312{
313 Q_D(QProgressBar);
314 if (d->value == value
315 || ((value > d->maximum || value < d->minimum)
316 && (d->maximum != 0 || d->minimum != 0)))
317 return;
318 d->value = value;
319 emit valueChanged(value);
320#ifndef QT_NO_ACCESSIBILITY
321 if (isVisible()) {
322 QAccessibleValueChangeEvent event(this, value);
323 QAccessible::updateAccessibility(event: &event);
324 }
325#endif
326 if (d->repaintRequired())
327 repaint();
328}
329
330int QProgressBar::value() const
331{
332 return d_func()->value;
333}
334
335/*!
336 Sets the progress bar's minimum and maximum values to \a minimum and
337 \a maximum respectively.
338
339 If \a maximum is smaller than \a minimum, \a minimum becomes the only
340 legal value.
341
342 If the current value falls outside the new range, the progress bar is reset
343 with reset().
344
345 The QProgressBar can be set to undetermined state by using setRange(0, 0).
346
347 \sa minimum, maximum
348*/
349void QProgressBar::setRange(int minimum, int maximum)
350{
351 Q_D(QProgressBar);
352 if (minimum != d->minimum || maximum != d->maximum) {
353 d->minimum = minimum;
354 d->maximum = qMax(a: minimum, b: maximum);
355
356 if (d->value < qint64(d->minimum) - 1 || d->value > d->maximum)
357 reset();
358 else
359 update();
360 }
361}
362
363/*!
364 \property QProgressBar::textVisible
365 \brief whether the current completed percentage should be displayed
366
367 This property may be ignored by the style (e.g., QMacStyle never draws the text).
368
369 \sa textDirection
370*/
371void QProgressBar::setTextVisible(bool visible)
372{
373 Q_D(QProgressBar);
374 if (d->textVisible != visible) {
375 d->textVisible = visible;
376 repaint();
377 }
378}
379
380bool QProgressBar::isTextVisible() const
381{
382 return d_func()->textVisible;
383}
384
385/*!
386 \property QProgressBar::alignment
387 \brief the alignment of the progress bar
388*/
389void QProgressBar::setAlignment(Qt::Alignment alignment)
390{
391 if (d_func()->alignment != alignment) {
392 d_func()->alignment = alignment;
393 repaint();
394 }
395}
396
397Qt::Alignment QProgressBar::alignment() const
398{
399 return d_func()->alignment;
400}
401
402/*!
403 \reimp
404*/
405void QProgressBar::paintEvent(QPaintEvent *)
406{
407 QStylePainter paint(this);
408 QStyleOptionProgressBar opt;
409 initStyleOption(option: &opt);
410 paint.drawControl(ce: QStyle::CE_ProgressBar, opt);
411 d_func()->lastPaintedValue = d_func()->value;
412}
413
414/*!
415 \reimp
416*/
417QSize QProgressBar::sizeHint() const
418{
419 ensurePolished();
420 QFontMetrics fm = fontMetrics();
421 QStyleOptionProgressBar opt;
422 initStyleOption(option: &opt);
423 int cw = style()->pixelMetric(metric: QStyle::PM_ProgressBarChunkWidth, option: &opt, widget: this);
424 QSize size = QSize(qMax(a: 9, b: cw) * 7 + fm.horizontalAdvance(QLatin1Char('0')) * 4, fm.height() + 8);
425 if (opt.orientation == Qt::Vertical)
426 size = size.transposed();
427 return style()->sizeFromContents(ct: QStyle::CT_ProgressBar, opt: &opt, contentsSize: size, w: this);
428}
429
430/*!
431 \reimp
432*/
433QSize QProgressBar::minimumSizeHint() const
434{
435 QSize size;
436 if (orientation() == Qt::Horizontal)
437 size = QSize(sizeHint().width(), fontMetrics().height() + 2);
438 else
439 size = QSize(fontMetrics().height() + 2, sizeHint().height());
440 return size;
441}
442
443/*!
444 \property QProgressBar::text
445 \brief the descriptive text shown with the progress bar
446
447 The text returned is the same as the text displayed in the center
448 (or in some styles, to the left) of the progress bar.
449
450 The progress shown in the text may be smaller than the minimum value,
451 indicating that the progress bar is in the "reset" state before any
452 progress is set.
453
454 In the default implementation, the text either contains a percentage
455 value that indicates the progress so far, or it is blank because the
456 progress bar is in the reset state.
457*/
458QString QProgressBar::text() const
459{
460 Q_D(const QProgressBar);
461 if ((d->maximum == 0 && d->minimum == 0) || d->value < d->minimum
462 || (d->value == INT_MIN && d->minimum == INT_MIN))
463 return QString();
464
465 qint64 totalSteps = qint64(d->maximum) - d->minimum;
466
467 QString result = d->format;
468 QLocale locale = d->locale; // Omit group separators for compatibility with previous versions that were non-localized.
469 locale.setNumberOptions(locale.numberOptions() | QLocale::OmitGroupSeparator);
470 result.replace(before: QLatin1String("%m"), after: locale.toString(i: totalSteps));
471 result.replace(before: QLatin1String("%v"), after: locale.toString(i: d->value));
472
473 // If max and min are equal and we get this far, it means that the
474 // progress bar has one step and that we are on that step. Return
475 // 100% here in order to avoid division by zero further down.
476 if (totalSteps == 0) {
477 result.replace(before: QLatin1String("%p"), after: locale.toString(i: 100));
478 return result;
479 }
480
481 const auto progress = static_cast<int>((qint64(d->value) - d->minimum) * 100.0 / totalSteps);
482 result.replace(before: QLatin1String("%p"), after: locale.toString(i: progress));
483 return result;
484}
485
486/*!
487 \since 4.1
488 \property QProgressBar::orientation
489 \brief the orientation of the progress bar
490
491 The orientation must be \l Qt::Horizontal (the default) or \l
492 Qt::Vertical.
493
494 \sa invertedAppearance, textDirection
495*/
496
497void QProgressBar::setOrientation(Qt::Orientation orientation)
498{
499 Q_D(QProgressBar);
500 if (d->orientation == orientation)
501 return;
502 d->orientation = orientation;
503 if (!testAttribute(attribute: Qt::WA_WState_OwnSizePolicy)) {
504 setSizePolicy(sizePolicy().transposed());
505 setAttribute(Qt::WA_WState_OwnSizePolicy, on: false);
506 }
507 d->resetLayoutItemMargins();
508 update();
509 updateGeometry();
510}
511
512Qt::Orientation QProgressBar::orientation() const
513{
514 Q_D(const QProgressBar);
515 return d->orientation;
516}
517
518/*!
519 \since 4.1
520 \property QProgressBar::invertedAppearance
521 \brief whether or not a progress bar shows its progress inverted
522
523 If this property is \c true, the progress bar grows in the other
524 direction (e.g. from right to left). By default, the progress bar
525 is not inverted.
526
527 \sa orientation, layoutDirection
528*/
529
530void QProgressBar::setInvertedAppearance(bool invert)
531{
532 Q_D(QProgressBar);
533 d->invertedAppearance = invert;
534 update();
535}
536
537bool QProgressBar::invertedAppearance() const
538{
539 Q_D(const QProgressBar);
540 return d->invertedAppearance;
541}
542
543/*!
544 \since 4.1
545 \property QProgressBar::textDirection
546 \brief the reading direction of the \l text for vertical progress bars
547
548 This property has no impact on horizontal progress bars.
549 By default, the reading direction is QProgressBar::TopToBottom.
550
551 \sa orientation, textVisible
552*/
553void QProgressBar::setTextDirection(QProgressBar::Direction textDirection)
554{
555 Q_D(QProgressBar);
556 d->textDirection = textDirection;
557 update();
558}
559
560QProgressBar::Direction QProgressBar::textDirection() const
561{
562 Q_D(const QProgressBar);
563 return d->textDirection;
564}
565
566/*! \reimp */
567bool QProgressBar::event(QEvent *e)
568{
569 Q_D(QProgressBar);
570 switch (e->type()) {
571 case QEvent::StyleChange:
572#ifdef Q_OS_MAC
573 case QEvent::MacSizeChange:
574#endif
575 d->resetLayoutItemMargins();
576 break;
577 case QEvent::LocaleChange:
578 d->initDefaultFormat();
579 break;
580 default:
581 break;
582 }
583 return QWidget::event(event: e);
584}
585
586/*!
587 \since 4.2
588 \property QProgressBar::format
589 \brief the string used to generate the current text
590
591 %p - is replaced by the percentage completed.
592 %v - is replaced by the current value.
593 %m - is replaced by the total number of steps.
594
595 The default value is "%p%".
596
597 \sa text()
598*/
599void QProgressBar::setFormat(const QString &format)
600{
601 Q_D(QProgressBar);
602 if (d->format == format)
603 return;
604 d->format = format;
605 d->defaultFormat = false;
606 update();
607}
608
609void QProgressBar::resetFormat()
610{
611 Q_D(QProgressBar);
612 d->defaultFormat = true;
613 d->initDefaultFormat();
614 update();
615}
616
617QString QProgressBar::format() const
618{
619 Q_D(const QProgressBar);
620 return d->format;
621}
622
623QT_END_NAMESPACE
624
625#include "moc_qprogressbar.cpp"
626

source code of qtbase/src/widgets/widgets/qprogressbar.cpp