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 | |
53 | QT_BEGIN_NAMESPACE |
54 | |
55 | class QProgressBarPrivate : public QWidgetPrivate |
56 | { |
57 | Q_DECLARE_PUBLIC(QProgressBar) |
58 | |
59 | public: |
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 | |
81 | QProgressBarPrivate::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 | |
89 | void QProgressBarPrivate::initDefaultFormat() |
90 | { |
91 | if (defaultFormat) |
92 | format = QLatin1String("%p") + locale.percent(); |
93 | } |
94 | |
95 | void 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 | |
106 | void 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 | */ |
121 | void 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 | |
141 | bool 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 | |
236 | QProgressBar::QProgressBar(QWidget *parent) |
237 | : QWidget(*(new QProgressBarPrivate), parent, { }) |
238 | { |
239 | d_func()->init(); |
240 | } |
241 | |
242 | /*! |
243 | Destructor. |
244 | */ |
245 | QProgressBar::~QProgressBar() |
246 | { |
247 | } |
248 | |
249 | /*! |
250 | Reset the progress bar. The progress bar "rewinds" and shows no |
251 | progress. |
252 | */ |
253 | |
254 | void 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 | */ |
273 | void QProgressBar::setMinimum(int minimum) |
274 | { |
275 | setRange(minimum, maximum: qMax(a: d_func()->maximum, b: minimum)); |
276 | } |
277 | |
278 | int 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 | |
294 | void QProgressBar::setMaximum(int maximum) |
295 | { |
296 | setRange(minimum: qMin(a: d_func()->minimum, b: maximum), maximum); |
297 | } |
298 | |
299 | int 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 | */ |
311 | void 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 | |
330 | int 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 | */ |
349 | void 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 | */ |
371 | void QProgressBar::setTextVisible(bool visible) |
372 | { |
373 | Q_D(QProgressBar); |
374 | if (d->textVisible != visible) { |
375 | d->textVisible = visible; |
376 | repaint(); |
377 | } |
378 | } |
379 | |
380 | bool 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 | */ |
389 | void QProgressBar::setAlignment(Qt::Alignment alignment) |
390 | { |
391 | if (d_func()->alignment != alignment) { |
392 | d_func()->alignment = alignment; |
393 | repaint(); |
394 | } |
395 | } |
396 | |
397 | Qt::Alignment QProgressBar::alignment() const |
398 | { |
399 | return d_func()->alignment; |
400 | } |
401 | |
402 | /*! |
403 | \reimp |
404 | */ |
405 | void 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 | */ |
417 | QSize 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 | */ |
433 | QSize 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 | */ |
458 | QString 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 | |
497 | void 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 | |
512 | Qt::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 | |
530 | void QProgressBar::setInvertedAppearance(bool invert) |
531 | { |
532 | Q_D(QProgressBar); |
533 | d->invertedAppearance = invert; |
534 | update(); |
535 | } |
536 | |
537 | bool 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 | */ |
553 | void QProgressBar::setTextDirection(QProgressBar::Direction textDirection) |
554 | { |
555 | Q_D(QProgressBar); |
556 | d->textDirection = textDirection; |
557 | update(); |
558 | } |
559 | |
560 | QProgressBar::Direction QProgressBar::textDirection() const |
561 | { |
562 | Q_D(const QProgressBar); |
563 | return d->textDirection; |
564 | } |
565 | |
566 | /*! \reimp */ |
567 | bool 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 | */ |
599 | void 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 | |
609 | void QProgressBar::resetFormat() |
610 | { |
611 | Q_D(QProgressBar); |
612 | d->defaultFormat = true; |
613 | d->initDefaultFormat(); |
614 | update(); |
615 | } |
616 | |
617 | QString QProgressBar::format() const |
618 | { |
619 | Q_D(const QProgressBar); |
620 | return d->format; |
621 | } |
622 | |
623 | QT_END_NAMESPACE |
624 | |
625 | #include "moc_qprogressbar.cpp" |
626 |
Definitions
- QProgressBarPrivate
- bound
- QProgressBarPrivate
- initDefaultFormat
- init
- resetLayoutItemMargins
- initStyleOption
- repaintRequired
- QProgressBar
- ~QProgressBar
- reset
- setMinimum
- minimum
- setMaximum
- maximum
- setValue
- value
- setRange
- setTextVisible
- isTextVisible
- setAlignment
- alignment
- paintEvent
- sizeHint
- minimumSizeHint
- text
- setOrientation
- orientation
- setInvertedAppearance
- invertedAppearance
- setTextDirection
- textDirection
- event
- setFormat
- resetFormat
Start learning QML with our Intro Training
Find out more