Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtoolbox.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qtoolbox.h"
6
7#include <qapplication.h>
8#include <qeventloop.h>
9#include <qlayout.h>
10#include <qlist.h>
11#include <qpainter.h>
12#include <qscrollarea.h>
13#include <qstyle.h>
14#include <qstyleoption.h>
15#if QT_CONFIG(tooltip)
16#include <qtooltip.h>
17#endif
18#include <qabstractbutton.h>
19
20#include "qframe_p.h"
21
23
24using namespace Qt::StringLiterals;
25
27{
29public:
37
38 inline void setSelected(bool b) { selected = b; update(); }
39 inline void setIndex(int newIndex) { indexInPage = newIndex; }
40
43
44protected:
45 void initStyleOption(QStyleOptionToolBox *opt) const;
46 void paintEvent(QPaintEvent *) override;
47
48private:
49 bool selected;
50 int indexInPage;
51};
52
53
55{
56 Q_DECLARE_PUBLIC(QToolBox)
57public:
58 struct Page
59 {
63
64 inline void setText(const QString &text) { button->setText(text); }
65 inline void setIcon(const QIcon &is) { button->setIcon(is); }
66#if QT_CONFIG(tooltip)
67 inline void setToolTip(const QString &tip) { button->setToolTip(tip); }
68 inline QString toolTip() const { return button->toolTip(); }
69#endif
70 inline QString text() const { return button->text(); }
71 inline QIcon icon() const { return button->icon(); }
72
73 inline bool operator==(const Page& other) const
74 {
75 return widget == other.widget;
76 }
77 };
79
81 : currentPage(nullptr)
82 {
83 }
85 void _q_widgetDestroyed(QObject*);
86
87 const Page *page(const QObject *widget) const;
88 const Page *page(int index) const;
89 Page *page(int index);
90
91 void updateTabs();
92 void relayout();
93
97};
98
99const QToolBoxPrivate::Page *QToolBoxPrivate::page(const QObject *widget) const
100{
101 if (!widget)
102 return nullptr;
103
104 for (const auto &page : pageList) {
105 if (page->widget == widget)
106 return page.get();
107 }
108 return nullptr;
109}
110
112{
113 if (index >= 0 && index < static_cast<int>(pageList.size()))
114 return pageList[index].get();
115 return nullptr;
116}
117
118const QToolBoxPrivate::Page *QToolBoxPrivate::page(int index) const
119{
120 if (index >= 0 && index < static_cast<int>(pageList.size()))
121 return pageList[index].get();
122 return nullptr;
123}
124
126{
127 QToolBoxButton *lastButton = currentPage ? currentPage->button : nullptr;
128 bool after = false;
129 int index = 0;
130 for (const auto &page : pageList) {
131 QToolBoxButton *tB = page->button;
132 // update indexes, since the updates are delayed, the indexes will be correct
133 // when we actually paint.
134 tB->setIndex(index);
135 QWidget *tW = page->widget;
136 if (after) {
137 QPalette p = tB->palette();
138 p.setColor(tB->backgroundRole(), tW->palette().color(tW->backgroundRole()));
139 tB->setPalette(p);
140 tB->update();
141 } else if (tB->backgroundRole() != QPalette::Window) {
142 tB->setBackgroundRole(QPalette::Window);
143 tB->update();
144 }
145 after = tB == lastButton;
146 ++index;
147 }
148}
149
150QSize QToolBoxButton::sizeHint() const
151{
152 QSize iconSize(8, 8);
153 if (!icon().isNull()) {
154 int icone = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, parentWidget() /* QToolBox */);
155 iconSize += QSize(icone + 2, icone);
156 }
157 QSize textSize = fontMetrics().size(Qt::TextShowMnemonic, text()) + QSize(0, 8);
158
159 return QSize(iconSize.width() + textSize.width(), qMax(iconSize.height(), textSize.height()));
160}
161
163{
164 if (icon().isNull())
165 return QSize();
166 int icone = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, parentWidget() /* QToolBox */);
167 return QSize(icone + 8, icone + 8);
168}
169
170void QToolBoxButton::initStyleOption(QStyleOptionToolBox *option) const
171{
172 if (!option)
173 return;
174 option->initFrom(this);
175 if (selected)
176 option->state |= QStyle::State_Selected;
177 if (isDown())
178 option->state |= QStyle::State_Sunken;
179 option->text = text();
180 option->icon = icon();
181
182 QToolBox *toolBox = static_cast<QToolBox *>(parentWidget()); // I know I'm in a tool box.
183 const int widgetCount = toolBox->count();
184 const int currIndex = toolBox->currentIndex();
185 if (widgetCount == 1) {
186 option->position = QStyleOptionToolBox::OnlyOneTab;
187 } else if (indexInPage == 0) {
188 option->position = QStyleOptionToolBox::Beginning;
189 } else if (indexInPage == widgetCount - 1) {
190 option->position = QStyleOptionToolBox::End;
191 } else {
192 option->position = QStyleOptionToolBox::Middle;
193 }
194 if (currIndex == indexInPage - 1) {
195 option->selectedPosition = QStyleOptionToolBox::PreviousIsSelected;
196 } else if (currIndex == indexInPage + 1) {
197 option->selectedPosition = QStyleOptionToolBox::NextIsSelected;
198 } else {
199 option->selectedPosition = QStyleOptionToolBox::NotAdjacent;
200 }
201}
202
203void QToolBoxButton::paintEvent(QPaintEvent *)
204{
205 QPainter paint(this);
206 QPainter *p = &paint;
208 initStyleOption(&opt);
209 style()->drawControl(QStyle::CE_ToolBoxTab, &opt, p, parentWidget());
210}
211
212/*!
213 \class QToolBox
214
215 \brief The QToolBox class provides a column of tabbed widget items.
216
217
218 \ingroup basicwidgets
219 \inmodule QtWidgets
220
221 A toolbox is a widget that displays a column of tabs one above the
222 other, with the current item displayed below the current tab.
223 Every tab has an index position within the column of tabs. A tab's
224 item is a QWidget.
225
226 Each item has an itemText(), an optional itemIcon(), an optional
227 itemToolTip(), and a widget(). The item's attributes can be
228 changed with setItemText(), setItemIcon(), and
229 setItemToolTip(). Each item can be enabled or disabled
230 individually with setItemEnabled().
231
232 Items are added using addItem(), or inserted at particular
233 positions using insertItem(). The total number of items is given
234 by count(). Items can be deleted with delete, or removed from the
235 toolbox with removeItem(). Combining removeItem() and insertItem()
236 allows you to move items to different positions.
237
238 The index of the current item widget is returned by currentIndex(),
239 and set with setCurrentIndex(). The index of a particular item can
240 be found using indexOf(), and the item at a given index is returned
241 by item().
242
243 The currentChanged() signal is emitted when the current item is
244 changed.
245
246 \sa QTabWidget
247*/
248
249/*!
250 \fn void QToolBox::currentChanged(int index)
251
252 This signal is emitted when the current item is changed. The new
253 current item's index is passed in \a index, or -1 if there is no
254 current item.
255*/
256
257
258/*!
259 Constructs a new toolbox with the given \a parent and the flags, \a f.
260*/
261QToolBox::QToolBox(QWidget *parent, Qt::WindowFlags f)
262 : QFrame(*new QToolBoxPrivate, parent, f)
263{
264 Q_D(QToolBox);
265 d->layout = new QVBoxLayout(this);
266 d->layout->setContentsMargins(QMargins());
267 setBackgroundRole(QPalette::Button);
268}
269
270/*!
271 Destroys the toolbox.
272*/
273
274QToolBox::~QToolBox()
275{
276}
277
278/*!
279 \fn int QToolBox::addItem(QWidget *w, const QString &text)
280 \overload
281
282 Adds the widget \a w in a new tab at bottom of the toolbox. The
283 new tab's text is set to \a text. Returns the new tab's index.
284*/
285
286/*!
287 \fn int QToolBox::addItem(QWidget *widget, const QIcon &iconSet,const QString &text)
288 Adds the \a widget in a new tab at bottom of the toolbox. The
289 new tab's text is set to \a text, and the \a iconSet is
290 displayed to the left of the \a text. Returns the new tab's index.
291*/
292
293/*!
294 \fn int QToolBox::insertItem(int index, QWidget *widget, const QString &text)
295 \overload
296
297 Inserts the \a widget at position \a index, or at the bottom
298 of the toolbox if \a index is out of range. The new item's text is
299 set to \a text. Returns the new item's index.
300*/
301
302/*!
303 Inserts the \a widget at position \a index, or at the bottom
304 of the toolbox if \a index is out of range. The new item's text
305 is set to \a text, and the \a icon is displayed to the left of
306 the \a text. Returns the new item's index.
307*/
308
309int QToolBox::insertItem(int index, QWidget *widget, const QIcon &icon, const QString &text)
310{
311 if (!widget)
312 return -1;
313
314 Q_D(QToolBox);
315 connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(_q_widgetDestroyed(QObject*)));
316
317 auto newPage = std::make_unique<QToolBoxPrivate::Page>();
318 auto &c = *newPage;
319 c.widget = widget;
320 c.button = new QToolBoxButton(this);
321 c.button->setObjectName("qt_toolbox_toolboxbutton"_L1);
322 connect(c.button, SIGNAL(clicked()), this, SLOT(_q_buttonClicked()));
323
324 c.sv = new QScrollArea(this);
325 c.sv->setWidget(widget);
326 c.sv->setWidgetResizable(true);
327 c.sv->hide();
328 c.sv->setFrameStyle(QFrame::NoFrame);
329
330 c.setText(text);
331 c.setIcon(icon);
332
333 if (index < 0 || index >= static_cast<int>(d->pageList.size())) {
334 index = static_cast<int>(d->pageList.size());
335 d->pageList.push_back(std::move(newPage));
336 d->layout->addWidget(c.button);
337 d->layout->addWidget(c.sv);
338 if (index == 0)
339 setCurrentIndex(index);
340 } else {
341 d->pageList.insert(d->pageList.cbegin() + index, std::move(newPage));
342 d->relayout();
343 if (d->currentPage) {
344 QWidget *current = d->currentPage->widget;
345 int oldindex = indexOf(current);
346 if (index <= oldindex) {
347 d->currentPage = nullptr; // trigger change
348 setCurrentIndex(oldindex);
349 }
350 }
351 }
352
353 c.button->show();
354
355 d->updateTabs();
356 itemInserted(index);
357 return index;
358}
359
361{
362 Q_Q(QToolBox);
363 QToolBoxButton *tb = qobject_cast<QToolBoxButton*>(q->sender());
364 QWidget* item = nullptr;
365 for (const auto &page : pageList) {
366 if (page->button == tb) {
367 item = page->widget;
368 break;
369 }
370 }
371
372 q->setCurrentIndex(q->indexOf(item));
373}
374
375/*!
376 \property QToolBox::count
377 \brief The number of items contained in the toolbox.
378
379 By default, this property has a value of 0.
380*/
381
382int QToolBox::count() const
383{
384 Q_D(const QToolBox);
385 return static_cast<int>(d->pageList.size());
386}
387
388void QToolBox::setCurrentIndex(int index)
389{
390 Q_D(QToolBox);
391 QToolBoxPrivate::Page *c = d->page(index);
392 if (!c || d->currentPage == c)
393 return;
394
395 c->button->setSelected(true);
396 if (d->currentPage) {
397 d->currentPage->sv->hide();
398 d->currentPage->button->setSelected(false);
399 }
400 d->currentPage = c;
401 d->currentPage->sv->show();
402 d->updateTabs();
403 emit currentChanged(index);
404}
405
407{
408 Q_Q(QToolBox);
409 delete layout;
410 layout = new QVBoxLayout(q);
411 layout->setContentsMargins(QMargins());
412 for (const auto &page : pageList) {
413 layout->addWidget(page->button);
414 layout->addWidget(page->sv);
415 }
416}
417
418auto pageEquals = [](const QToolBoxPrivate::Page *page) {
419 return [page](const std::unique_ptr<QToolBoxPrivate::Page> &ptr) {
420 return ptr.get() == page;
421 };
422};
423
425{
426 Q_Q(QToolBox);
427
428 const QToolBoxPrivate::Page * const c = page(object);
429 if (!c)
430 return;
431
432 layout->removeWidget(c->sv);
433 layout->removeWidget(c->button);
434 c->sv->deleteLater(); // page might still be a child of sv
435 delete c->button;
436
437 bool removeCurrent = c == currentPage;
438 pageList.erase(std::remove_if(pageList.begin(), pageList.end(), pageEquals(c)), pageList.end());
439
440 if (pageList.empty()) {
441 currentPage = nullptr;
442 emit q->currentChanged(-1);
443 } else if (removeCurrent) {
444 currentPage = nullptr;
445 q->setCurrentIndex(0);
446 }
447}
448
449/*!
450 Removes the item at position \a index from the toolbox. Note that
451 the widget is \e not deleted.
452*/
453
454void QToolBox::removeItem(int index)
455{
456 Q_D(QToolBox);
457 if (QWidget *w = widget(index)) {
458 disconnect(w, SIGNAL(destroyed(QObject*)), this, SLOT(_q_widgetDestroyed(QObject*)));
459 w->setParent(this);
460 // destroy internal data
461 d->_q_widgetDestroyed(w);
462 itemRemoved(index);
463 }
464}
465
466
467/*!
468 \property QToolBox::currentIndex
469 \brief the index of the current item
470
471 By default, for an empty toolbox, this property has a value of -1.
472
473 \sa indexOf(), widget()
474*/
475
476
477int QToolBox::currentIndex() const
478{
479 Q_D(const QToolBox);
480 return d->currentPage ? indexOf(d->currentPage->widget) : -1;
481}
482
483/*!
484 Returns a pointer to the current widget, or \nullptr if there is
485 no such item.
486
487 \sa currentIndex(), setCurrentWidget()
488*/
489
490QWidget * QToolBox::currentWidget() const
491{
492 Q_D(const QToolBox);
493 return d->currentPage ? d->currentPage->widget : nullptr;
494}
495
496/*!
497 Makes\a widget the current widget. The \a widget must be an item in this tool box.
498
499 \sa addItem(), setCurrentIndex(), currentWidget()
500 */
501void QToolBox::setCurrentWidget(QWidget *widget)
502{
503 int i = indexOf(widget);
504 if (Q_UNLIKELY(i < 0))
505 qWarning("QToolBox::setCurrentWidget: widget not contained in tool box");
506 else
507 setCurrentIndex(i);
508}
509
510/*!
511 Returns the widget at position \a index, or \nullptr if there is
512 no such item.
513*/
514
515QWidget *QToolBox::widget(int index) const
516{
517 Q_D(const QToolBox);
518 if (index < 0 || index >= static_cast<int>(d->pageList.size()))
519 return nullptr;
520 return d->pageList[index]->widget;
521}
522
523/*!
524 Returns the index of \a widget, or -1 if the item does not
525 exist.
526*/
527
528int QToolBox::indexOf(const QWidget *widget) const
529{
530 Q_D(const QToolBox);
531 const QToolBoxPrivate::Page *c = (widget ? d->page(widget) : nullptr);
532 if (!c)
533 return -1;
534 const auto it = std::find_if(d->pageList.cbegin(), d->pageList.cend(), pageEquals(c));
535 if (it == d->pageList.cend())
536 return -1;
537 return static_cast<int>(it - d->pageList.cbegin());
538}
539
540/*!
541 If \a enabled is true then the item at position \a index is enabled; otherwise
542 the item at position \a index is disabled.
543*/
544
545void QToolBox::setItemEnabled(int index, bool enabled)
546{
547 Q_D(QToolBox);
548 QToolBoxPrivate::Page *c = d->page(index);
549 if (!c)
550 return;
551
552 c->button->setEnabled(enabled);
553 if (!enabled && c == d->currentPage) {
554 int curIndexUp = index;
555 int curIndexDown = curIndexUp;
556 const int count = static_cast<int>(d->pageList.size());
557 while (curIndexUp > 0 || curIndexDown < count-1) {
558 if (curIndexDown < count-1) {
559 if (d->page(++curIndexDown)->button->isEnabled()) {
560 index = curIndexDown;
561 break;
562 }
563 }
564 if (curIndexUp > 0) {
565 if (d->page(--curIndexUp)->button->isEnabled()) {
566 index = curIndexUp;
567 break;
568 }
569 }
570 }
571 setCurrentIndex(index);
572 }
573}
574
575
576/*!
577 Sets the text of the item at position \a index to \a text.
578
579 If the provided text contains an ampersand character ('&'), a
580 mnemonic is automatically created for it. The character that
581 follows the '&' will be used as the shortcut key. Any previous
582 mnemonic will be overwritten, or cleared if no mnemonic is defined
583 by the text. See the \l {QShortcut#mnemonic}{QShortcut}
584 documentation for details (to display an actual ampersand, use
585 '&&').
586*/
587
588void QToolBox::setItemText(int index, const QString &text)
589{
590 Q_D(QToolBox);
591 QToolBoxPrivate::Page *c = d->page(index);
592 if (c)
593 c->setText(text);
594}
595
596/*!
597 Sets the icon of the item at position \a index to \a icon.
598*/
599
600void QToolBox::setItemIcon(int index, const QIcon &icon)
601{
602 Q_D(QToolBox);
603 QToolBoxPrivate::Page *c = d->page(index);
604 if (c)
605 c->setIcon(icon);
606}
607
608#if QT_CONFIG(tooltip)
609/*!
610 Sets the tooltip of the item at position \a index to \a toolTip.
611*/
612
613void QToolBox::setItemToolTip(int index, const QString &toolTip)
614{
615 Q_D(QToolBox);
616 QToolBoxPrivate::Page *c = d->page(index);
617 if (c)
618 c->setToolTip(toolTip);
619}
620#endif // QT_CONFIG(tooltip)
621
622/*!
623 Returns \c true if the item at position \a index is enabled; otherwise returns \c false.
624*/
625
626bool QToolBox::isItemEnabled(int index) const
627{
628 Q_D(const QToolBox);
629 const QToolBoxPrivate::Page *c = d->page(index);
630 return c && c->button->isEnabled();
631}
632
633/*!
634 Returns the text of the item at position \a index, or an empty string if
635 \a index is out of range.
636*/
637
638QString QToolBox::itemText(int index) const
639{
640 Q_D(const QToolBox);
641 const QToolBoxPrivate::Page *c = d->page(index);
642 return (c ? c->text() : QString());
643}
644
645/*!
646 Returns the icon of the item at position \a index, or a null
647 icon if \a index is out of range.
648*/
649
650QIcon QToolBox::itemIcon(int index) const
651{
652 Q_D(const QToolBox);
653 const QToolBoxPrivate::Page *c = d->page(index);
654 return (c ? c->icon() : QIcon());
655}
656
657#if QT_CONFIG(tooltip)
658/*!
659 Returns the tooltip of the item at position \a index, or an
660 empty string if \a index is out of range.
661*/
662
663QString QToolBox::itemToolTip(int index) const
664{
665 Q_D(const QToolBox);
666 const QToolBoxPrivate::Page *c = d->page(index);
667 return (c ? c->toolTip() : QString());
668}
669#endif // QT_CONFIG(tooltip)
670
671/*! \reimp */
672void QToolBox::showEvent(QShowEvent *e)
673{
674 QWidget::showEvent(e);
675}
676
677/*! \reimp */
678void QToolBox::changeEvent(QEvent *ev)
679{
680 Q_D(QToolBox);
681 if (ev->type() == QEvent::StyleChange)
682 d->updateTabs();
683 QFrame::changeEvent(ev);
684}
685
686/*!
687 This virtual handler is called after a new item was added or
688 inserted at position \a index.
689
690 \sa itemRemoved()
691 */
692void QToolBox::itemInserted(int index)
693{
694 Q_UNUSED(index);
695}
696
697/*!
698 This virtual handler is called after an item was removed from
699 position \a index.
700
701 \sa itemInserted()
702 */
703void QToolBox::itemRemoved(int index)
704{
705 Q_UNUSED(index);
706}
707
708/*! \reimp */
709bool QToolBox::event(QEvent *e)
710{
711 return QFrame::event(e);
712}
713
714QT_END_NAMESPACE
715
716#include "moc_qtoolbox.cpp"
717#include "qtoolbox.moc"
QPainter(QPaintDevice *)
Constructs a painter that begins painting the paint device immediately.
friend class QWidget
Definition qpainter.h:431
\variable QStyleOptionComboBox::editable
void paintEvent(QPaintEvent *) override
\reimp
Definition qtoolbox.cpp:203
QSize minimumSizeHint() const override
Definition qtoolbox.cpp:162
void initStyleOption(QStyleOptionToolBox *opt) const
Definition qtoolbox.cpp:170
void _q_buttonClicked()
Definition qtoolbox.cpp:360
Page * currentPage
Definition qtoolbox.cpp:96
QVBoxLayout * layout
Definition qtoolbox.cpp:95
Page * page(int index)
Definition qtoolbox.cpp:111
const Page * page(int index) const
Definition qtoolbox.cpp:118
void _q_widgetDestroyed(QObject *)
Definition qtoolbox.cpp:424
const Page * page(const QObject *widget) const
Definition qtoolbox.cpp:99
std::vector< std::unique_ptr< Page > > PageList
Definition qtoolbox.cpp:78
PageList pageList
Definition qtoolbox.cpp:94
auto pageEquals
Definition qtoolbox.cpp:418