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