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

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