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
qtabwidget.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 "qtabwidget.h"
6
7#include "private/qapplication_p.h"
8#include "private/qwidget_p.h"
9#include "private/qtabbar_p.h"
10#include "qapplication.h"
11#include "qbitmap.h"
12#include "qevent.h"
13#include "qlayout.h"
14#include "qstackedwidget.h"
15#include "qstyle.h"
16#include "qstyleoption.h"
17#include "qstylepainter.h"
18#include "qtabbar.h"
19#include "qtoolbutton.h"
20
22
23using namespace Qt::StringLiterals;
24
25/*!
26 \class QTabWidget
27 \brief The QTabWidget class provides a stack of tabbed widgets.
28
29 \ingroup organizers
30 \ingroup basicwidgets
31 \inmodule QtWidgets
32
33 \image fusion-tabwidget.png {Tab widget with two tabs}
34
35 A tab widget provides a tab bar (see QTabBar) and a "page area"
36 that is used to display pages related to each tab. By default, the
37 tab bar is shown above the page area, but different configurations
38 are available (see \l{TabPosition}). Each tab is associated with a
39 different widget (called a page). Only the current page is shown in
40 the page area; all the other pages are hidden. The user can show a
41 different page by clicking on its tab or by pressing its
42 Alt+\e{letter} shortcut if it has one.
43
44 The normal way to use QTabWidget is to do the following:
45 \list 1
46 \li Create a QTabWidget.
47 \li Create a QWidget for each of the pages in the tab dialog, but
48 do not specify parent widgets for them.
49 \li Insert child widgets into the page widget, using layouts to
50 position them as normal.
51 \li Call addTab() or insertTab() to put the page widgets into the
52 tab widget, giving each tab a suitable label with an optional
53 keyboard shortcut.
54 \endlist
55
56 The position of the tabs is defined by \l tabPosition, their shape
57 by \l tabShape.
58
59 The signal currentChanged() is emitted when the user selects a
60 page.
61
62 The current page index is available as currentIndex(), the current
63 page widget with currentWidget(). You can retrieve a pointer to a
64 page widget with a given index using widget(), and can find the
65 index position of a widget with indexOf(). Use setCurrentWidget()
66 or setCurrentIndex() to show a particular page.
67
68 You can change a tab's text and icon using setTabText() or
69 setTabIcon(). A tab and its associated page can be removed with
70 removeTab().
71
72 Each tab is either enabled or disabled at any given time (see
73 setTabEnabled()). If a tab is enabled, the tab text is drawn
74 normally and the user can select that tab. If it is disabled, the
75 tab is drawn in a different way and the user cannot select that
76 tab. Note that even if a tab is disabled, the page can still be
77 visible, for example if all of the tabs happen to be disabled.
78
79 Tab widgets can be a very good way to split up a complex dialog.
80 An alternative is to use a QStackedWidget for which you provide some
81 means of navigating between pages, for example, a QToolBar or a
82 QListWidget.
83
84 Most of the functionality in QTabWidget is provided by a QTabBar
85 (at the top, providing the tabs) and a QStackedWidget (most of the
86 area, organizing the individual pages).
87
88 \sa QTabBar, QStackedWidget, QToolBox, {Tab Dialog Example}
89*/
90
91/*!
92 \enum QTabWidget::TabPosition
93
94 This enum type defines where QTabWidget draws the tab row:
95
96 \value North The tabs are drawn above the pages.
97 \value South The tabs are drawn below the pages.
98 \value West The tabs are drawn to the left of the pages.
99 \value East The tabs are drawn to the right of the pages.
100*/
101
102/*!
103 \enum QTabWidget::TabShape
104
105 This enum type defines the shape of the tabs:
106 \value Rounded The tabs are drawn with a rounded look. This is the default
107 shape.
108 \value Triangular The tabs are drawn with a triangular look.
109*/
110
111/*!
112 \fn void QTabWidget::currentChanged(int index)
113
114 This signal is emitted whenever the current page index changes.
115 The parameter is the new current page \a index position, or -1
116 if there isn't a new one (for example, if there are no widgets
117 in the QTabWidget)
118
119 \sa currentWidget(), currentIndex
120*/
121
122/*!
123 \fn void QTabWidget::tabCloseRequested(int index)
124 \since 4.5
125
126 This signal is emitted when the close button on a tab is clicked.
127 The \a index is the index that should be removed.
128
129 \sa setTabsClosable()
130*/
131
132/*!
133 \fn void QTabWidget::tabBarClicked(int index)
134
135 This signal is emitted when user clicks on a tab at an \a index.
136
137 \a index refers to the tab clicked, or -1 if no tab is under the cursor.
138
139 \since 5.2
140*/
141
142/*!
143 \fn void QTabWidget::tabBarDoubleClicked(int index)
144
145 This signal is emitted when the user double clicks on a tab at an \a index.
146
147 \a index is the index of a clicked tab, or -1 if no tab is under the cursor.
148
149 \since 5.2
150*/
151
153{
154 Q_DECLARE_PUBLIC(QTabWidget)
155
156public:
160 void showTab(int);
161 void removeTab(int);
162 void tabMoved(int from, int to);
163 void init();
164 bool isAutoHidden() const
165 {
166 // see QTabBarPrivate::autoHideTabs()
167 return (tabs->autoHide() && tabs->count() <= 1);
168 }
169
170 void initBasicStyleOption(QStyleOptionTabWidgetFrame *option) const;
171
172 QTabBar *tabs;
175 bool dirty;
180};
181
182QTabWidgetPrivate::QTabWidgetPrivate()
183 : tabs(nullptr), stack(nullptr), dirty(true),
184 pos(QTabWidget::North), shape(QTabWidget::Rounded),
185 leftCornerWidget(nullptr), rightCornerWidget(nullptr)
186{}
187
190
192{
193 Q_Q(QTabWidget);
194
195 stack = new QStackedWidget(q);
196 stack->setObjectName("qt_tabwidget_stackedwidget"_L1);
197 stack->setLineWidth(0);
198 // hack so that QMacStyle::layoutSpacing() can detect tab widget pages
199 stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::TabWidget));
200
201 QObjectPrivate::connect(stack, &QStackedWidget::widgetRemoved, this, &QTabWidgetPrivate::removeTab);
202 QTabBar *tabBar = new QTabBar(q);
203 tabBar->setObjectName("qt_tabwidget_tabbar"_L1);
204 tabBar->setDrawBase(false);
205 q->setTabBar(tabBar);
206
207 q->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding,
208 QSizePolicy::TabWidget));
209 q->setFocusPolicy(Qt::TabFocus);
210 q->setFocusProxy(tabs);
211 q->setTabPosition(static_cast<QTabWidget::TabPosition> (q->style()->styleHint(
212 QStyle::SH_TabWidget_DefaultTabPosition, nullptr, q )));
213
214}
215
216/*!
217 \reimp
218*/
219
220bool QTabWidget::hasHeightForWidth() const
221{
222 Q_D(const QTabWidget);
223 bool has = d->size_policy.hasHeightForWidth();
224 if (!has && d->stack)
225 has = d->stack->hasHeightForWidth();
226 return has;
227}
228
229/*!
230 \internal
231
232 Initialize only time inexpensive parts of the style option
233 for QTabWidget::setUpLayout()'s non-visible code path.
234*/
235void QTabWidgetPrivate::initBasicStyleOption(QStyleOptionTabWidgetFrame *option) const
236{
237 Q_Q(const QTabWidget);
238 option->initFrom(q);
239
240 if (q->documentMode())
241 option->lineWidth = 0;
242 else
243 option->lineWidth = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, q);
244
245 switch (pos) {
246 case QTabWidget::North:
247 option->shape = shape == QTabWidget::Rounded ? QTabBar::RoundedNorth
248 : QTabBar::TriangularNorth;
249 break;
250 case QTabWidget::South:
251 option->shape = shape == QTabWidget::Rounded ? QTabBar::RoundedSouth
252 : QTabBar::TriangularSouth;
253 break;
254 case QTabWidget::West:
255 option->shape = shape == QTabWidget::Rounded ? QTabBar::RoundedWest
256 : QTabBar::TriangularWest;
257 break;
258 case QTabWidget::East:
259 option->shape = shape == QTabWidget::Rounded ? QTabBar::RoundedEast
260 : QTabBar::TriangularEast;
261 break;
262 }
263
264 option->tabBarRect = q->tabBar()->geometry();
265}
266
267/*!
268 Initialize \a option with the values from this QTabWidget. This method is useful
269 for subclasses when they need a QStyleOptionTabWidgetFrame, but don't want to fill
270 in all the information themselves.
271
272 \sa QStyleOption::initFrom(), QTabBar::initStyleOption()
273*/
274void QTabWidget::initStyleOption(QStyleOptionTabWidgetFrame *option) const
275{
276 if (!option)
277 return;
278
279 Q_D(const QTabWidget);
280 d->initBasicStyleOption(option);
281
282 int exth = style()->pixelMetric(QStyle::PM_TabBarBaseHeight, nullptr, this);
283 QSize t(0, d->stack->frameWidth());
284 if (d->tabs->isVisibleTo(const_cast<QTabWidget *>(this))) {
285 t = d->tabs->sizeHint();
286 if (documentMode()) {
287 if (tabPosition() == East || tabPosition() == West) {
288 t.setHeight(height());
289 } else {
290 t.setWidth(width());
291 }
292 }
293 }
294
295 if (d->rightCornerWidget && d->rightCornerWidget->isVisible()) {
296 const QSize rightCornerSizeHint = d->rightCornerWidget->sizeHint();
297 const QSize bounds(rightCornerSizeHint.width(), t.height() - exth);
298 option->rightCornerWidgetSize = rightCornerSizeHint.boundedTo(bounds);
299 } else {
300 option->rightCornerWidgetSize = QSize(0, 0);
301 }
302
303 if (d->leftCornerWidget && d->leftCornerWidget->isVisible()) {
304 const QSize leftCornerSizeHint = d->leftCornerWidget->sizeHint();
305 const QSize bounds(leftCornerSizeHint.width(), t.height() - exth);
306 option->leftCornerWidgetSize = leftCornerSizeHint.boundedTo(bounds);
307 } else {
308 option->leftCornerWidgetSize = QSize(0, 0);
309 }
310
311 option->tabBarSize = t;
312
313 QRect selectedTabRect = tabBar()->tabRect(tabBar()->currentIndex());
314 selectedTabRect.moveTopLeft(selectedTabRect.topLeft() + option->tabBarRect.topLeft());
315 option->selectedTabRect = selectedTabRect;
316}
317
318/*!
319 Constructs a tabbed widget with parent \a parent.
320*/
321QTabWidget::QTabWidget(QWidget *parent)
322 : QWidget(*new QTabWidgetPrivate, parent, { })
323{
324 Q_D(QTabWidget);
325 d->init();
326}
327
328
329/*!
330 Destroys the tabbed widget.
331*/
332QTabWidget::~QTabWidget()
333{
334}
335
336/*!
337 \fn int QTabWidget::addTab(QWidget *page, const QString &label)
338
339 Adds a tab with the given \a page and \a label to the tab widget,
340 and returns the index of the tab in the tab bar. Ownership of \a page
341 is passed on to the QTabWidget.
342
343 If the tab's \a label contains an ampersand, the letter following
344 the ampersand is used as a shortcut for the tab, e.g. if the
345 label is "Bro\&wse" then Alt+W becomes a shortcut which will
346 move the focus to this tab.
347
348 \note If you call addTab() after show(), the layout system will try
349 to adjust to the changes in its widgets hierarchy and may cause
350 flicker. To prevent this, you can set the QWidget::updatesEnabled
351 property to false prior to changes; remember to set the property
352 to true when the changes are done, making the widget receive paint
353 events again.
354
355 \sa insertTab()
356*/
357int QTabWidget::addTab(QWidget *child, const QString &label)
358{
359 return insertTab(-1, child, label);
360}
361
362
363/*!
364 \fn int QTabWidget::addTab(QWidget *page, const QIcon &icon, const QString &label)
365 \overload
366
367 Adds a tab with the given \a page, \a icon, and \a label to the tab
368 widget, and returns the index of the tab in the tab bar. Ownership
369 of \a page is passed on to the QTabWidget.
370
371 This function is the same as addTab(), but with an additional \a
372 icon.
373*/
374int QTabWidget::addTab(QWidget *child, const QIcon& icon, const QString &label)
375{
376 return insertTab(-1, child, icon, label);
377}
378
379
380/*!
381 \fn int QTabWidget::insertTab(int index, QWidget *page, const QString &label)
382
383 Inserts a tab with the given \a label and \a page into the tab
384 widget at the specified \a index, and returns the index of the
385 inserted tab in the tab bar. Ownership of \a page is passed on to the
386 QTabWidget.
387
388 The label is displayed in the tab and may vary in appearance depending
389 on the configuration of the tab widget.
390
391 If the tab's \a label contains an ampersand, the letter following
392 the ampersand is used as a shortcut for the tab, e.g. if the
393 label is "Bro\&wse" then Alt+W becomes a shortcut which will
394 move the focus to this tab.
395
396 If \a index is out of range, the tab is simply appended.
397 Otherwise it is inserted at the specified position.
398
399 If the QTabWidget was empty before this function is called, the
400 new page becomes the current page. Inserting a new tab at an index
401 less than or equal to the current index will increment the current
402 index, but keep the current page.
403
404 \note If you call insertTab() after show(), the layout system will try
405 to adjust to the changes in its widgets hierarchy and may cause
406 flicker. To prevent this, you can set the QWidget::updatesEnabled
407 property to false prior to changes; remember to set the property
408 to true when the changes are done, making the widget receive paint
409 events again.
410
411 \sa addTab()
412*/
413int QTabWidget::insertTab(int index, QWidget *w, const QString &label)
414{
415 return insertTab(index, w, QIcon(), label);
416}
417
418
419/*!
420 \fn int QTabWidget::insertTab(int index, QWidget *page, const QIcon& icon, const QString &label)
421 \overload
422
423 Inserts a tab with the given \a label, \a page, and \a icon into
424 the tab widget at the specified \a index, and returns the index of the
425 inserted tab in the tab bar. Ownership of \a page is passed on to the
426 QTabWidget.
427
428 This function is the same as insertTab(), but with an additional
429 \a icon.
430*/
431int QTabWidget::insertTab(int index, QWidget *w, const QIcon& icon, const QString &label)
432{
433 Q_D(QTabWidget);
434 if (!w)
435 return -1;
436 index = d->stack->insertWidget(index, w);
437 d->tabs->insertTab(index, icon, label);
438 setUpLayout();
439 tabInserted(index);
440
441 return index;
442}
443
444
445/*!
446 Defines a new \a label for the page at position \a index's tab.
447
448 If the provided text contains an ampersand character ('&'), a
449 shortcut is automatically created for it. The character that
450 follows the '&' will be used as the shortcut key. Any previous
451 shortcut will be overwritten, or cleared if no shortcut is defined
452 by the text. See the \l {QShortcut#mnemonic}{QShortcut}
453 documentation for details (to display an actual ampersand, use
454 '&&').
455
456*/
457void QTabWidget::setTabText(int index, const QString &label)
458{
459 Q_D(QTabWidget);
460 d->tabs->setTabText(index, label);
461 setUpLayout();
462}
463
464/*!
465 Returns the label text for the tab on the page at position \a index.
466*/
467
468QString QTabWidget::tabText(int index) const
469{
470 Q_D(const QTabWidget);
471 return d->tabs->tabText(index);
472}
473
474/*!
475 Sets the \a icon for the tab at position \a index.
476*/
477void QTabWidget::setTabIcon(int index, const QIcon &icon)
478{
479 Q_D(QTabWidget);
480 d->tabs->setTabIcon(index, icon);
481 setUpLayout();
482}
483
484/*!
485 Returns the icon for the tab on the page at position \a index.
486*/
487
488QIcon QTabWidget::tabIcon(int index) const
489{
490 Q_D(const QTabWidget);
491 return d->tabs->tabIcon(index);
492}
493
494/*!
495 Returns \c true if the page at position \a index is enabled; otherwise returns \c false.
496
497 \sa setTabEnabled(), QWidget::isEnabled()
498*/
499
500bool QTabWidget::isTabEnabled(int index) const
501{
502 Q_D(const QTabWidget);
503 return d->tabs->isTabEnabled(index);
504}
505
506/*!
507 If \a enable is true, the page at position \a index is enabled; otherwise the page at
508 position \a index is disabled. The page's tab is redrawn appropriately.
509
510 QTabWidget uses QWidget::setEnabled() internally, rather than
511 keeping a separate flag.
512
513 Note that even a disabled tab/page may be visible. If the page is
514 visible already, QTabWidget will not hide it; if all the pages are
515 disabled, QTabWidget will show one of them.
516
517 \sa isTabEnabled(), QWidget::setEnabled()
518*/
519
520void QTabWidget::setTabEnabled(int index, bool enable)
521{
522 Q_D(QTabWidget);
523 d->tabs->setTabEnabled(index, enable);
524 if (QWidget *widget = d->stack->widget(index))
525 widget->setEnabled(enable);
526}
527
528/*!
529 Returns true if the page at position \a index is visible; otherwise returns false.
530
531 \sa setTabVisible()
532 \since 5.15
533*/
534
535bool QTabWidget::isTabVisible(int index) const
536{
537 Q_D(const QTabWidget);
538 return d->tabs->isTabVisible(index);
539}
540
541/*!
542 If \a visible is true, the page at position \a index is visible; otherwise the page at
543 position \a index is hidden. The page's tab is redrawn appropriately.
544
545 \sa isTabVisible()
546 \since 5.15
547*/
548
549void QTabWidget::setTabVisible(int index, bool visible)
550{
551 Q_D(QTabWidget);
552 QWidget *widget = d->stack->widget(index);
553 bool currentVisible = d->tabs->isTabVisible(d->tabs->currentIndex());
554 d->tabs->setTabVisible(index, visible);
555 if (!visible) {
556 if (widget)
557 widget->setVisible(false);
558 } else if (!currentVisible) {
559 setCurrentIndex(index);
560 if (widget)
561 widget->setVisible(true);
562 }
563 setUpLayout();
564}
565
566/*!
567 \fn void QTabWidget::setCornerWidget(QWidget *widget, Qt::Corner corner)
568
569 Sets the given \a widget to be shown in the specified \a corner of the
570 tab widget. The geometry of the widget is determined based on the widget's
571 sizeHint() and the style().
572
573 Only the horizontal element of the \a corner will be used.
574
575 Passing \nullptr shows no widget in the corner.
576
577 Any previously set corner widget is hidden.
578
579 All widgets set here will be deleted by the tab widget when it is
580 destroyed unless you separately reparent the widget after setting
581 some other corner widget (or \nullptr).
582
583 Note: Corner widgets are designed for \l North and \l South tab positions;
584 other orientations are known to not work properly.
585
586 \sa cornerWidget(), setTabPosition()
587*/
588void QTabWidget::setCornerWidget(QWidget * widget, Qt::Corner corner)
589{
590 Q_D(QTabWidget);
591 if (widget && widget->parentWidget() != this)
592 widget->setParent(this);
593
594 if (corner & Qt::TopRightCorner) {
595 if (d->rightCornerWidget)
596 d->rightCornerWidget->hide();
597 d->rightCornerWidget = widget;
598 } else {
599 if (d->leftCornerWidget)
600 d->leftCornerWidget->hide();
601 d->leftCornerWidget = widget;
602 }
603 setUpLayout();
604}
605
606/*!
607 Returns the widget shown in the \a corner of the tab widget or \nullptr.
608*/
609QWidget * QTabWidget::cornerWidget(Qt::Corner corner) const
610{
611 Q_D(const QTabWidget);
612 if (corner & Qt::TopRightCorner)
613 return d->rightCornerWidget;
614 return d->leftCornerWidget;
615}
616
617/*!
618 Removes the tab at position \a index from this stack of widgets.
619 The page widget itself is not deleted.
620
621 \sa addTab(), insertTab()
622*/
623void QTabWidget::removeTab(int index)
624{
625 Q_D(QTabWidget);
626 if (QWidget *w = d->stack->widget(index))
627 d->stack->removeWidget(w);
628}
629
630/*!
631 Returns a pointer to the page currently being displayed by the tab
632 dialog. The tab dialog does its best to make sure that this value
633 is never 0 (but if you try hard enough, it can be).
634
635 \sa currentIndex(), setCurrentWidget()
636*/
637
638QWidget * QTabWidget::currentWidget() const
639{
640 Q_D(const QTabWidget);
641 return d->stack->currentWidget();
642}
643
644/*!
645 Makes \a widget the current widget. The \a widget used must be a page in
646 this tab widget.
647
648 \sa addTab(), setCurrentIndex(), currentWidget()
649 */
650void QTabWidget::setCurrentWidget(QWidget *widget)
651{
652 Q_D(const QTabWidget);
653 d->tabs->setCurrentIndex(indexOf(widget));
654}
655
656
657/*!
658 \property QTabWidget::currentIndex
659 \brief the index position of the current tab page
660
661 The current index is -1 if there is no current widget.
662
663 By default, this property contains a value of -1 because there are initially
664 no tabs in the widget.
665*/
666
667int QTabWidget::currentIndex() const
668{
669 Q_D(const QTabWidget);
670 return d->tabs->currentIndex();
671}
672
673void QTabWidget::setCurrentIndex(int index)
674{
675 Q_D(QTabWidget);
676 d->tabs->setCurrentIndex(index);
677}
678
679
680/*!
681 Returns the index position of the page occupied by the widget \a
682 w, or -1 if the widget cannot be found.
683*/
684int QTabWidget::indexOf(const QWidget *w) const
685{
686 Q_D(const QTabWidget);
687 return d->stack->indexOf(w);
688}
689
690
691/*!
692 \reimp
693*/
694void QTabWidget::resizeEvent(QResizeEvent *e)
695{
696 QWidget::resizeEvent(e);
697 setUpLayout();
698}
699
700/*!
701 Replaces the dialog's QTabBar heading with the tab bar \a tb. Note
702 that this must be called \e before any tabs have been added, or
703 the behavior is undefined.
704
705 \sa tabBar()
706*/
707void QTabWidget::setTabBar(QTabBar* tb)
708{
709 Q_D(QTabWidget);
710 Q_ASSERT(tb);
711
712 if (tb->parentWidget() != this) {
713 tb->setParent(this);
714 tb->show();
715 }
716 delete d->tabs;
717 d->tabs = tb;
718 setFocusProxy(d->tabs);
719 QObjectPrivate::connect(d->tabs, &QTabBar::currentChanged,
720 d, &QTabWidgetPrivate::showTab);
721 QObjectPrivate::connect(d->tabs, &QTabBar::tabMoved,
722 d, &QTabWidgetPrivate::tabMoved);
723 connect(d->tabs, &QTabBar::tabBarClicked,
724 this, &QTabWidget::tabBarClicked);
725 connect(d->tabs, &QTabBar::tabBarDoubleClicked,
726 this, &QTabWidget::tabBarDoubleClicked);
727 if (d->tabs->tabsClosable())
728 connect(d->tabs, &QTabBar::tabCloseRequested,
729 this, &QTabWidget::tabCloseRequested);
730 tb->setExpanding(!documentMode());
731 setUpLayout();
732}
733
734
735/*!
736 Returns the current QTabBar.
737
738 \sa setTabBar()
739*/
740QTabBar* QTabWidget::tabBar() const
741{
742 Q_D(const QTabWidget);
743 return d->tabs;
744}
745
746/*
747 Ensures that the selected tab's page is visible and appropriately
748 sized.
749*/
750
752{
753 Q_Q(QTabWidget);
754 if (index < stack->count() && index >= 0)
755 stack->setCurrentIndex(index);
756 emit q->currentChanged(index);
757}
758
760{
761 Q_Q(QTabWidget);
762 tabs->removeTab(index);
763 q->setUpLayout();
764 q->tabRemoved(index);
765}
766
767void QTabWidgetPrivate::tabMoved(int from, int to)
768{
769 const QSignalBlocker blocker(stack);
770 QWidget *w = stack->widget(from);
771 stack->removeWidget(w);
772 stack->insertWidget(to, w);
773}
774
775/*
776 Set up the layout.
777 Get subrect from the current style, and set the geometry for the
778 stack widget, tab bar and corner widgets.
779*/
780void QTabWidget::setUpLayout(bool onlyCheck)
781{
782 Q_D(QTabWidget);
783 if (onlyCheck && !d->dirty)
784 return; // nothing to do
785
786 if (!isVisible()) {
787 // this must be done immediately, because QWidgetItem relies on it (even if !isVisible())
788 QStyleOptionTabWidgetFrame basicOption;
789 d->initBasicStyleOption(&basicOption);
790 d->setLayoutItemMargins(QStyle::SE_TabWidgetLayoutItem, &basicOption);
791 d->dirty = true;
792 return; // we'll do it later
793 }
794
795 QStyleOptionTabWidgetFrame option;
796 initStyleOption(&option);
797 d->setLayoutItemMargins(QStyle::SE_TabWidgetLayoutItem, &option);
798
799 QRect tabRect = style()->subElementRect(QStyle::SE_TabWidgetTabBar, &option, this);
800 d->panelRect = style()->subElementRect(QStyle::SE_TabWidgetTabPane, &option, this);
801 QRect contentsRect = style()->subElementRect(QStyle::SE_TabWidgetTabContents, &option, this);
802 QRect leftCornerRect = style()->subElementRect(QStyle::SE_TabWidgetLeftCorner, &option, this);
803 QRect rightCornerRect = style()->subElementRect(QStyle::SE_TabWidgetRightCorner, &option, this);
804
805 d->tabs->setGeometry(tabRect);
806 d->stack->setGeometry(contentsRect);
807 if (d->leftCornerWidget && d->leftCornerWidget->isVisible())
808 d->leftCornerWidget->setGeometry(leftCornerRect);
809 if (d->rightCornerWidget && d->rightCornerWidget->isVisible())
810 d->rightCornerWidget->setGeometry(rightCornerRect);
811
812 if (!onlyCheck)
813 update();
814 updateGeometry();
815}
816
817/*!
818 \internal
819*/
820static inline QSize basicSize(
821 bool horizontal, const QSize &lc, const QSize &rc, const QSize &s, const QSize &t)
822{
823 return horizontal
824 ? QSize(qMax(s.width(), t.width() + rc.width() + lc.width()),
825 s.height() + (qMax(rc.height(), qMax(lc.height(), t.height()))))
826 : QSize(s.width() + (qMax(rc.width(), qMax(lc.width(), t.width()))),
827 qMax(s.height(), t.height() + rc.height() + lc.height()));
828}
829
830/*!
831 \reimp
832*/
833QSize QTabWidget::sizeHint() const
834{
835 Q_D(const QTabWidget);
836 QSize lc(0, 0), rc(0, 0);
837 QStyleOptionTabWidgetFrame opt;
838 initStyleOption(&opt);
839 opt.state = QStyle::State_None;
840
841 if (d->leftCornerWidget && d->leftCornerWidget->isVisible())
842 lc = d->leftCornerWidget->sizeHint();
843 if (d->rightCornerWidget && d->rightCornerWidget->isVisible())
844 rc = d->rightCornerWidget->sizeHint();
845 if (!d->dirty) {
846 QTabWidget *that = const_cast<QTabWidget*>(this);
847 that->setUpLayout(true);
848 }
849 QSize s;
850 for (int i=0; i< d->stack->count(); ++i) {
851 if (const QWidget* w = d->stack->widget(i)) {
852 if (d->tabs->isTabVisible(i))
853 s = s.expandedTo(w->sizeHint());
854 }
855 }
856 QSize t;
857 if (!d->isAutoHidden()) {
858 t = d->tabs->sizeHint();
859 if (usesScrollButtons())
860 t = t.boundedTo(QSize(200,200));
861 else
862 t = t.boundedTo(QGuiApplication::primaryScreen()->virtualGeometry().size());
863 }
864
865 QSize sz = basicSize(d->pos == North || d->pos == South, lc, rc, s, t);
866
867 return style()->sizeFromContents(QStyle::CT_TabWidget, &opt, sz, this);
868}
869
870
871/*!
872 \reimp
873
874 Returns a suitable minimum size for the tab widget.
875*/
876QSize QTabWidget::minimumSizeHint() const
877{
878 Q_D(const QTabWidget);
879 QSize lc(0, 0), rc(0, 0);
880
881 if (d->leftCornerWidget && d->leftCornerWidget->isVisible())
882 lc = d->leftCornerWidget->minimumSizeHint();
883 if (d->rightCornerWidget && d->rightCornerWidget->isVisible())
884 rc = d->rightCornerWidget->minimumSizeHint();
885 if (!d->dirty) {
886 QTabWidget *that = const_cast<QTabWidget*>(this);
887 that->setUpLayout(true);
888 }
889 QSize s(d->stack->minimumSizeHint());
890 QSize t;
891 if (!d->isAutoHidden())
892 t = d->tabs->minimumSizeHint();
893
894 QSize sz = basicSize(d->pos == North || d->pos == South, lc, rc, s, t);
895
896 QStyleOptionTabWidgetFrame opt;
897 initStyleOption(&opt);
898 opt.palette = palette();
899 opt.state = QStyle::State_None;
900 return style()->sizeFromContents(QStyle::CT_TabWidget, &opt, sz, this);
901}
902
903/*!
904 \reimp
905*/
906int QTabWidget::heightForWidth(int width) const
907{
908 Q_D(const QTabWidget);
909 QStyleOptionTabWidgetFrame opt;
910 initStyleOption(&opt);
911 opt.state = QStyle::State_None;
912
913 QSize zero(0,0);
914 const QSize padding = style()->sizeFromContents(QStyle::CT_TabWidget, &opt, zero, this);
915
916 QSize lc(0, 0), rc(0, 0);
917 if (d->leftCornerWidget && d->leftCornerWidget->isVisible())
918 lc = d->leftCornerWidget->sizeHint();
919 if (d->rightCornerWidget && d->rightCornerWidget->isVisible())
920 rc = d->rightCornerWidget->sizeHint();
921 if (!d->dirty) {
922 QTabWidget *that = const_cast<QTabWidget*>(this);
923 that->setUpLayout(true);
924 }
925 QSize t;
926 if (!d->isAutoHidden()) {
927 t = d->tabs->sizeHint();
928 if (usesScrollButtons())
929 t = t.boundedTo(QSize(200,200));
930 else
931 t = t.boundedTo(QGuiApplication::primaryScreen()->virtualSize());
932 }
933
934 const bool tabIsHorizontal = (d->pos == North || d->pos == South);
935 const int contentsWidth = width - padding.width();
936 int stackWidth = contentsWidth;
937 if (!tabIsHorizontal)
938 stackWidth -= qMax(t.width(), qMax(lc.width(), rc.width()));
939
940 int stackHeight = d->stack->heightForWidth(stackWidth);
941 QSize s(stackWidth, stackHeight);
942
943 QSize contentSize = basicSize(tabIsHorizontal, lc, rc, s, t);
944 return (contentSize + padding).height();
945}
946
947
948/*!
949 \reimp
950 */
951void QTabWidget::showEvent(QShowEvent *)
952{
953 setUpLayout();
954}
955
957{
958 Q_Q(QTabWidget);
959 switch (pos) {
960 case QTabWidget::North:
961 tabs->setShape(shape == QTabWidget::Rounded ? QTabBar::RoundedNorth
962 : QTabBar::TriangularNorth);
963 break;
964 case QTabWidget::South:
965 tabs->setShape(shape == QTabWidget::Rounded ? QTabBar::RoundedSouth
966 : QTabBar::TriangularSouth);
967 break;
968 case QTabWidget::West:
969 tabs->setShape(shape == QTabWidget::Rounded ? QTabBar::RoundedWest
970 : QTabBar::TriangularWest);
971 break;
972 case QTabWidget::East:
973 tabs->setShape(shape == QTabWidget::Rounded ? QTabBar::RoundedEast
974 : QTabBar::TriangularEast);
975 break;
976 }
977 q->setUpLayout();
978}
979
980/*!
981 \property QTabWidget::tabPosition
982 \brief the position of the tabs in this tab widget
983
984 Possible values for this property are described by the TabPosition
985 enum.
986
987 By default, this property is set to \l North.
988
989 \sa TabPosition
990*/
991QTabWidget::TabPosition QTabWidget::tabPosition() const
992{
993 Q_D(const QTabWidget);
994 return d->pos;
995}
996
997void QTabWidget::setTabPosition(TabPosition pos)
998{
999 Q_D(QTabWidget);
1000 if (d->pos == pos)
1001 return;
1002 d->pos = pos;
1003 d->updateTabBarPosition();
1004}
1005
1006/*!
1007 \property QTabWidget::tabsClosable
1008 \brief whether close buttons are automatically added to each tab.
1009
1010 \since 4.5
1011
1012 \sa QTabBar::tabsClosable()
1013*/
1014bool QTabWidget::tabsClosable() const
1015{
1016 return tabBar()->tabsClosable();
1017}
1018
1019void QTabWidget::setTabsClosable(bool closeable)
1020{
1021 if (tabsClosable() == closeable)
1022 return;
1023
1024 tabBar()->setTabsClosable(closeable);
1025 if (closeable)
1026 connect(tabBar(), SIGNAL(tabCloseRequested(int)),
1027 this, SIGNAL(tabCloseRequested(int)));
1028 else
1029 disconnect(tabBar(), SIGNAL(tabCloseRequested(int)),
1030 this, SIGNAL(tabCloseRequested(int)));
1031 setUpLayout();
1032}
1033
1034/*!
1035 \property QTabWidget::movable
1036 \brief This property holds whether the user can move the tabs
1037 within the tabbar area.
1038
1039 \since 4.5
1040
1041 By default, this property is \c false;
1042*/
1043
1044bool QTabWidget::isMovable() const
1045{
1046 return tabBar()->isMovable();
1047}
1048
1049void QTabWidget::setMovable(bool movable)
1050{
1051 tabBar()->setMovable(movable);
1052}
1053
1054/*!
1055 \property QTabWidget::tabShape
1056 \brief the shape of the tabs in this tab widget
1057
1058 Possible values for this property are QTabWidget::Rounded
1059 (default) or QTabWidget::Triangular.
1060
1061 \sa TabShape
1062*/
1063
1064QTabWidget::TabShape QTabWidget::tabShape() const
1065{
1066 Q_D(const QTabWidget);
1067 return d->shape;
1068}
1069
1070void QTabWidget::setTabShape(TabShape s)
1071{
1072 Q_D(QTabWidget);
1073 if (d->shape == s)
1074 return;
1075 d->shape = s;
1076 d->updateTabBarPosition();
1077}
1078
1079/*!
1080 \reimp
1081 */
1082bool QTabWidget::event(QEvent *ev)
1083{
1084 if (ev->type() == QEvent::LayoutRequest)
1085 setUpLayout();
1086 return QWidget::event(ev);
1087}
1088
1089/*!
1090 \reimp
1091 */
1092void QTabWidget::changeEvent(QEvent *ev)
1093{
1094 if (ev->type() == QEvent::StyleChange
1095#ifdef Q_OS_MACOS
1096 || ev->type() == QEvent::MacSizeChange
1097#endif
1098 )
1099 setUpLayout();
1100 QWidget::changeEvent(ev);
1101}
1102
1103
1104/*!
1105 \reimp
1106 */
1107void QTabWidget::keyPressEvent(QKeyEvent *e)
1108{
1109 Q_D(QTabWidget);
1110 if (((e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab) &&
1111 count() > 1 && e->modifiers() & Qt::ControlModifier)) {
1112 int pageCount = d->tabs->count();
1113 int page = currentIndex();
1114 int dx = (e->key() == Qt::Key_Backtab || e->modifiers() & Qt::ShiftModifier) ? -1 : 1;
1115 for (int pass = 0; pass < pageCount; ++pass) {
1116 page+=dx;
1117 if (page < 0) {
1118 page = count() - 1;
1119 } else if (page >= pageCount) {
1120 page = 0;
1121 }
1122 if (d->tabs->isTabEnabled(page) && d->tabs->isTabVisible(page)) {
1123 setCurrentIndex(page);
1124 break;
1125 }
1126 }
1127 if (!QApplication::focusWidget())
1128 d->tabs->setFocus();
1129 } else {
1130 e->ignore();
1131 }
1132}
1133
1134/*!
1135 Returns the tab page at index position \a index or \nullptr if the
1136 \a index is out of range.
1137*/
1138QWidget *QTabWidget::widget(int index) const
1139{
1140 Q_D(const QTabWidget);
1141 return d->stack->widget(index);
1142}
1143
1144/*!
1145 \property QTabWidget::count
1146 \brief the number of tabs in the tab bar
1147
1148 By default, this property contains a value of 0.
1149*/
1150int QTabWidget::count() const
1151{
1152 Q_D(const QTabWidget);
1153 return d->tabs->count();
1154}
1155
1156#if QT_CONFIG(tooltip)
1157/*!
1158 Sets the tab tool tip for the page at position \a index to \a tip.
1159
1160 \sa tabToolTip()
1161*/
1162void QTabWidget::setTabToolTip(int index, const QString & tip)
1163{
1164 Q_D(QTabWidget);
1165 d->tabs->setTabToolTip(index, tip);
1166}
1167
1168/*!
1169 Returns the tab tool tip for the page at position \a index or
1170 an empty string if no tool tip has been set.
1171
1172 \sa setTabToolTip()
1173*/
1174QString QTabWidget::tabToolTip(int index) const
1175{
1176 Q_D(const QTabWidget);
1177 return d->tabs->tabToolTip(index);
1178}
1179#endif // QT_CONFIG(tooltip)
1180
1181#if QT_CONFIG(whatsthis)
1182/*!
1183 \since 4.1
1184
1185 Sets the What's This help text for the page at position \a index
1186 to \a text.
1187*/
1188void QTabWidget::setTabWhatsThis(int index, const QString &text)
1189{
1190 Q_D(QTabWidget);
1191 d->tabs->setTabWhatsThis(index, text);
1192}
1193
1194/*!
1195 \since 4.1
1196
1197 Returns the What's This help text for the page at position \a index,
1198 or an empty string if no help text has been set.
1199*/
1200QString QTabWidget::tabWhatsThis(int index) const
1201{
1202 Q_D(const QTabWidget);
1203 return d->tabs->tabWhatsThis(index);
1204}
1205#endif // QT_CONFIG(whatsthis)
1206
1207/*!
1208 This virtual handler is called after a new tab was added or
1209 inserted at position \a index.
1210
1211 \sa tabRemoved()
1212 */
1213void QTabWidget::tabInserted(int index)
1214{
1215 Q_UNUSED(index);
1216}
1217
1218/*!
1219 This virtual handler is called after a tab was removed from
1220 position \a index.
1221
1222 \sa tabInserted()
1223 */
1224void QTabWidget::tabRemoved(int index)
1225{
1226 Q_UNUSED(index);
1227}
1228
1229/*!
1230 \fn void QTabWidget::paintEvent(QPaintEvent *event)
1231
1232 Paints the tab widget's tab bar in response to the paint \a event.
1233*/
1234void QTabWidget::paintEvent(QPaintEvent *)
1235{
1236 Q_D(QTabWidget);
1237 if (documentMode()) {
1238 if (d->tabs->drawBase()) {
1239 QStylePainter p(this, tabBar());
1240 if (QWidget *w = cornerWidget(Qt::TopLeftCorner); w && w->isVisible()) {
1241 QStyleOptionTabBarBase opt;
1242 QTabBarPrivate::initStyleBaseOption(&opt, tabBar(), w->size());
1243 opt.rect.moveLeft(w->x() + opt.rect.x());
1244 opt.rect.moveTop(w->y() + opt.rect.y());
1245 p.drawPrimitive(QStyle::PE_FrameTabBarBase, opt);
1246 }
1247 if (QWidget *w = cornerWidget(Qt::TopRightCorner); w && w->isVisible()) {
1248 QStyleOptionTabBarBase opt;
1249 QTabBarPrivate::initStyleBaseOption(&opt, tabBar(), w->size());
1250 opt.rect.moveLeft(w->x() + opt.rect.x());
1251 opt.rect.moveTop(w->y() + opt.rect.y());
1252 p.drawPrimitive(QStyle::PE_FrameTabBarBase, opt);
1253 }
1254 }
1255 return;
1256 }
1257 QStylePainter p(this);
1258
1259 QStyleOptionTabWidgetFrame opt;
1260 initStyleOption(&opt);
1261 opt.rect = d->panelRect;
1262 p.drawPrimitive(QStyle::PE_FrameTabWidget, opt);
1263}
1264
1265/*!
1266 \property QTabWidget::iconSize
1267 \brief The size for icons in the tab bar
1268 \since 4.2
1269
1270 The default value is style-dependent. This is the maximum size
1271 that the icons will have. Icons are not scaled up if they are of
1272 smaller size.
1273
1274 \sa QTabBar::iconSize
1275*/
1276QSize QTabWidget::iconSize() const
1277{
1278 return d_func()->tabs->iconSize();
1279}
1280
1281void QTabWidget::setIconSize(const QSize &size)
1282{
1283 d_func()->tabs->setIconSize(size);
1284}
1285
1286/*!
1287 \property QTabWidget::elideMode
1288 \brief how to elide text in the tab bar
1289 \since 4.2
1290
1291 This property controls how items are elided when there is not
1292 enough space to show them for a given tab bar size.
1293
1294 By default the value is style dependent.
1295
1296 \sa QTabBar::elideMode, usesScrollButtons, QStyle::SH_TabBar_ElideMode
1297*/
1298Qt::TextElideMode QTabWidget::elideMode() const
1299{
1300 return d_func()->tabs->elideMode();
1301}
1302
1303void QTabWidget::setElideMode(Qt::TextElideMode mode)
1304{
1305 d_func()->tabs->setElideMode(mode);
1306}
1307
1308/*!
1309 \property QTabWidget::usesScrollButtons
1310 \brief Whether or not a tab bar should use buttons to scroll tabs when it
1311 has many tabs.
1312 \since 4.2
1313
1314 When there are too many tabs in a tab bar for its size, the tab bar can either choose
1315 to expand its size or to add buttons that allow you to scroll through the tabs.
1316
1317 By default the value is style dependent.
1318
1319 \sa elideMode, QTabBar::usesScrollButtons, QStyle::SH_TabBar_PreferNoArrows
1320*/
1321bool QTabWidget::usesScrollButtons() const
1322{
1323 return d_func()->tabs->usesScrollButtons();
1324}
1325
1326void QTabWidget::setUsesScrollButtons(bool useButtons)
1327{
1328 d_func()->tabs->setUsesScrollButtons(useButtons);
1329}
1330
1331/*!
1332 \property QTabWidget::documentMode
1333 \brief Whether or not the tab widget is rendered in a mode suitable for document
1334 pages. This is the same as document mode on \macos.
1335 \since 4.5
1336
1337 When this property is set the tab widget frame is not rendered. This mode is useful
1338 for showing document-type pages where the page covers most of the tab widget
1339 area.
1340
1341 \sa elideMode, QTabBar::documentMode, QTabBar::usesScrollButtons, QStyle::SH_TabBar_PreferNoArrows
1342*/
1343bool QTabWidget::documentMode() const
1344{
1345 Q_D(const QTabWidget);
1346 // QStyleSheetStyle could query documentMode during creation of our QTabBar.
1347 return d->tabs ? d->tabs->documentMode() : false;
1348}
1349
1350void QTabWidget::setDocumentMode(bool enabled)
1351{
1352 Q_D(QTabWidget);
1353 d->tabs->setDocumentMode(enabled);
1354 d->tabs->setExpanding(!enabled);
1355 d->tabs->setDrawBase(enabled);
1356 setUpLayout();
1357}
1358
1359/*!
1360 \property QTabWidget::tabBarAutoHide
1361 \brief If true, the tab bar is automatically hidden when it contains less
1362 than 2 tabs.
1363 \since 5.4
1364
1365 By default, this property is false.
1366
1367 \sa QWidget::visible
1368*/
1369
1370bool QTabWidget::tabBarAutoHide() const
1371{
1372 Q_D(const QTabWidget);
1373 return d->tabs->autoHide();
1374}
1375
1376void QTabWidget::setTabBarAutoHide(bool enabled)
1377{
1378 Q_D(QTabWidget);
1379 return d->tabs->setAutoHide(enabled);
1380}
1381
1382/*!
1383 Removes all the pages, but does not delete them. Calling this function
1384 is equivalent to calling removeTab() until the tab widget is empty.
1385*/
1386void QTabWidget::clear()
1387{
1388 Q_D(QTabWidget);
1389 Q_ASSERT(d->stack->layout());
1390 d->stack->layout()->setEnabled(false);
1391 d->stack->setUpdatesEnabled(false);
1392 d->tabs->setUpdatesEnabled(false);
1393
1394 int c = count();
1395 while (c)
1396 removeTab(--c);
1397
1398 d->tabs->setUpdatesEnabled(true);
1399 d->stack->setUpdatesEnabled(true);
1400 d->stack->layout()->setEnabled(true);
1401 d->stack->layout()->activate();
1402}
1403
1404QTabBar::Shape _q_tb_tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
1405{
1406 const bool rounded = (shape == QTabWidget::Rounded);
1407 if (position == QTabWidget::North)
1408 return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
1409 if (position == QTabWidget::South)
1410 return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
1411 if (position == QTabWidget::East)
1412 return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
1413 if (position == QTabWidget::West)
1414 return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
1415 return QTabBar::RoundedNorth;
1416}
1417
1418QT_END_NAMESPACE
1419
1420#include "moc_qtabwidget.cpp"
QStackedWidget * stack
QWidget * rightCornerWidget
void tabMoved(int from, int to)
bool isAutoHidden() const
void updateTabBarPosition()
QWidget * leftCornerWidget
void initBasicStyleOption(QStyleOptionTabWidgetFrame *option) const
Combined button and popup list for selecting options.
QTabBar::Shape _q_tb_tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
static QSize basicSize(bool horizontal, const QSize &lc, const QSize &rc, const QSize &s, const QSize &t)