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

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