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 "qstatusbar.h"
41
42#include "qlist.h"
43#include "qdebug.h"
44#include "qevent.h"
45#include "qlayout.h"
46#include "qpainter.h"
47#include "qtimer.h"
48#include "qstyle.h"
49#include "qstyleoption.h"
50#if QT_CONFIG(sizegrip)
51#include "qsizegrip.h"
52#endif
53#if QT_CONFIG(mainwindow)
54#include "qmainwindow.h"
55#endif
56
57#ifndef QT_NO_ACCESSIBILITY
58#include "qaccessible.h"
59#endif
60
61#include <private/qlayoutengine_p.h>
62#include <private/qwidget_p.h>
63
64QT_BEGIN_NAMESPACE
65
66class QStatusBarPrivate : public QWidgetPrivate
67{
68 Q_DECLARE_PUBLIC(QStatusBar)
69public:
70 QStatusBarPrivate() {}
71
72 struct SBItem {
73 SBItem(QWidget* widget, int stretch, bool permanent)
74 : s(stretch), w(widget), p(permanent) {}
75 int s;
76 QWidget * w;
77 bool p;
78 };
79
80 QList<SBItem *> items;
81 QString tempItem;
82
83 QBoxLayout * box;
84 QTimer * timer;
85
86#if QT_CONFIG(sizegrip)
87 QSizeGrip * resizer;
88 bool showSizeGrip;
89#endif
90
91 int savedStrut;
92
93 int indexToLastNonPermanentWidget() const
94 {
95 int i = items.size() - 1;
96 for (; i >= 0; --i) {
97 SBItem *item = items.at(i);
98 if (!(item && item->p))
99 break;
100 }
101 return i;
102 }
103
104#if QT_CONFIG(sizegrip)
105 void tryToShowSizeGrip()
106 {
107 if (!showSizeGrip)
108 return;
109 showSizeGrip = false;
110 if (!resizer || resizer->isVisible())
111 return;
112 resizer->setAttribute(Qt::WA_WState_ExplicitShowHide, on: false);
113 QMetaObject::invokeMethod(obj: resizer, member: "_q_showIfNotHidden", type: Qt::DirectConnection);
114 resizer->setAttribute(Qt::WA_WState_ExplicitShowHide, on: false);
115 }
116#endif
117
118 QRect messageRect() const;
119};
120
121
122QRect QStatusBarPrivate::messageRect() const
123{
124 Q_Q(const QStatusBar);
125 bool rtl = q->layoutDirection() == Qt::RightToLeft;
126
127 int left = 6;
128 int right = q->width() - 12;
129
130#if QT_CONFIG(sizegrip)
131 if (resizer && resizer->isVisible()) {
132 if (rtl)
133 left = resizer->x() + resizer->width();
134 else
135 right = resizer->x();
136 }
137#endif
138
139 for (int i=0; i<items.size(); ++i) {
140 QStatusBarPrivate::SBItem* item = items.at(i);
141 if (!item)
142 break;
143 if (item->p && item->w->isVisible()) {
144 if (item->p) {
145 if (rtl)
146 left = qMax(a: left, b: item->w->x() + item->w->width() + 2);
147 else
148 right = qMin(a: right, b: item->w->x() - 2);
149 }
150 break;
151 }
152 }
153 return QRect(left, 0, right-left, q->height());
154}
155
156
157/*!
158 \class QStatusBar
159 \brief The QStatusBar class provides a horizontal bar suitable for
160 presenting status information.
161
162 \ingroup mainwindow-classes
163 \ingroup helpsystem
164 \inmodule QtWidgets
165
166 Each status indicator falls into one of three categories:
167
168 \list
169 \li \e Temporary - briefly occupies most of the status bar. Used
170 to explain tool tip texts or menu entries, for example.
171 \li \e Normal - occupies part of the status bar and may be hidden
172 by temporary messages. Used to display the page and line
173 number in a word processor, for example.
174 \li \e Permanent - is never hidden. Used for important mode
175 indications, for example, some applications put a Caps Lock
176 indicator in the status bar.
177 \endlist
178
179 QStatusBar lets you display all three types of indicators.
180
181 Typically, a request for the status bar functionality occurs in
182 relation to a QMainWindow object. QMainWindow provides a main
183 application window, with a menu bar, tool bars, dock widgets \e
184 and a status bar around a large central widget. The status bar can
185 be retrieved using the QMainWindow::statusBar() function, and
186 replaced using the QMainWindow::setStatusBar() function.
187
188 Use the showMessage() slot to display a \e temporary message:
189
190 \snippet mainwindows/dockwidgets/mainwindow.cpp 8
191
192 To remove a temporary message, use the clearMessage() slot, or set
193 a time limit when calling showMessage(). For example:
194
195 \snippet mainwindows/dockwidgets/mainwindow.cpp 3
196
197 Use the currentMessage() function to retrieve the temporary
198 message currently shown. The QStatusBar class also provide the
199 messageChanged() signal which is emitted whenever the temporary
200 status message changes.
201
202 \target permanent message
203 \e Normal and \e Permanent messages are displayed by creating a
204 small widget (QLabel, QProgressBar or even QToolButton) and then
205 adding it to the status bar using the addWidget() or the
206 addPermanentWidget() function. Use the removeWidget() function to
207 remove such messages from the status bar.
208
209 \snippet code/src_gui_widgets_qstatusbar.cpp 0
210
211 By default QStatusBar provides a QSizeGrip in the lower-right
212 corner. You can disable it using the setSizeGripEnabled()
213 function. Use the isSizeGripEnabled() function to determine the
214 current status of the size grip.
215
216 \image fusion-statusbar-sizegrip.png A status bar shown in the Fusion widget style
217
218 \sa QMainWindow, QStatusTipEvent, {fowler}{GUI Design Handbook:
219 Status Bar}, {Application Example}
220*/
221
222
223/*!
224 Constructs a status bar with a size grip and the given \a parent.
225
226 \sa setSizeGripEnabled()
227*/
228QStatusBar::QStatusBar(QWidget * parent)
229 : QWidget(*new QStatusBarPrivate, parent, { })
230{
231 Q_D(QStatusBar);
232 d->box = nullptr;
233 d->timer = nullptr;
234
235#if QT_CONFIG(sizegrip)
236 d->resizer = nullptr;
237 setSizeGripEnabled(true); // causes reformat()
238#else
239 reformat();
240#endif
241}
242
243/*!
244 Destroys this status bar and frees any allocated resources and
245 child widgets.
246*/
247QStatusBar::~QStatusBar()
248{
249 Q_D(QStatusBar);
250 while (!d->items.isEmpty())
251 delete d->items.takeFirst();
252}
253
254
255/*!
256 Adds the given \a widget to this status bar, reparenting the
257 widget if it isn't already a child of this QStatusBar object. The
258 \a stretch parameter is used to compute a suitable size for the
259 given \a widget as the status bar grows and shrinks. The default
260 stretch factor is 0, i.e giving the widget a minimum of space.
261
262 The widget is located to the far left of the first permanent
263 widget (see addPermanentWidget()) and may be obscured by temporary
264 messages.
265
266 \sa insertWidget(), removeWidget(), addPermanentWidget()
267*/
268
269void QStatusBar::addWidget(QWidget * widget, int stretch)
270{
271 if (!widget)
272 return;
273 insertWidget(index: d_func()->indexToLastNonPermanentWidget() + 1, widget, stretch);
274}
275
276/*!
277 \since 4.2
278
279 Inserts the given \a widget at the given \a index to this status bar,
280 reparenting the widget if it isn't already a child of this
281 QStatusBar object. If \a index is out of range, the widget is appended
282 (in which case it is the actual index of the widget that is returned).
283
284 The \a stretch parameter is used to compute a suitable size for
285 the given \a widget as the status bar grows and shrinks. The
286 default stretch factor is 0, i.e giving the widget a minimum of
287 space.
288
289 The widget is located to the far left of the first permanent
290 widget (see addPermanentWidget()) and may be obscured by temporary
291 messages.
292
293 \sa addWidget(), removeWidget(), addPermanentWidget()
294*/
295int QStatusBar::insertWidget(int index, QWidget *widget, int stretch)
296{
297 if (!widget)
298 return -1;
299
300 Q_D(QStatusBar);
301 QStatusBarPrivate::SBItem* item = new QStatusBarPrivate::SBItem(widget, stretch, false);
302
303 int idx = d->indexToLastNonPermanentWidget();
304 if (Q_UNLIKELY(index < 0 || index > d->items.size() || (idx >= 0 && index > idx + 1))) {
305 qWarning(msg: "QStatusBar::insertWidget: Index out of range (%d), appending widget", index);
306 index = idx + 1;
307 }
308 d->items.insert(i: index, t: item);
309
310 if (!d->tempItem.isEmpty())
311 widget->hide();
312
313 reformat();
314 if (!widget->isHidden() || !widget->testAttribute(attribute: Qt::WA_WState_ExplicitShowHide))
315 widget->show();
316
317 return index;
318}
319
320/*!
321 Adds the given \a widget permanently to this status bar,
322 reparenting the widget if it isn't already a child of this
323 QStatusBar object. The \a stretch parameter is used to compute a
324 suitable size for the given \a widget as the status bar grows and
325 shrinks. The default stretch factor is 0, i.e giving the widget a
326 minimum of space.
327
328 Permanently means that the widget may not be obscured by temporary
329 messages. It is is located at the far right of the status bar.
330
331 \sa insertPermanentWidget(), removeWidget(), addWidget()
332*/
333
334void QStatusBar::addPermanentWidget(QWidget * widget, int stretch)
335{
336 if (!widget)
337 return;
338 insertPermanentWidget(index: d_func()->items.size(), widget, stretch);
339}
340
341
342/*!
343 \since 4.2
344
345 Inserts the given \a widget at the given \a index permanently to this status bar,
346 reparenting the widget if it isn't already a child of this
347 QStatusBar object. If \a index is out of range, the widget is appended
348 (in which case it is the actual index of the widget that is returned).
349
350 The \a stretch parameter is used to compute a
351 suitable size for the given \a widget as the status bar grows and
352 shrinks. The default stretch factor is 0, i.e giving the widget a
353 minimum of space.
354
355 Permanently means that the widget may not be obscured by temporary
356 messages. It is is located at the far right of the status bar.
357
358 \sa addPermanentWidget(), removeWidget(), addWidget()
359*/
360int QStatusBar::insertPermanentWidget(int index, QWidget *widget, int stretch)
361{
362 if (!widget)
363 return -1;
364
365 Q_D(QStatusBar);
366 QStatusBarPrivate::SBItem* item = new QStatusBarPrivate::SBItem(widget, stretch, true);
367
368 int idx = d->indexToLastNonPermanentWidget();
369 if (Q_UNLIKELY(index < 0 || index > d->items.size() || (idx >= 0 && index <= idx))) {
370 qWarning(msg: "QStatusBar::insertPermanentWidget: Index out of range (%d), appending widget", index);
371 index = d->items.size();
372 }
373 d->items.insert(i: index, t: item);
374
375 reformat();
376 if (!widget->isHidden() || !widget->testAttribute(attribute: Qt::WA_WState_ExplicitShowHide))
377 widget->show();
378
379 return index;
380}
381
382/*!
383 Removes the specified \a widget from the status bar.
384
385 \note This function does not delete the widget but \e hides it.
386 To add the widget again, you must call both the addWidget() and
387 show() functions.
388
389 \sa addWidget(), addPermanentWidget(), clearMessage()
390*/
391
392void QStatusBar::removeWidget(QWidget *widget)
393{
394 if (!widget)
395 return;
396
397 Q_D(QStatusBar);
398 bool found = false;
399 QStatusBarPrivate::SBItem* item;
400 for (int i=0; i<d->items.size(); ++i) {
401 item = d->items.at(i);
402 if (!item)
403 break;
404 if (item->w == widget) {
405 d->items.removeAt(i);
406 item->w->hide();
407 delete item;
408 found = true;
409 break;
410 }
411 }
412
413 if (found)
414 reformat();
415#if defined(QT_DEBUG)
416 else
417 qDebug(msg: "QStatusBar::removeWidget(): Widget not found.");
418#endif
419}
420
421/*!
422 \property QStatusBar::sizeGripEnabled
423
424 \brief whether the QSizeGrip in the bottom-right corner of the
425 status bar is enabled
426
427 The size grip is enabled by default.
428*/
429
430bool QStatusBar::isSizeGripEnabled() const
431{
432#if !QT_CONFIG(sizegrip)
433 return false;
434#else
435 Q_D(const QStatusBar);
436 return !!d->resizer;
437#endif
438}
439
440void QStatusBar::setSizeGripEnabled(bool enabled)
441{
442#if !QT_CONFIG(sizegrip)
443 Q_UNUSED(enabled);
444#else
445 Q_D(QStatusBar);
446 if (!enabled != !d->resizer) {
447 if (enabled) {
448 d->resizer = new QSizeGrip(this);
449 d->resizer->hide();
450 d->resizer->installEventFilter(filterObj: this);
451 d->showSizeGrip = true;
452 } else {
453 delete d->resizer;
454 d->resizer = nullptr;
455 d->showSizeGrip = false;
456 }
457 reformat();
458 if (d->resizer && isVisible())
459 d->tryToShowSizeGrip();
460 }
461#endif
462}
463
464
465/*!
466 Changes the status bar's appearance to account for item changes.
467
468 Special subclasses may need this function, but geometry management
469 will usually take care of any necessary rearrangements.
470*/
471void QStatusBar::reformat()
472{
473 Q_D(QStatusBar);
474 if (d->box)
475 delete d->box;
476
477 QBoxLayout *vbox;
478#if QT_CONFIG(sizegrip)
479 if (d->resizer) {
480 d->box = new QHBoxLayout(this);
481 d->box->setContentsMargins(QMargins());
482 vbox = new QVBoxLayout;
483 d->box->addLayout(layout: vbox);
484 } else
485#endif
486 {
487 vbox = d->box = new QVBoxLayout(this);
488 d->box->setContentsMargins(QMargins());
489 }
490 vbox->addSpacing(size: 3);
491 QBoxLayout* l = new QHBoxLayout;
492 vbox->addLayout(layout: l);
493 l->addSpacing(size: 2);
494 l->setSpacing(6);
495
496 int maxH = fontMetrics().height();
497
498 int i;
499 QStatusBarPrivate::SBItem* item;
500 for (i=0,item=nullptr; i<d->items.size(); ++i) {
501 item = d->items.at(i);
502 if (!item || item->p)
503 break;
504 l->addWidget(item->w, stretch: item->s);
505 int itemH = qMin(a: qSmartMinSize(w: item->w).height(), b: item->w->maximumHeight());
506 maxH = qMax(a: maxH, b: itemH);
507 }
508
509 l->addStretch(stretch: 0);
510
511 for (item=nullptr; i<d->items.size(); ++i) {
512 item = d->items.at(i);
513 if (!item)
514 break;
515 l->addWidget(item->w, stretch: item->s);
516 int itemH = qMin(a: qSmartMinSize(w: item->w).height(), b: item->w->maximumHeight());
517 maxH = qMax(a: maxH, b: itemH);
518 }
519#if QT_CONFIG(sizegrip)
520 if (d->resizer) {
521 maxH = qMax(a: maxH, b: d->resizer->sizeHint().height());
522 d->box->addSpacing(size: 1);
523 d->box->addWidget(d->resizer, stretch: 0, alignment: Qt::AlignBottom);
524 }
525#endif
526 l->addStrut(maxH);
527 d->savedStrut = maxH;
528 vbox->addSpacing(size: 2);
529 d->box->activate();
530 update();
531}
532
533/*!
534
535 Hides the normal status indications and displays the given \a
536 message for the specified number of milli-seconds (\a{timeout}). If
537 \a{timeout} is 0 (default), the \a {message} remains displayed until
538 the clearMessage() slot is called or until the showMessage() slot is
539 called again to change the message.
540
541 Note that showMessage() is called to show temporary explanations of
542 tool tip texts, so passing a \a{timeout} of 0 is not sufficient to
543 display a \l{permanent message}{permanent message}.
544
545 \sa messageChanged(), currentMessage(), clearMessage()
546*/
547void QStatusBar::showMessage(const QString &message, int timeout)
548{
549 Q_D(QStatusBar);
550
551 if (timeout > 0) {
552 if (!d->timer) {
553 d->timer = new QTimer(this);
554 connect(sender: d->timer, SIGNAL(timeout()), receiver: this, SLOT(clearMessage()));
555 }
556 d->timer->start(msec: timeout);
557 } else if (d->timer) {
558 delete d->timer;
559 d->timer = nullptr;
560 }
561 if (d->tempItem == message)
562 return;
563 d->tempItem = message;
564
565 hideOrShow();
566}
567
568/*!
569 Removes any temporary message being shown.
570
571 \sa currentMessage(), showMessage(), removeWidget()
572*/
573
574void QStatusBar::clearMessage()
575{
576 Q_D(QStatusBar);
577 if (d->tempItem.isEmpty())
578 return;
579 if (d->timer) {
580 qDeleteInEventHandler(o: d->timer);
581 d->timer = nullptr;
582 }
583 d->tempItem.clear();
584 hideOrShow();
585}
586
587/*!
588 Returns the temporary message currently shown,
589 or an empty string if there is no such message.
590
591 \sa showMessage()
592*/
593QString QStatusBar::currentMessage() const
594{
595 Q_D(const QStatusBar);
596 return d->tempItem;
597}
598
599/*!
600 \fn void QStatusBar::messageChanged(const QString &message)
601
602 This signal is emitted whenever the temporary status message
603 changes. The new temporary message is passed in the \a message
604 parameter which is a null-string when the message has been
605 removed.
606
607 \sa showMessage(), clearMessage()
608*/
609
610/*!
611 Ensures that the right widgets are visible.
612
613 Used by the showMessage() and clearMessage() functions.
614*/
615void QStatusBar::hideOrShow()
616{
617 Q_D(QStatusBar);
618 bool haveMessage = !d->tempItem.isEmpty();
619
620 QStatusBarPrivate::SBItem* item = nullptr;
621 for (int i=0; i<d->items.size(); ++i) {
622 item = d->items.at(i);
623 if (!item || item->p)
624 break;
625 if (haveMessage && item->w->isVisible()) {
626 item->w->hide();
627 item->w->setAttribute(Qt::WA_WState_ExplicitShowHide, on: false);
628 } else if (!haveMessage && !item->w->testAttribute(attribute: Qt::WA_WState_ExplicitShowHide)) {
629 item->w->show();
630 }
631 }
632
633 emit messageChanged(text: d->tempItem);
634
635#ifndef QT_NO_ACCESSIBILITY
636 if (QAccessible::isActive()) {
637 QAccessibleEvent event(this, QAccessible::NameChanged);
638 QAccessible::updateAccessibility(event: &event);
639 }
640#endif
641
642 update(d->messageRect());
643}
644
645/*!
646 \reimp
647 */
648void QStatusBar::showEvent(QShowEvent *)
649{
650#if QT_CONFIG(sizegrip)
651 Q_D(QStatusBar);
652 if (d->resizer && d->showSizeGrip)
653 d->tryToShowSizeGrip();
654#endif
655}
656
657/*!
658 \reimp
659 \fn void QStatusBar::paintEvent(QPaintEvent *event)
660
661 Shows the temporary message, if appropriate, in response to the
662 paint \a event.
663*/
664void QStatusBar::paintEvent(QPaintEvent *event)
665{
666 Q_D(QStatusBar);
667 bool haveMessage = !d->tempItem.isEmpty();
668
669 QPainter p(this);
670 QStyleOption opt;
671 opt.initFrom(w: this);
672 style()->drawPrimitive(pe: QStyle::PE_PanelStatusBar, opt: &opt, p: &p, w: this);
673
674 for (int i=0; i<d->items.size(); ++i) {
675 QStatusBarPrivate::SBItem* item = d->items.at(i);
676 if (item && item->w->isVisible() && (!haveMessage || item->p)) {
677 QRect ir = item->w->geometry().adjusted(xp1: -2, yp1: -1, xp2: 2, yp2: 1);
678 if (event->rect().intersects(r: ir)) {
679 QStyleOption opt(0);
680 opt.rect = ir;
681 opt.palette = palette();
682 opt.state = QStyle::State_None;
683 style()->drawPrimitive(pe: QStyle::PE_FrameStatusBarItem, opt: &opt, p: &p, w: item->w);
684 }
685 }
686 }
687 if (haveMessage) {
688 p.setPen(palette().windowText().color());
689 p.drawText(r: d->messageRect(), flags: Qt::AlignLeading | Qt::AlignVCenter | Qt::TextSingleLine, text: d->tempItem);
690 }
691}
692
693/*!
694 \reimp
695*/
696void QStatusBar::resizeEvent(QResizeEvent * e)
697{
698 QWidget::resizeEvent(event: e);
699}
700
701/*!
702 \reimp
703*/
704
705bool QStatusBar::event(QEvent *e)
706{
707 Q_D(QStatusBar);
708
709 if (e->type() == QEvent::LayoutRequest
710 ) {
711 // Calculate new strut height and call reformat() if it has changed
712 int maxH = fontMetrics().height();
713
714 QStatusBarPrivate::SBItem* item = nullptr;
715 for (int i=0; i<d->items.size(); ++i) {
716 item = d->items.at(i);
717 if (!item)
718 break;
719 int itemH = qMin(a: qSmartMinSize(w: item->w).height(), b: item->w->maximumHeight());
720 maxH = qMax(a: maxH, b: itemH);
721 }
722
723#if QT_CONFIG(sizegrip)
724 if (d->resizer)
725 maxH = qMax(a: maxH, b: d->resizer->sizeHint().height());
726#endif
727
728 if (maxH != d->savedStrut)
729 reformat();
730 else
731 update();
732 }
733 if (e->type() == QEvent::ChildRemoved) {
734 QStatusBarPrivate::SBItem* item = nullptr;
735 for (int i=0; i<d->items.size(); ++i) {
736 item = d->items.at(i);
737 if (!item)
738 break;
739 if (item->w == ((QChildEvent*)e)->child()) {
740 d->items.removeAt(i);
741 delete item;
742 }
743 }
744 }
745
746 return QWidget::event(event: e);
747}
748
749QT_END_NAMESPACE
750
751#include "moc_qstatusbar.cpp"
752

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