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 "private/qlayoutengine_p.h"
41 #if QT_CONFIG(itemviews)
42 #include "qabstractitemdelegate.h"
43 #endif
44 #include "qapplication.h"
45 #include "qbitmap.h"
46 #include "qcursor.h"
47 #include "qevent.h"
48 #include "qpainter.h"
49 #include "qstyle.h"
50 #include "qstyleoption.h"
51 #include "qstylepainter.h"
52 #if QT_CONFIG(tabwidget)
53 #include "qtabwidget.h"
54 #endif
55 #include "qtooltip.h"
56 #if QT_CONFIG(whatsthis)
57 #include "qwhatsthis.h"
58 #endif
59 #include "private/qtextengine_p.h"
60 #ifndef QT_NO_ACCESSIBILITY
61 #include "qaccessible.h"
62 #endif
63 #ifdef Q_OS_MACOS
64 #include <qpa/qplatformnativeinterface.h>
65 #endif
66 
67 #include "qdebug.h"
68 #include "private/qapplication_p.h"
69 #include "private/qtabbar_p.h"
70 
71 QT_BEGIN_NAMESPACE
72 
73 namespace {
74 class CloseButton : public QAbstractButton
75 {
76     Q_OBJECT
77 
78 public:
79     explicit CloseButton(QWidget *parent = nullptr);
80 
81     QSize sizeHint() const override;
minimumSizeHint() const82     QSize minimumSizeHint() const override
83         { return sizeHint(); }
84     void enterEvent(QEvent *event) override;
85     void leaveEvent(QEvent *event) override;
86     void paintEvent(QPaintEvent *event) override;
87 };
88 }
89 
QMovableTabWidget(QWidget * parent)90 QMovableTabWidget::QMovableTabWidget(QWidget *parent)
91     : QWidget(parent)
92 {
93 }
94 
setPixmap(const QPixmap & pixmap)95 void QMovableTabWidget::setPixmap(const QPixmap &pixmap)
96 {
97     m_pixmap = pixmap;
98     update();
99 }
100 
paintEvent(QPaintEvent * e)101 void QMovableTabWidget::paintEvent(QPaintEvent *e)
102 {
103     Q_UNUSED(e);
104     QPainter p(this);
105     p.drawPixmap(0, 0, m_pixmap);
106 }
107 
verticalTabs(QTabBar::Shape shape)108 inline static bool verticalTabs(QTabBar::Shape shape)
109 {
110     return shape == QTabBar::RoundedWest
111            || shape == QTabBar::RoundedEast
112            || shape == QTabBar::TriangularWest
113            || shape == QTabBar::TriangularEast;
114 }
115 
updateMacBorderMetrics()116 void QTabBarPrivate::updateMacBorderMetrics()
117 {
118 #if defined(Q_OS_MACOS)
119     Q_Q(QTabBar);
120     // Extend the unified title and toolbar area to cover the tab bar iff
121     // 1) the tab bar is in document mode
122     // 2) the tab bar is directly below an "unified" area.
123     // The extending itself is done in the Cocoa platform plugin and Mac style,
124     // this function registers geometry and visibility state for the tab bar.
125 
126     // Calculate geometry
127     int upper, lower;
128     if (documentMode) {
129         QPoint windowPos = q->mapTo(q->window(), QPoint(0,0));
130         upper = windowPos.y();
131         int tabStripHeight = q->tabSizeHint(0).height();
132         int pixelTweak = -3;
133         lower = upper + tabStripHeight + pixelTweak;
134     } else {
135         upper = 0;
136         lower = 0;
137     }
138 
139     QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
140     if (!nativeInterface)
141         return;
142     quintptr identifier = reinterpret_cast<quintptr>(q);
143 
144     // Set geometry
145     QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
146         nativeInterface->nativeResourceFunctionForIntegration("registerContentBorderArea");
147     if (!function)
148         return; // Not Cocoa platform plugin.
149     typedef void (*RegisterContentBorderAreaFunction)(QWindow *window, quintptr identifier, int upper, int lower);
150     (reinterpret_cast<RegisterContentBorderAreaFunction>(function))(q->window()->windowHandle(), identifier, upper, lower);
151 
152     // Set visibility state
153     function = nativeInterface->nativeResourceFunctionForIntegration("setContentBorderAreaEnabled");
154     if (!function)
155         return;
156     typedef void (*SetContentBorderAreaEnabledFunction)(QWindow *window, quintptr identifier, bool enable);
157     (reinterpret_cast<SetContentBorderAreaEnabledFunction>(function))(q->window()->windowHandle(), identifier, q->isVisible());
158 #endif
159 }
160 
161 /*!
162     \internal
163     This is basically QTabBar::initStyleOption() but
164     without the expensive QFontMetrics::elidedText() call.
165 */
166 
initBasicStyleOption(QStyleOptionTab * option,int tabIndex) const167 void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option, int tabIndex) const
168 {
169     Q_Q(const QTabBar);
170     const int totalTabs = tabList.size();
171 
172     if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
173         return;
174 
175     const QTabBarPrivate::Tab &tab = tabList.at(tabIndex);
176     option->initFrom(q);
177     option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
178     option->rect = q->tabRect(tabIndex);
179     const bool isCurrent = tabIndex == currentIndex;
180     option->row = 0;
181     if (tabIndex == pressedIndex)
182         option->state |= QStyle::State_Sunken;
183     if (isCurrent)
184         option->state |= QStyle::State_Selected;
185     if (isCurrent && q->hasFocus())
186         option->state |= QStyle::State_HasFocus;
187     if (!tab.enabled)
188         option->state &= ~QStyle::State_Enabled;
189     if (q->isActiveWindow())
190         option->state |= QStyle::State_Active;
191     if (!dragInProgress && option->rect == hoverRect)
192         option->state |= QStyle::State_MouseOver;
193     option->shape = shape;
194     option->text = tab.text;
195 
196     if (tab.textColor.isValid())
197         option->palette.setColor(q->foregroundRole(), tab.textColor);
198     option->icon = tab.icon;
199     option->iconSize = q->iconSize();  // Will get the default value then.
200 
201     option->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
202     option->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
203     option->documentMode = documentMode;
204 
205     if (tabIndex > 0 && tabIndex - 1 == currentIndex)
206         option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
207     else if (tabIndex + 1 < totalTabs && tabIndex + 1 == currentIndex)
208         option->selectedPosition = QStyleOptionTab::NextIsSelected;
209     else
210         option->selectedPosition = QStyleOptionTab::NotAdjacent;
211 
212     const bool paintBeginning = (tabIndex == firstVisible) || (dragInProgress && tabIndex == pressedIndex + 1);
213     const bool paintEnd = (tabIndex == lastVisible) || (dragInProgress && tabIndex == pressedIndex - 1);
214     if (paintBeginning) {
215         if (paintEnd)
216             option->position = QStyleOptionTab::OnlyOneTab;
217         else
218             option->position = QStyleOptionTab::Beginning;
219     } else if (paintEnd) {
220         option->position = QStyleOptionTab::End;
221     } else {
222         option->position = QStyleOptionTab::Middle;
223     }
224 
225 #if QT_CONFIG(tabwidget)
226     if (const QTabWidget *tw = qobject_cast<const QTabWidget *>(q->parentWidget())) {
227         option->features |= QStyleOptionTab::HasFrame;
228         if (tw->cornerWidget(Qt::TopLeftCorner) || tw->cornerWidget(Qt::BottomLeftCorner))
229             option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
230         if (tw->cornerWidget(Qt::TopRightCorner) || tw->cornerWidget(Qt::BottomRightCorner))
231             option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
232     }
233 #endif
234     if (QStyleOptionTabV4 *optv4 = qstyleoption_cast<QStyleOptionTabV4 *>(option))
235         optv4->tabIndex = tabIndex;
236 }
237 
238 /*!
239     Initialize \a option with the values from the tab at \a tabIndex. This method
240     is useful for subclasses when they need a QStyleOptionTab,
241     but don't want to fill in all the information themselves.
242 
243     \sa QStyleOption::initFrom(), QTabWidget::initStyleOption()
244 */
initStyleOption(QStyleOptionTab * option,int tabIndex) const245 void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
246 {
247     Q_D(const QTabBar);
248     d->initBasicStyleOption(option, tabIndex);
249 
250     QRect textRect = style()->subElementRect(QStyle::SE_TabBarTabText, option, this);
251     option->text = fontMetrics().elidedText(option->text, d->elideMode, textRect.width(),
252                         Qt::TextShowMnemonic);
253 }
254 
255 /*!
256     \class QTabBar
257     \brief The QTabBar class provides a tab bar, e.g. for use in tabbed dialogs.
258 
259     \ingroup basicwidgets
260     \inmodule QtWidgets
261 
262     QTabBar is straightforward to use; it draws the tabs using one of
263     the predefined \l{QTabBar::Shape}{shapes}, and emits a
264     signal when a tab is selected. It can be subclassed to tailor the
265     look and feel. Qt also provides a ready-made \l{QTabWidget}.
266 
267     Each tab has a tabText(), an optional tabIcon(), an optional
268     tabToolTip(), optional tabWhatsThis() and optional tabData().
269     The tabs's attributes can be changed with setTabText(), setTabIcon(),
270     setTabToolTip(), setTabWhatsThis and setTabData(). Each tabs can be
271     enabled or disabled individually with setTabEnabled().
272 
273     Each tab can display text in a distinct color. The current text color
274     for a tab can be found with the tabTextColor() function. Set the text
275     color for a particular tab with setTabTextColor().
276 
277     Tabs are added using addTab(), or inserted at particular positions
278     using insertTab(). The total number of tabs is given by
279     count(). Tabs can be removed from the tab bar with
280     removeTab(). Combining removeTab() and insertTab() allows you to
281     move tabs to different positions.
282 
283     The \l shape property defines the tabs' appearance. The choice of
284     shape is a matter of taste, although tab dialogs (for preferences
285     and similar) invariably use \l RoundedNorth.
286     Tab controls in windows other than dialogs almost
287     always use either \l RoundedSouth or \l TriangularSouth. Many
288     spreadsheets and other tab controls in which all the pages are
289     essentially similar use \l TriangularSouth, whereas \l
290     RoundedSouth is used mostly when the pages are different (e.g. a
291     multi-page tool palette). The default in QTabBar is \l
292     RoundedNorth.
293 
294     The most important part of QTabBar's API is the currentChanged()
295     signal.  This is emitted whenever the current tab changes (even at
296     startup, when the current tab changes from 'none'). There is also
297     a slot, setCurrentIndex(), which can be used to select a tab
298     programmatically. The function currentIndex() returns the index of
299     the current tab, \l count holds the number of tabs.
300 
301     QTabBar creates automatic mnemonic keys in the manner of QAbstractButton;
302     e.g. if a tab's label is "\&Graphics", Alt+G becomes a shortcut
303     key for switching to that tab.
304 
305     The following virtual functions may need to be reimplemented in
306     order to tailor the look and feel or store extra data with each
307     tab:
308 
309     \list
310     \li tabSizeHint() calcuates the size of a tab.
311     \li tabInserted() notifies that a new tab was added.
312     \li tabRemoved() notifies that a tab was removed.
313     \li tabLayoutChange() notifies that the tabs have been re-laid out.
314     \li paintEvent() paints all tabs.
315     \endlist
316 
317     For subclasses, you might also need the tabRect() functions which
318     returns the visual geometry of a single tab.
319 
320     \table 100%
321     \row \li \inlineimage fusion-tabbar.png Screenshot of a Fusion style tab bar
322          \li A tab bar shown in the \l{Qt Widget Gallery}{Fusion widget style}.
323     \row \li \inlineimage fusion-tabbar-truncated.png Screenshot of a truncated Fusion tab bar
324          \li A truncated tab bar shown in the Fusion widget style.
325     \endtable
326 
327     \sa QTabWidget
328 */
329 
330 /*!
331     \enum QTabBar::Shape
332 
333     This enum type lists the built-in shapes supported by QTabBar. Treat these
334     as hints as some styles may not render some of the shapes. However,
335     position should be honored.
336 
337     \value RoundedNorth  The normal rounded look above the pages
338 
339     \value RoundedSouth  The normal rounded look below the pages
340 
341     \value RoundedWest  The normal rounded look on the left side of the pages
342 
343     \value RoundedEast  The normal rounded look on the right side the pages
344 
345     \value TriangularNorth  Triangular tabs above the pages.
346 
347     \value TriangularSouth  Triangular tabs similar to those used in
348     the Excel spreadsheet, for example
349 
350     \value TriangularWest  Triangular tabs on the left of the pages.
351 
352     \value TriangularEast  Triangular tabs on the right of the pages.
353 */
354 
355 /*!
356     \fn void QTabBar::currentChanged(int index)
357 
358     This signal is emitted when the tab bar's current tab changes. The
359     new current has the given \a index, or -1 if there isn't a new one
360     (for example, if there are no tab in the QTabBar)
361 */
362 
363 /*!
364     \fn void QTabBar::tabCloseRequested(int index)
365     \since 4.5
366 
367     This signal is emitted when the close button on a tab is clicked.
368     The \a index is the index that should be removed.
369 
370     \sa setTabsClosable()
371 */
372 
373 /*!
374     \fn void QTabBar::tabMoved(int from, int to)
375     \since 4.5
376 
377     This signal is emitted when the tab has moved the tab
378     at index position \a from to index position \a to.
379 
380     note: QTabWidget will automatically move the page when
381     this signal is emitted from its tab bar.
382 
383     \sa moveTab()
384 */
385 
386 /*!
387     \fn void QTabBar::tabBarClicked(int index)
388 
389     This signal is emitted when user clicks on a tab at an \a index.
390 
391     \a index is the index of a clicked tab, or -1 if no tab is under the cursor.
392 
393     \since 5.2
394 */
395 
396 /*!
397     \fn void QTabBar::tabBarDoubleClicked(int index)
398 
399     This signal is emitted when the user double clicks on a tab at \a index.
400 
401     \a index refers to the tab clicked, or -1 if no tab is under the cursor.
402 
403     \since 5.2
404 */
405 
init()406 void QTabBarPrivate::init()
407 {
408     Q_Q(QTabBar);
409     leftB = new QToolButton(q);
410     leftB->setAutoRepeat(true);
411     QObject::connect(leftB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
412     leftB->hide();
413     rightB = new QToolButton(q);
414     rightB->setAutoRepeat(true);
415     QObject::connect(rightB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
416     rightB->hide();
417 #ifdef QT_KEYPAD_NAVIGATION
418     if (QApplicationPrivate::keypadNavigationEnabled()) {
419         leftB->setFocusPolicy(Qt::NoFocus);
420         rightB->setFocusPolicy(Qt::NoFocus);
421         q->setFocusPolicy(Qt::NoFocus);
422     } else
423 #endif
424         q->setFocusPolicy(Qt::TabFocus);
425 
426 #ifndef QT_NO_ACCESSIBILITY
427     leftB->setAccessibleName(QTabBar::tr("Scroll Left"));
428     rightB->setAccessibleName(QTabBar::tr("Scroll Right"));
429 #endif
430     q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
431     elideMode = Qt::TextElideMode(q->style()->styleHint(QStyle::SH_TabBar_ElideMode, nullptr, q));
432     useScrollButtons = !q->style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, nullptr, q);
433 }
434 
at(int index)435 QTabBarPrivate::Tab *QTabBarPrivate::at(int index)
436 {
437     return validIndex(index)?&tabList[index]:nullptr;
438 }
439 
at(int index) const440 const QTabBarPrivate::Tab *QTabBarPrivate::at(int index) const
441 {
442     return validIndex(index)?&tabList[index]:nullptr;
443 }
444 
indexAtPos(const QPoint & p) const445 int QTabBarPrivate::indexAtPos(const QPoint &p) const
446 {
447     Q_Q(const QTabBar);
448     if (q->tabRect(currentIndex).contains(p))
449         return currentIndex;
450     for (int i = 0; i < tabList.count(); ++i)
451         if (tabList.at(i).enabled && q->tabRect(i).contains(p))
452             return i;
453     return -1;
454 }
455 
layoutTabs()456 void QTabBarPrivate::layoutTabs()
457 {
458     Q_Q(QTabBar);
459     layoutDirty = false;
460     QSize size = q->size();
461     int last, available;
462     int maxExtent;
463     int i;
464     bool vertTabs = verticalTabs(shape);
465     int tabChainIndex = 0;
466     int hiddenTabs = 0;
467 
468     Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, nullptr, q));
469     QVector<QLayoutStruct> tabChain(tabList.count() + 2);
470 
471     // We put an empty item at the front and back and set its expansive attribute
472     // depending on tabAlignment and expanding.
473     tabChain[tabChainIndex].init();
474     tabChain[tabChainIndex].expansive = (!expanding)
475                                         && (tabAlignment != Qt::AlignLeft)
476                                         && (tabAlignment != Qt::AlignJustify);
477     tabChain[tabChainIndex].empty = true;
478     ++tabChainIndex;
479 
480     // We now go through our list of tabs and set the minimum size and the size hint
481     // This will allow us to elide text if necessary. Since we don't set
482     // a maximum size, tabs will EXPAND to fill up the empty space.
483     // Since tab widget is rather *ahem* strict about keeping the geometry of the
484     // tab bar to its absolute minimum, this won't bleed through, but will show up
485     // if you use tab bar on its own (a.k.a. not a bug, but a feature).
486     // Update: if expanding is false, we DO set a maximum size to prevent the tabs
487     // being wider than necessary.
488     if (!vertTabs) {
489         int minx = 0;
490         int x = 0;
491         int maxHeight = 0;
492         for (i = 0; i < tabList.count(); ++i) {
493             if (!tabList.at(i).visible) {
494                 ++hiddenTabs;
495                 continue;
496             }
497             QSize sz = q->tabSizeHint(i);
498             tabList[i].maxRect = QRect(x, 0, sz.width(), sz.height());
499             x += sz.width();
500             maxHeight = qMax(maxHeight, sz.height());
501             sz = q->minimumTabSizeHint(i);
502             tabList[i].minRect = QRect(minx, 0, sz.width(), sz.height());
503             minx += sz.width();
504             tabChain[tabChainIndex].init();
505             tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.width();
506             tabChain[tabChainIndex].minimumSize = sz.width();
507             tabChain[tabChainIndex].empty = false;
508             tabChain[tabChainIndex].expansive = true;
509 
510             if (!expanding)
511                 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
512             ++tabChainIndex;
513         }
514 
515         last = minx;
516         available = size.width();
517         maxExtent = maxHeight;
518     } else {
519         int miny = 0;
520         int y = 0;
521         int maxWidth = 0;
522         for (i = 0; i < tabList.count(); ++i) {
523             if (!tabList.at(i).visible) {
524                 ++hiddenTabs;
525                 continue;
526             }
527             QSize sz = q->tabSizeHint(i);
528             tabList[i].maxRect = QRect(0, y, sz.width(), sz.height());
529             y += sz.height();
530             maxWidth = qMax(maxWidth, sz.width());
531             sz = q->minimumTabSizeHint(i);
532             tabList[i].minRect = QRect(0, miny, sz.width(), sz.height());
533             miny += sz.height();
534             tabChain[tabChainIndex].init();
535             tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.height();
536             tabChain[tabChainIndex].minimumSize = sz.height();
537             tabChain[tabChainIndex].empty = false;
538             tabChain[tabChainIndex].expansive = true;
539 
540             if (!expanding)
541                 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
542             ++tabChainIndex;
543         }
544 
545         last = miny;
546         available = size.height();
547         maxExtent = maxWidth;
548     }
549 
550     // Mirror our front item.
551     tabChain[tabChainIndex].init();
552     tabChain[tabChainIndex].expansive = (!expanding)
553                                         && (tabAlignment != Qt::AlignRight)
554                                         && (tabAlignment != Qt::AlignJustify);
555     tabChain[tabChainIndex].empty = true;
556     Q_ASSERT(tabChainIndex == tabChain.count() - 1 - hiddenTabs); // add an assert just to make sure.
557 
558     // Do the calculation
559     qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0);
560 
561     // Use the results
562     hiddenTabs = 0;
563     for (i = 0; i < tabList.count(); ++i) {
564         if (!tabList.at(i).visible) {
565             tabList[i].rect = QRect();
566             ++hiddenTabs;
567             continue;
568         }
569         const QLayoutStruct &lstruct = tabChain.at(i + 1 - hiddenTabs);
570         if (!vertTabs)
571             tabList[i].rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent);
572         else
573             tabList[i].rect.setRect(0, lstruct.pos, maxExtent, lstruct.size);
574     }
575 
576     if (useScrollButtons && tabList.count() && last > available) {
577         const QRect scrollRect = normalizedScrollRect(0);
578         scrollOffset = -scrollRect.left();
579 
580         Q_Q(QTabBar);
581         QStyleOption opt;
582         opt.init(q);
583         QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
584         QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
585         int scrollButtonWidth = q->style()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, &opt, q);
586 
587         // Normally SE_TabBarScrollLeftButton should have the same width as PM_TabBarScrollButtonWidth.
588         // But if that is not the case, we set the actual button width to PM_TabBarScrollButtonWidth, and
589         // use the extra space from SE_TabBarScrollLeftButton as margins towards the tabs.
590         if (vertTabs) {
591             scrollButtonLeftRect.setHeight(scrollButtonWidth);
592             scrollButtonRightRect.setY(scrollButtonRightRect.bottom() + 1 - scrollButtonWidth);
593             scrollButtonRightRect.setHeight(scrollButtonWidth);
594             leftB->setArrowType(Qt::UpArrow);
595             rightB->setArrowType(Qt::DownArrow);
596         } else if (q->layoutDirection() == Qt::RightToLeft) {
597             scrollButtonRightRect.setWidth(scrollButtonWidth);
598             scrollButtonLeftRect.setX(scrollButtonLeftRect.right() + 1 - scrollButtonWidth);
599             scrollButtonLeftRect.setWidth(scrollButtonWidth);
600             leftB->setArrowType(Qt::RightArrow);
601             rightB->setArrowType(Qt::LeftArrow);
602         } else {
603             scrollButtonLeftRect.setWidth(scrollButtonWidth);
604             scrollButtonRightRect.setX(scrollButtonRightRect.right() + 1 - scrollButtonWidth);
605             scrollButtonRightRect.setWidth(scrollButtonWidth);
606             leftB->setArrowType(Qt::LeftArrow);
607             rightB->setArrowType(Qt::RightArrow);
608         }
609 
610         leftB->setGeometry(scrollButtonLeftRect);
611         leftB->setEnabled(false);
612         leftB->show();
613 
614         rightB->setGeometry(scrollButtonRightRect);
615         rightB->setEnabled(last - scrollOffset > scrollRect.x() + scrollRect.width());
616         rightB->show();
617     } else {
618         scrollOffset = 0;
619         rightB->hide();
620         leftB->hide();
621     }
622 
623     layoutWidgets();
624     q->tabLayoutChange();
625 }
626 
normalizedScrollRect(int index)627 QRect QTabBarPrivate::normalizedScrollRect(int index)
628 {
629     // "Normalized scroll rect" means return the free space on the tab bar
630     // that doesn't overlap with scroll buttons or tear indicators, and
631     // always return the rect as horizontal Qt::LeftToRight, even if the
632     // tab bar itself is in a different orientation.
633 
634     Q_Q(QTabBar);
635     QStyleOptionTabV4 opt;
636     q->initStyleOption(&opt, currentIndex);
637     opt.rect = q->rect();
638 
639     QRect scrollButtonLeftRect = q->style()->subElementRect(QStyle::SE_TabBarScrollLeftButton, &opt, q);
640     QRect scrollButtonRightRect = q->style()->subElementRect(QStyle::SE_TabBarScrollRightButton, &opt, q);
641     QRect tearLeftRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &opt, q);
642     QRect tearRightRect = q->style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &opt, q);
643 
644     if (verticalTabs(shape)) {
645         int topEdge, bottomEdge;
646         bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2;
647         bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2;
648 
649         if (leftButtonIsOnTop && rightButtonIsOnTop) {
650             topEdge = scrollButtonRightRect.bottom() + 1;
651             bottomEdge = q->height();
652         } else if (!leftButtonIsOnTop && !rightButtonIsOnTop) {
653             topEdge = 0;
654             bottomEdge = scrollButtonLeftRect.top();
655         } else {
656             topEdge = scrollButtonLeftRect.bottom() + 1;
657             bottomEdge = scrollButtonRightRect.top();
658         }
659 
660         bool tearTopVisible = index != 0 && topEdge != -scrollOffset;
661         bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != tabList.constLast().rect.bottom() + 1 - scrollOffset;
662         if (tearTopVisible && !tearLeftRect.isNull())
663             topEdge = tearLeftRect.bottom() + 1;
664         if (tearBottomVisible && !tearRightRect.isNull())
665             bottomEdge = tearRightRect.top();
666 
667         return QRect(topEdge, 0, bottomEdge - topEdge, q->height());
668     } else {
669         if (q->layoutDirection() == Qt::RightToLeft) {
670             scrollButtonLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonLeftRect);
671             scrollButtonRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), scrollButtonRightRect);
672             tearLeftRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearLeftRect);
673             tearRightRect = QStyle::visualRect(Qt::RightToLeft, q->rect(), tearRightRect);
674         }
675 
676         int leftEdge, rightEdge;
677         bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2;
678         bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2;
679 
680         if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) {
681             leftEdge = scrollButtonRightRect.right() + 1;
682             rightEdge = q->width();
683         } else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) {
684             leftEdge = 0;
685             rightEdge = scrollButtonLeftRect.left();
686         } else {
687             leftEdge = scrollButtonLeftRect.right() + 1;
688             rightEdge = scrollButtonRightRect.left();
689         }
690 
691         bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset;
692         bool tearRightVisible = index != tabList.size() - 1 && rightEdge != tabList.constLast().rect.right() + 1 - scrollOffset;
693         if (tearLeftVisible && !tearLeftRect.isNull())
694             leftEdge = tearLeftRect.right() + 1;
695         if (tearRightVisible && !tearRightRect.isNull())
696             rightEdge = tearRightRect.left();
697 
698         return QRect(leftEdge, 0, rightEdge - leftEdge, q->height());
699     }
700 }
701 
hoveredTabIndex() const702 int QTabBarPrivate::hoveredTabIndex() const
703 {
704     if (dragInProgress)
705         return currentIndex;
706     if (hoverIndex >= 0)
707         return hoverIndex;
708     return -1;
709 }
710 
makeVisible(int index)711 void QTabBarPrivate::makeVisible(int index)
712 {
713     Q_Q(QTabBar);
714     if (!validIndex(index) || leftB->isHidden())
715         return;
716 
717     const QRect tabRect = tabList.at(index).rect;
718     const int oldScrollOffset = scrollOffset;
719     const bool horiz = !verticalTabs(shape);
720     const int tabStart = horiz ? tabRect.left() : tabRect.top();
721     const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
722     const int lastTabEnd = horiz ? tabList.constLast().rect.right() : tabList.constLast().rect.bottom();
723     const QRect scrollRect = normalizedScrollRect(index);
724     const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
725     const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
726 
727     if (tabStart < scrolledTabBarStart) {
728         // Tab is outside on the left, so scroll left.
729         scrollOffset = tabStart - scrollRect.left();
730     } else if (tabEnd > scrolledTabBarEnd) {
731         // Tab is outside on the right, so scroll right.
732         scrollOffset = tabEnd - scrollRect.right();
733     }
734 
735     leftB->setEnabled(scrollOffset > -scrollRect.left());
736     rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right());
737 
738     if (oldScrollOffset != scrollOffset) {
739         q->update();
740         layoutWidgets();
741     }
742 }
743 
killSwitchTabTimer()744 void QTabBarPrivate::killSwitchTabTimer()
745 {
746     Q_Q(QTabBar);
747     if (switchTabTimerId) {
748         q->killTimer(switchTabTimerId);
749         switchTabTimerId = 0;
750     }
751     switchTabCurrentIndex = -1;
752 }
753 
layoutTab(int index)754 void QTabBarPrivate::layoutTab(int index)
755 {
756     Q_Q(QTabBar);
757     Q_ASSERT(index >= 0);
758 
759     Tab &tab = tabList[index];
760     bool vertical = verticalTabs(shape);
761     if (!(tab.leftWidget || tab.rightWidget))
762         return;
763 
764     QStyleOptionTabV4 opt;
765     q->initStyleOption(&opt, index);
766     if (tab.leftWidget) {
767         QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabLeftButton, &opt, q);
768         QPoint p = rect.topLeft();
769         if ((index == pressedIndex) || paintWithOffsets) {
770             if (vertical)
771                 p.setY(p.y() + tabList[index].dragOffset);
772             else
773                 p.setX(p.x() + tabList[index].dragOffset);
774         }
775         tab.leftWidget->move(p);
776     }
777     if (tab.rightWidget) {
778         QRect rect = q->style()->subElementRect(QStyle::SE_TabBarTabRightButton, &opt, q);
779         QPoint p = rect.topLeft();
780         if ((index == pressedIndex) || paintWithOffsets) {
781             if (vertical)
782                 p.setY(p.y() + tab.dragOffset);
783             else
784                 p.setX(p.x() + tab.dragOffset);
785         }
786         tab.rightWidget->move(p);
787     }
788 }
789 
layoutWidgets(int start)790 void QTabBarPrivate::layoutWidgets(int start)
791 {
792     Q_Q(QTabBar);
793     for (int i = start; i < q->count(); ++i) {
794         layoutTab(i);
795     }
796 }
797 
autoHideTabs()798 void QTabBarPrivate::autoHideTabs()
799 {
800     Q_Q(QTabBar);
801 
802     if (autoHide)
803         q->setVisible(q->count() > 1);
804 }
805 
_q_closeTab()806 void QTabBarPrivate::_q_closeTab()
807 {
808     Q_Q(QTabBar);
809     QObject *object = q->sender();
810     int tabToClose = -1;
811     QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, q);
812     for (int i = 0; i < tabList.count(); ++i) {
813         if (closeSide == QTabBar::LeftSide) {
814             if (tabList.at(i).leftWidget == object) {
815                 tabToClose = i;
816                 break;
817             }
818         } else {
819             if (tabList.at(i).rightWidget == object) {
820                 tabToClose = i;
821                 break;
822             }
823         }
824     }
825     if (tabToClose != -1)
826         emit q->tabCloseRequested(tabToClose);
827 }
828 
_q_scrollTabs()829 void QTabBarPrivate::_q_scrollTabs()
830 {
831     Q_Q(QTabBar);
832     const QObject *sender = q->sender();
833     const bool horizontal = !verticalTabs(shape);
834     const QRect scrollRect = normalizedScrollRect();
835     int i = -1;
836 
837     if (sender == leftB) {
838         for (i = tabList.count() - 1; i >= 0; --i) {
839             int start = horizontal ? tabList.at(i).rect.left() : tabList.at(i).rect.top();
840             if (start < scrollRect.left() + scrollOffset) {
841                 makeVisible(i);
842                 return;
843             }
844         }
845     } else if (sender == rightB) {
846         for (i = 0; i < tabList.count(); ++i) {
847             int end = horizontal ? tabList.at(i).rect.right() : tabList.at(i).rect.bottom();
848             if (end > scrollRect.right() + scrollOffset) {
849                 makeVisible(i);
850                 return;
851             }
852         }
853     }
854 }
855 
refresh()856 void QTabBarPrivate::refresh()
857 {
858     Q_Q(QTabBar);
859 
860     // be safe in case a subclass is also handling move with the tabs
861     if (pressedIndex != -1
862         && movable
863         && QGuiApplication::mouseButtons() == Qt::NoButton) {
864         moveTabFinished(pressedIndex);
865         if (!validIndex(pressedIndex))
866             pressedIndex = -1;
867     }
868 
869     if (!q->isVisible()) {
870         layoutDirty = true;
871     } else {
872         layoutTabs();
873         makeVisible(currentIndex);
874         q->update();
875         q->updateGeometry();
876     }
877 }
878 
879 /*!
880     Creates a new tab bar with the given \a parent.
881 */
QTabBar(QWidget * parent)882 QTabBar::QTabBar(QWidget* parent)
883     :QWidget(*new QTabBarPrivate, parent, { })
884 {
885     Q_D(QTabBar);
886     d->init();
887 }
888 
889 
890 /*!
891     Destroys the tab bar.
892 */
~QTabBar()893 QTabBar::~QTabBar()
894 {
895 }
896 
897 /*!
898     \property QTabBar::shape
899     \brief the shape of the tabs in the tab bar
900 
901     Possible values for this property are described by the Shape enum.
902 */
903 
904 
shape() const905 QTabBar::Shape QTabBar::shape() const
906 {
907     Q_D(const QTabBar);
908     return d->shape;
909 }
910 
setShape(Shape shape)911 void QTabBar::setShape(Shape shape)
912 {
913     Q_D(QTabBar);
914     if (d->shape == shape)
915         return;
916     d->shape = shape;
917     d->refresh();
918 }
919 
920 /*!
921     \property QTabBar::drawBase
922     \brief defines whether or not tab bar should draw its base.
923 
924     If true then QTabBar draws a base in relation to the styles overlab.
925     Otherwise only the tabs are drawn.
926 
927     \sa QStyle::pixelMetric(), QStyle::PM_TabBarBaseOverlap, QStyleOptionTabBarBase
928 */
929 
setDrawBase(bool drawBase)930 void QTabBar::setDrawBase(bool drawBase)
931 {
932     Q_D(QTabBar);
933     if (d->drawBase == drawBase)
934         return;
935     d->drawBase = drawBase;
936     update();
937 }
938 
drawBase() const939 bool QTabBar::drawBase() const
940 {
941     Q_D(const QTabBar);
942     return d->drawBase;
943 }
944 
945 /*!
946     Adds a new tab with text \a text. Returns the new
947     tab's index.
948 */
addTab(const QString & text)949 int QTabBar::addTab(const QString &text)
950 {
951     return insertTab(-1, text);
952 }
953 
954 /*!
955     \overload
956 
957     Adds a new tab with icon \a icon and text \a
958     text. Returns the new tab's index.
959 */
addTab(const QIcon & icon,const QString & text)960 int QTabBar::addTab(const QIcon& icon, const QString &text)
961 {
962     return insertTab(-1, icon, text);
963 }
964 
965 /*!
966     Inserts a new tab with text \a text at position \a index. If \a
967     index is out of range, the new tab is appened. Returns the new
968     tab's index.
969 */
insertTab(int index,const QString & text)970 int QTabBar::insertTab(int index, const QString &text)
971 {
972     return insertTab(index, QIcon(), text);
973 }
974 
975 /*!\overload
976 
977     Inserts a new tab with icon \a icon and text \a text at position
978     \a index. If \a index is out of range, the new tab is
979     appended. Returns the new tab's index.
980 
981     If the QTabBar was empty before this function is called, the inserted tab
982     becomes the current tab.
983 
984     Inserting a new tab at an index less than or equal to the current index
985     will increment the current index, but keep the current tab.
986 */
insertTab(int index,const QIcon & icon,const QString & text)987 int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
988 {
989     Q_D(QTabBar);
990     if (!d->validIndex(index)) {
991         index = d->tabList.count();
992         d->tabList.append(QTabBarPrivate::Tab(icon, text));
993     } else {
994         d->tabList.insert(index, QTabBarPrivate::Tab(icon, text));
995     }
996 #ifndef QT_NO_SHORTCUT
997     d->tabList[index].shortcutId = grabShortcut(QKeySequence::mnemonic(text));
998 #endif
999     d->firstVisible = qMax(qMin(index, d->firstVisible), 0);
1000     d->lastVisible  = qMax(index, d->lastVisible);
1001     d->refresh();
1002     if (d->tabList.count() == 1)
1003         setCurrentIndex(index);
1004     else if (index <= d->currentIndex) {
1005         ++d->currentIndex;
1006         ++d->lastVisible;
1007     }
1008 
1009     if (d->closeButtonOnTabs) {
1010         QStyleOptionTabV4 opt;
1011         initStyleOption(&opt, index);
1012         ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, this);
1013         QAbstractButton *closeButton = new CloseButton(this);
1014         connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
1015         setTabButton(index, closeSide, closeButton);
1016     }
1017 
1018     for (int i = 0; i < d->tabList.count(); ++i) {
1019         if (d->tabList[i].lastTab >= index)
1020             ++d->tabList[i].lastTab;
1021     }
1022 
1023     tabInserted(index);
1024     d->autoHideTabs();
1025     return index;
1026 }
1027 
1028 
1029 /*!
1030     Removes the tab at position \a index.
1031 
1032     \sa SelectionBehavior
1033  */
removeTab(int index)1034 void QTabBar::removeTab(int index)
1035 {
1036     Q_D(QTabBar);
1037     if (d->validIndex(index)) {
1038         if (d->dragInProgress)
1039             d->moveTabFinished(d->pressedIndex);
1040 
1041 #ifndef QT_NO_SHORTCUT
1042         releaseShortcut(d->tabList.at(index).shortcutId);
1043 #endif
1044         if (d->tabList[index].leftWidget) {
1045             d->tabList[index].leftWidget->hide();
1046             d->tabList[index].leftWidget->deleteLater();
1047             d->tabList[index].leftWidget = nullptr;
1048         }
1049         if (d->tabList[index].rightWidget) {
1050             d->tabList[index].rightWidget->hide();
1051             d->tabList[index].rightWidget->deleteLater();
1052             d->tabList[index].rightWidget = nullptr;
1053         }
1054 
1055         int newIndex = d->tabList[index].lastTab;
1056         d->tabList.removeAt(index);
1057         for (int i = 0; i < d->tabList.count(); ++i) {
1058             if (d->tabList[i].lastTab == index)
1059                 d->tabList[i].lastTab = -1;
1060             if (d->tabList[i].lastTab > index)
1061                 --d->tabList[i].lastTab;
1062         }
1063 
1064         d->calculateFirstLastVisible(index, false, true);
1065 
1066         if (index == d->currentIndex) {
1067             // The current tab is going away, in order to make sure
1068             // we emit that "current has changed", we need to reset this
1069             // around.
1070             d->currentIndex = -1;
1071             if (d->tabList.size() > 0) {
1072                 switch(d->selectionBehaviorOnRemove) {
1073                 case SelectPreviousTab:
1074                     if (newIndex > index)
1075                         newIndex--;
1076                     if (d->validIndex(newIndex) && d->tabList.at(newIndex).visible)
1077                         break;
1078                     Q_FALLTHROUGH();
1079                 case SelectRightTab:
1080                     newIndex = qBound(d->firstVisible, index, d->lastVisible);
1081                     break;
1082                 case SelectLeftTab:
1083                     newIndex = qBound(d->firstVisible, index-1, d->lastVisible);
1084                     if (newIndex < 0)
1085                         newIndex = 0;
1086                     break;
1087                 default:
1088                     break;
1089                 }
1090 
1091                 if (d->validIndex(newIndex)) {
1092                     // don't loose newIndex's old through setCurrentIndex
1093                     int bump = d->tabList[newIndex].lastTab;
1094                     setCurrentIndex(newIndex);
1095                     d->tabList[newIndex].lastTab = bump;
1096                 }
1097             } else {
1098                 emit currentChanged(-1);
1099             }
1100         } else if (index < d->currentIndex) {
1101             setCurrentIndex(d->currentIndex - 1);
1102         }
1103         d->refresh();
1104         d->autoHideTabs();
1105         if (!d->hoverRect.isEmpty()) {
1106             for (int i = 0; i < d->tabList.count(); ++i) {
1107                 const QRect area = tabRect(i);
1108                 if (area.contains(mapFromGlobal(QCursor::pos()))) {
1109                     d->hoverIndex = i;
1110                     d->hoverRect = area;
1111                     break;
1112                 }
1113             }
1114             update(d->hoverRect);
1115         }
1116         tabRemoved(index);
1117     }
1118 }
1119 
1120 
1121 /*!
1122     Returns \c true if the tab at position \a index is enabled; otherwise
1123     returns \c false.
1124 */
isTabEnabled(int index) const1125 bool QTabBar::isTabEnabled(int index) const
1126 {
1127     Q_D(const QTabBar);
1128     if (const QTabBarPrivate::Tab *tab = d->at(index))
1129         return tab->enabled;
1130     return false;
1131 }
1132 
1133 /*!
1134     If \a enabled is true then the tab at position \a index is
1135     enabled; otherwise the item at position \a index is disabled.
1136 */
setTabEnabled(int index,bool enabled)1137 void QTabBar::setTabEnabled(int index, bool enabled)
1138 {
1139     Q_D(QTabBar);
1140     if (QTabBarPrivate::Tab *tab = d->at(index)) {
1141         tab->enabled = enabled;
1142 #ifndef QT_NO_SHORTCUT
1143         setShortcutEnabled(tab->shortcutId, enabled);
1144 #endif
1145         update();
1146         if (!enabled && index == d->currentIndex)
1147             setCurrentIndex(d->selectNewCurrentIndexFrom(index+1));
1148         else if (enabled && !isTabVisible(d->currentIndex))
1149             setCurrentIndex(d->selectNewCurrentIndexFrom(index));
1150     }
1151 }
1152 
1153 
1154 /*!
1155     Returns true if the tab at position \a index is visible; otherwise
1156     returns false.
1157     \since 5.15
1158 */
isTabVisible(int index) const1159 bool QTabBar::isTabVisible(int index) const
1160 {
1161     Q_D(const QTabBar);
1162     if (d->validIndex(index))
1163         return d->tabList.at(index).visible;
1164     return false;
1165 }
1166 
1167 /*!
1168     If \a visible is true, make the tab at position \a index visible,
1169     otherwise make it hidden.
1170     \since 5.15
1171 */
setTabVisible(int index,bool visible)1172 void QTabBar::setTabVisible(int index, bool visible)
1173 {
1174     Q_D(QTabBar);
1175     if (QTabBarPrivate::Tab *tab = d->at(index)) {
1176         d->layoutDirty = (visible != tab->visible);
1177         if (!d->layoutDirty)
1178             return;
1179         tab->visible = visible;
1180         if (tab->leftWidget)
1181             tab->leftWidget->setVisible(visible);
1182         if (tab->rightWidget)
1183             tab->rightWidget->setVisible(visible);
1184 #ifndef QT_NO_SHORTCUT
1185         setShortcutEnabled(tab->shortcutId, visible);
1186 #endif
1187         d->calculateFirstLastVisible(index, visible, false);
1188         if (!visible && index == d->currentIndex) {
1189             const int newindex = d->selectNewCurrentIndexFrom(index+1);
1190             setCurrentIndex(newindex);
1191         }
1192         update();
1193     }
1194 }
1195 
1196 
1197 /*!
1198     Returns the text of the tab at position \a index, or an empty
1199     string if \a index is out of range.
1200 */
tabText(int index) const1201 QString QTabBar::tabText(int index) const
1202 {
1203     Q_D(const QTabBar);
1204     if (const QTabBarPrivate::Tab *tab = d->at(index))
1205         return tab->text;
1206     return QString();
1207 }
1208 
1209 /*!
1210     Sets the text of the tab at position \a index to \a text.
1211 */
setTabText(int index,const QString & text)1212 void QTabBar::setTabText(int index, const QString &text)
1213 {
1214     Q_D(QTabBar);
1215     if (QTabBarPrivate::Tab *tab = d->at(index)) {
1216         d->textSizes.remove(tab->text);
1217         tab->text = text;
1218 #ifndef QT_NO_SHORTCUT
1219         releaseShortcut(tab->shortcutId);
1220         tab->shortcutId = grabShortcut(QKeySequence::mnemonic(text));
1221         setShortcutEnabled(tab->shortcutId, tab->enabled);
1222 #endif
1223         d->refresh();
1224     }
1225 }
1226 
1227 /*!
1228     Returns the text color of the tab with the given \a index, or a invalid
1229     color if \a index is out of range.
1230 
1231     \sa setTabTextColor()
1232 */
tabTextColor(int index) const1233 QColor QTabBar::tabTextColor(int index) const
1234 {
1235     Q_D(const QTabBar);
1236     if (const QTabBarPrivate::Tab *tab = d->at(index))
1237         return tab->textColor;
1238     return QColor();
1239 }
1240 
1241 /*!
1242     Sets the color of the text in the tab with the given \a index to the specified \a color.
1243 
1244     If an invalid color is specified, the tab will use the QTabBar foreground role instead.
1245 
1246     \sa tabTextColor()
1247 */
setTabTextColor(int index,const QColor & color)1248 void QTabBar::setTabTextColor(int index, const QColor &color)
1249 {
1250     Q_D(QTabBar);
1251     if (QTabBarPrivate::Tab *tab = d->at(index)) {
1252         tab->textColor = color;
1253         update(tabRect(index));
1254     }
1255 }
1256 
1257 /*!
1258     Returns the icon of the tab at position \a index, or a null icon
1259     if \a index is out of range.
1260 */
tabIcon(int index) const1261 QIcon QTabBar::tabIcon(int index) const
1262 {
1263     Q_D(const QTabBar);
1264     if (const QTabBarPrivate::Tab *tab = d->at(index))
1265         return tab->icon;
1266     return QIcon();
1267 }
1268 
1269 /*!
1270     Sets the icon of the tab at position \a index to \a icon.
1271 */
setTabIcon(int index,const QIcon & icon)1272 void QTabBar::setTabIcon(int index, const QIcon & icon)
1273 {
1274     Q_D(QTabBar);
1275     if (QTabBarPrivate::Tab *tab = d->at(index)) {
1276         bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
1277         tab->icon = icon;
1278         if (simpleIconChange)
1279             update(tabRect(index));
1280         else
1281             d->refresh();
1282     }
1283 }
1284 
1285 #ifndef QT_NO_TOOLTIP
1286 /*!
1287     Sets the tool tip of the tab at position \a index to \a tip.
1288 */
setTabToolTip(int index,const QString & tip)1289 void QTabBar::setTabToolTip(int index, const QString & tip)
1290 {
1291     Q_D(QTabBar);
1292     if (QTabBarPrivate::Tab *tab = d->at(index))
1293         tab->toolTip = tip;
1294 }
1295 
1296 /*!
1297     Returns the tool tip of the tab at position \a index, or an empty
1298     string if \a index is out of range.
1299 */
tabToolTip(int index) const1300 QString QTabBar::tabToolTip(int index) const
1301 {
1302     Q_D(const QTabBar);
1303     if (const QTabBarPrivate::Tab *tab = d->at(index))
1304         return tab->toolTip;
1305     return QString();
1306 }
1307 #endif // QT_NO_TOOLTIP
1308 
1309 #if QT_CONFIG(whatsthis)
1310 /*!
1311     \since 4.1
1312 
1313     Sets the What's This help text of the tab at position \a index
1314     to \a text.
1315 */
setTabWhatsThis(int index,const QString & text)1316 void QTabBar::setTabWhatsThis(int index, const QString &text)
1317 {
1318     Q_D(QTabBar);
1319     if (QTabBarPrivate::Tab *tab = d->at(index))
1320         tab->whatsThis = text;
1321 }
1322 
1323 /*!
1324     \since 4.1
1325 
1326     Returns the What's This help text of the tab at position \a index,
1327     or an empty string if \a index is out of range.
1328 */
tabWhatsThis(int index) const1329 QString QTabBar::tabWhatsThis(int index) const
1330 {
1331     Q_D(const QTabBar);
1332     if (const QTabBarPrivate::Tab *tab = d->at(index))
1333         return tab->whatsThis;
1334     return QString();
1335 }
1336 
1337 #endif // QT_CONFIG(whatsthis)
1338 
1339 /*!
1340     Sets the data of the tab at position \a index to \a data.
1341 */
setTabData(int index,const QVariant & data)1342 void QTabBar::setTabData(int index, const QVariant & data)
1343 {
1344     Q_D(QTabBar);
1345     if (QTabBarPrivate::Tab *tab = d->at(index))
1346         tab->data = data;
1347 }
1348 
1349 /*!
1350     Returns the data of the tab at position \a index, or a null
1351     variant if \a index is out of range.
1352 */
tabData(int index) const1353 QVariant QTabBar::tabData(int index) const
1354 {
1355     Q_D(const QTabBar);
1356     if (const QTabBarPrivate::Tab *tab = d->at(index))
1357         return tab->data;
1358     return QVariant();
1359 }
1360 
1361 /*!
1362     Returns the visual rectangle of the tab at position \a
1363     index, or a null rectangle if \a index is hidden, or out of range.
1364 */
tabRect(int index) const1365 QRect QTabBar::tabRect(int index) const
1366 {
1367     Q_D(const QTabBar);
1368     if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1369         if (d->layoutDirty)
1370             const_cast<QTabBarPrivate*>(d)->layoutTabs();
1371         if (!tab->visible)
1372             return QRect();
1373         QRect r = tab->rect;
1374         if (verticalTabs(d->shape))
1375             r.translate(0, -d->scrollOffset);
1376         else
1377             r.translate(-d->scrollOffset, 0);
1378         if (!verticalTabs(d->shape))
1379             r = QStyle::visualRect(layoutDirection(), rect(), r);
1380         return r;
1381     }
1382     return QRect();
1383 }
1384 
1385 /*!
1386     \since 4.3
1387     Returns the index of the tab that covers \a position or -1 if no
1388     tab covers \a position;
1389 */
1390 
tabAt(const QPoint & position) const1391 int QTabBar::tabAt(const QPoint &position) const
1392 {
1393     Q_D(const QTabBar);
1394     if (d->validIndex(d->currentIndex)
1395         && tabRect(d->currentIndex).contains(position)) {
1396         return d->currentIndex;
1397     }
1398     const int max = d->tabList.size();
1399     for (int i = 0; i < max; ++i) {
1400         if (tabRect(i).contains(position)) {
1401             return i;
1402         }
1403     }
1404     return -1;
1405 }
1406 
1407 /*!
1408     \property QTabBar::currentIndex
1409     \brief the index of the tab bar's visible tab
1410 
1411     The current index is -1 if there is no current tab.
1412 */
1413 
currentIndex() const1414 int QTabBar::currentIndex() const
1415 {
1416     Q_D(const QTabBar);
1417     if (d->validIndex(d->currentIndex))
1418         return d->currentIndex;
1419     return -1;
1420 }
1421 
1422 
setCurrentIndex(int index)1423 void QTabBar::setCurrentIndex(int index)
1424 {
1425     Q_D(QTabBar);
1426     if (d->dragInProgress && d->pressedIndex != -1)
1427         return;
1428 
1429     int oldIndex = d->currentIndex;
1430     if (d->validIndex(index) && d->currentIndex != index) {
1431         d->currentIndex = index;
1432         update();
1433         d->makeVisible(index);
1434         d->tabList[index].lastTab = oldIndex;
1435         if (oldIndex >= 0 && oldIndex < count())
1436             d->layoutTab(oldIndex);
1437         d->layoutTab(index);
1438 #ifndef QT_NO_ACCESSIBILITY
1439         if (QAccessible::isActive()) {
1440             if (hasFocus()) {
1441                 QAccessibleEvent focusEvent(this, QAccessible::Focus);
1442                 focusEvent.setChild(index);
1443                 QAccessible::updateAccessibility(&focusEvent);
1444             }
1445             QAccessibleEvent selectionEvent(this, QAccessible::Selection);
1446             selectionEvent.setChild(index);
1447             QAccessible::updateAccessibility(&selectionEvent);
1448         }
1449 #endif
1450         emit currentChanged(index);
1451     }
1452 }
1453 
1454 /*!
1455     \property QTabBar::iconSize
1456     \brief The size for icons in the tab bar
1457     \since 4.1
1458 
1459     The default value is style-dependent. \c iconSize is a maximum
1460     size; icons that are smaller are not scaled up.
1461 
1462     \sa QTabWidget::iconSize
1463 */
iconSize() const1464 QSize QTabBar::iconSize() const
1465 {
1466     Q_D(const QTabBar);
1467     if (d->iconSize.isValid())
1468         return d->iconSize;
1469     int iconExtent = style()->pixelMetric(QStyle::PM_TabBarIconSize, nullptr, this);
1470     return QSize(iconExtent, iconExtent);
1471 
1472 }
1473 
setIconSize(const QSize & size)1474 void QTabBar::setIconSize(const QSize &size)
1475 {
1476     Q_D(QTabBar);
1477     d->iconSize = size;
1478     d->layoutDirty = true;
1479     update();
1480     updateGeometry();
1481 }
1482 
1483 /*!
1484     \property QTabBar::count
1485     \brief the number of tabs in the tab bar
1486 */
1487 
count() const1488 int QTabBar::count() const
1489 {
1490     Q_D(const QTabBar);
1491     return d->tabList.count();
1492 }
1493 
1494 
1495 /*!\reimp
1496  */
sizeHint() const1497 QSize QTabBar::sizeHint() const
1498 {
1499     Q_D(const QTabBar);
1500     if (d->layoutDirty)
1501         const_cast<QTabBarPrivate*>(d)->layoutTabs();
1502     QRect r;
1503     for (int i = 0; i < d->tabList.count(); ++i) {
1504         if (d->tabList.at(i).visible)
1505             r = r.united(d->tabList.at(i).maxRect);
1506     }
1507     QSize sz = QApplication::globalStrut();
1508     return r.size().expandedTo(sz);
1509 }
1510 
1511 /*!\reimp
1512  */
minimumSizeHint() const1513 QSize QTabBar::minimumSizeHint() const
1514 {
1515     Q_D(const QTabBar);
1516     if (d->layoutDirty)
1517         const_cast<QTabBarPrivate*>(d)->layoutTabs();
1518     if (!d->useScrollButtons) {
1519         QRect r;
1520         for (int i = 0; i < d->tabList.count(); ++i) {
1521             if (d->tabList.at(i).visible)
1522                 r = r.united(d->tabList.at(i).minRect);
1523         }
1524         return r.size().expandedTo(QApplication::globalStrut());
1525     }
1526     if (verticalTabs(d->shape))
1527         return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
1528     else
1529         return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
1530 }
1531 
1532 // Compute the most-elided possible text, for minimumSizeHint
computeElidedText(Qt::TextElideMode mode,const QString & text)1533 static QString computeElidedText(Qt::TextElideMode mode, const QString &text)
1534 {
1535     if (text.length() <= 3)
1536         return text;
1537 
1538     static const QLatin1String Ellipses("...");
1539     QString ret;
1540     switch (mode) {
1541     case Qt::ElideRight:
1542         ret = text.leftRef(2) + Ellipses;
1543         break;
1544     case Qt::ElideMiddle:
1545         ret = text.leftRef(1) + Ellipses + text.rightRef(1);
1546         break;
1547     case Qt::ElideLeft:
1548         ret = Ellipses + text.rightRef(2);
1549         break;
1550     case Qt::ElideNone:
1551         ret = text;
1552         break;
1553     }
1554     return ret;
1555 }
1556 
1557 /*!
1558     Returns the minimum tab size hint for the tab at position \a index.
1559     \since 5.0
1560 */
1561 
minimumTabSizeHint(int index) const1562 QSize QTabBar::minimumTabSizeHint(int index) const
1563 {
1564     Q_D(const QTabBar);
1565     QTabBarPrivate::Tab &tab = const_cast<QTabBarPrivate::Tab&>(d->tabList[index]);
1566     QString oldText = tab.text;
1567     tab.text = computeElidedText(d->elideMode, oldText);
1568     QSize size = tabSizeHint(index);
1569     tab.text = oldText;
1570     return size;
1571 }
1572 
1573 /*!
1574     Returns the size hint for the tab at position \a index.
1575 */
tabSizeHint(int index) const1576 QSize QTabBar::tabSizeHint(int index) const
1577 {
1578     //Note: this must match with the computations in QCommonStylePrivate::tabLayout
1579     Q_D(const QTabBar);
1580     if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1581         QStyleOptionTabV4 opt;
1582         d->initBasicStyleOption(&opt, index);
1583         opt.text = d->tabList.at(index).text;
1584         QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1585         int hframe = style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &opt, this);
1586         int vframe = style()->pixelMetric(QStyle::PM_TabBarTabVSpace, &opt, this);
1587         const QFontMetrics fm = fontMetrics();
1588 
1589         int maxWidgetHeight = qMax(opt.leftButtonSize.height(), opt.rightButtonSize.height());
1590         int maxWidgetWidth = qMax(opt.leftButtonSize.width(), opt.rightButtonSize.width());
1591 
1592         int widgetWidth = 0;
1593         int widgetHeight = 0;
1594         int padding = 0;
1595         if (!opt.leftButtonSize.isEmpty()) {
1596             padding += 4;
1597             widgetWidth += opt.leftButtonSize.width();
1598             widgetHeight += opt.leftButtonSize.height();
1599         }
1600         if (!opt.rightButtonSize.isEmpty()) {
1601             padding += 4;
1602             widgetWidth += opt.rightButtonSize.width();
1603             widgetHeight += opt.rightButtonSize.height();
1604         }
1605         if (!opt.icon.isNull())
1606             padding += 4;
1607 
1608         QHash<QString, QSize>::iterator it = d->textSizes.find(tab->text);
1609         if (it == d->textSizes.end())
1610            it = d->textSizes.insert(tab->text, fm.size(Qt::TextShowMnemonic, tab->text));
1611         const int textWidth = it.value().width();
1612         QSize csz;
1613         if (verticalTabs(d->shape)) {
1614             csz = QSize( qMax(maxWidgetWidth, qMax(fm.height(), iconSize.height())) + vframe,
1615                          textWidth + iconSize.width() + hframe + widgetHeight + padding);
1616         } else {
1617             csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
1618                   qMax(maxWidgetHeight, qMax(fm.height(), iconSize.height())) + vframe);
1619         }
1620 
1621         QSize retSize = style()->sizeFromContents(QStyle::CT_TabBarTab, &opt, csz, this);
1622         return retSize;
1623     }
1624     return QSize();
1625 }
1626 
1627 /*!
1628   This virtual handler is called after a new tab was added or
1629   inserted at position \a index.
1630 
1631   \sa tabRemoved()
1632  */
tabInserted(int index)1633 void QTabBar::tabInserted(int index)
1634 {
1635     Q_UNUSED(index)
1636 }
1637 
1638 /*!
1639   This virtual handler is called after a tab was removed from
1640   position \a index.
1641 
1642   \sa tabInserted()
1643  */
tabRemoved(int index)1644 void QTabBar::tabRemoved(int index)
1645 {
1646     Q_UNUSED(index)
1647 }
1648 
1649 /*!
1650   This virtual handler is called whenever the tab layout changes.
1651 
1652   \sa tabRect()
1653  */
tabLayoutChange()1654 void QTabBar::tabLayoutChange()
1655 {
1656 }
1657 
1658 
1659 /*!\reimp
1660  */
showEvent(QShowEvent *)1661 void QTabBar::showEvent(QShowEvent *)
1662 {
1663     Q_D(QTabBar);
1664     if (d->layoutDirty)
1665         d->refresh();
1666     if (!d->validIndex(d->currentIndex))
1667         setCurrentIndex(0);
1668     d->updateMacBorderMetrics();
1669 }
1670 
1671 /*!\reimp
1672  */
hideEvent(QHideEvent *)1673 void QTabBar::hideEvent(QHideEvent *)
1674 {
1675     Q_D(QTabBar);
1676     d->updateMacBorderMetrics();
1677 }
1678 
1679 /*!\reimp
1680  */
event(QEvent * event)1681 bool QTabBar::event(QEvent *event)
1682 {
1683     Q_D(QTabBar);
1684     if (event->type() == QEvent::HoverMove
1685         || event->type() == QEvent::HoverEnter) {
1686         QHoverEvent *he = static_cast<QHoverEvent *>(event);
1687         if (!d->hoverRect.contains(he->pos())) {
1688             QRect oldHoverRect = d->hoverRect;
1689             bool cursorOverTabs = false;
1690             for (int i = 0; i < d->tabList.count(); ++i) {
1691                 QRect area = tabRect(i);
1692                 if (area.contains(he->pos())) {
1693                     d->hoverIndex = i;
1694                     d->hoverRect = area;
1695                     cursorOverTabs = true;
1696                     break;
1697                 }
1698             }
1699             if (!cursorOverTabs) {
1700                 d->hoverIndex = -1;
1701                 d->hoverRect = QRect();
1702             }
1703             if (he->oldPos() != QPoint(-1, -1))
1704                 update(oldHoverRect);
1705             update(d->hoverRect);
1706         }
1707         return true;
1708     } else if (event->type() == QEvent::HoverLeave) {
1709         QRect oldHoverRect = d->hoverRect;
1710         d->hoverIndex = -1;
1711         d->hoverRect = QRect();
1712         update(oldHoverRect);
1713         return true;
1714 #ifndef QT_NO_TOOLTIP
1715     } else if (event->type() == QEvent::ToolTip) {
1716         if (const QTabBarPrivate::Tab *tab = d->at(tabAt(static_cast<QHelpEvent*>(event)->pos()))) {
1717             if (!tab->toolTip.isEmpty()) {
1718                 QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), tab->toolTip, this);
1719                 return true;
1720             }
1721         }
1722 #endif // QT_NO_TOOLTIP
1723 #if QT_CONFIG(whatsthis)
1724     } else if (event->type() == QEvent::QueryWhatsThis) {
1725         const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()));
1726         if (!tab || tab->whatsThis.isEmpty())
1727             event->ignore();
1728         return true;
1729     } else if (event->type() == QEvent::WhatsThis) {
1730         if (const QTabBarPrivate::Tab *tab = d->at(d->indexAtPos(static_cast<QHelpEvent*>(event)->pos()))) {
1731             if (!tab->whatsThis.isEmpty()) {
1732                 QWhatsThis::showText(static_cast<QHelpEvent*>(event)->globalPos(),
1733                                      tab->whatsThis, this);
1734                 return true;
1735             }
1736         }
1737 #endif // QT_CONFIG(whatsthis)
1738 #ifndef QT_NO_SHORTCUT
1739     } else if (event->type() == QEvent::Shortcut) {
1740         QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
1741         for (int i = 0; i < d->tabList.count(); ++i) {
1742             const QTabBarPrivate::Tab *tab = &d->tabList.at(i);
1743             if (tab->shortcutId == se->shortcutId()) {
1744                 setCurrentIndex(i);
1745                 return true;
1746             }
1747         }
1748 #endif
1749     } else if (event->type() == QEvent::MouseButtonDblClick) { // ### fixme Qt 6: move to mouseDoubleClickEvent(), here for BC reasons.
1750         const QPoint pos = static_cast<const QMouseEvent *>(event)->pos();
1751         const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
1752                                             || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
1753         if (!isEventInCornerButtons)
1754             emit tabBarDoubleClicked(tabAt(pos));
1755     } else if (event->type() == QEvent::Move) {
1756         d->updateMacBorderMetrics();
1757         return QWidget::event(event);
1758 
1759 #if QT_CONFIG(draganddrop)
1760     } else if (event->type() == QEvent::DragEnter) {
1761         if (d->changeCurrentOnDrag)
1762             event->accept();
1763     } else if (event->type() == QEvent::DragMove) {
1764         if (d->changeCurrentOnDrag) {
1765             const int tabIndex = tabAt(static_cast<QDragMoveEvent *>(event)->pos());
1766             if (isTabEnabled(tabIndex) && d->switchTabCurrentIndex != tabIndex) {
1767                 d->switchTabCurrentIndex = tabIndex;
1768                 if (d->switchTabTimerId)
1769                     killTimer(d->switchTabTimerId);
1770                 d->switchTabTimerId = startTimer(style()->styleHint(QStyle::SH_TabBar_ChangeCurrentDelay));
1771             }
1772             event->ignore();
1773         }
1774     } else if (event->type() == QEvent::DragLeave || event->type() == QEvent::Drop) {
1775         d->killSwitchTabTimer();
1776         event->ignore();
1777 #endif
1778     }
1779     return QWidget::event(event);
1780 }
1781 
1782 /*!\reimp
1783  */
resizeEvent(QResizeEvent *)1784 void QTabBar::resizeEvent(QResizeEvent *)
1785 {
1786     Q_D(QTabBar);
1787     if (d->layoutDirty)
1788         updateGeometry();
1789     d->layoutTabs();
1790 
1791     d->makeVisible(d->currentIndex);
1792 }
1793 
1794 /*!\reimp
1795  */
paintEvent(QPaintEvent *)1796 void QTabBar::paintEvent(QPaintEvent *)
1797 {
1798     Q_D(QTabBar);
1799 
1800     QStyleOptionTabBarBase optTabBase;
1801     QTabBarPrivate::initStyleBaseOption(&optTabBase, this, size());
1802 
1803     QStylePainter p(this);
1804     int selected = -1;
1805     int cutLeft = -1;
1806     int cutRight = -1;
1807     bool vertical = verticalTabs(d->shape);
1808     QStyleOptionTab cutTabLeft;
1809     QStyleOptionTab cutTabRight;
1810     selected = d->currentIndex;
1811     if (d->dragInProgress)
1812         selected = d->pressedIndex;
1813     const QRect scrollRect = d->normalizedScrollRect();
1814 
1815     for (int i = 0; i < d->tabList.count(); ++i)
1816          optTabBase.tabBarRect |= tabRect(i);
1817 
1818     optTabBase.selectedTabRect = tabRect(selected);
1819 
1820     if (d->drawBase)
1821         p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
1822 
1823     for (int i = 0; i < d->tabList.count(); ++i) {
1824         if (!d->at(i)->visible)
1825             continue;
1826         QStyleOptionTabV4 tab;
1827         initStyleOption(&tab, i);
1828         if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) {
1829             if (vertical) {
1830                 tab.rect.moveTop(tab.rect.y() + d->tabList[i].dragOffset);
1831             } else {
1832                 tab.rect.moveLeft(tab.rect.x() + d->tabList[i].dragOffset);
1833             }
1834         }
1835         if (!(tab.state & QStyle::State_Enabled)) {
1836             tab.palette.setCurrentColorGroup(QPalette::Disabled);
1837         }
1838 
1839         // If this tab is partially obscured, make a note of it so that we can pass the information
1840         // along when we draw the tear.
1841         QRect tabRect = d->tabList[i].rect;
1842         int tabStart = vertical ? tabRect.top() : tabRect.left();
1843         int tabEnd = vertical ? tabRect.bottom() : tabRect.right();
1844         if (tabStart < scrollRect.left() + d->scrollOffset) {
1845             cutLeft = i;
1846             cutTabLeft = tab;
1847         } else if (tabEnd > scrollRect.right() + d->scrollOffset) {
1848             cutRight = i;
1849             cutTabRight = tab;
1850         }
1851 
1852         // Don't bother drawing a tab if the entire tab is outside of the visible tab bar.
1853         if ((!vertical && (tab.rect.right() < 0 || tab.rect.left() > width()))
1854             || (vertical && (tab.rect.bottom() < 0 || tab.rect.top() > height())))
1855             continue;
1856 
1857         optTabBase.tabBarRect |= tab.rect;
1858         if (i == selected)
1859             continue;
1860 
1861         p.drawControl(QStyle::CE_TabBarTab, tab);
1862     }
1863 
1864     // Draw the selected tab last to get it "on top"
1865     if (selected >= 0) {
1866         QStyleOptionTabV4 tab;
1867         initStyleOption(&tab, selected);
1868         if (d->paintWithOffsets && d->tabList[selected].dragOffset != 0) {
1869             if (vertical)
1870                 tab.rect.moveTop(tab.rect.y() + d->tabList[selected].dragOffset);
1871             else
1872                 tab.rect.moveLeft(tab.rect.x() + d->tabList[selected].dragOffset);
1873         }
1874         if (!d->dragInProgress)
1875             p.drawControl(QStyle::CE_TabBarTab, tab);
1876         else {
1877             int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, nullptr, this);
1878             if (verticalTabs(d->shape))
1879                 d->movingTab->setGeometry(tab.rect.adjusted(0, -taboverlap, 0, taboverlap));
1880             else
1881                 d->movingTab->setGeometry(tab.rect.adjusted(-taboverlap, 0, taboverlap, 0));
1882         }
1883     }
1884 
1885     // Only draw the tear indicator if necessary. Most of the time we don't need too.
1886     if (d->leftB->isVisible() && cutLeft >= 0) {
1887         cutTabLeft.rect = rect();
1888         cutTabLeft.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorLeft, &cutTabLeft, this);
1889         p.drawPrimitive(QStyle::PE_IndicatorTabTearLeft, cutTabLeft);
1890     }
1891 
1892     if (d->rightB->isVisible() && cutRight >= 0) {
1893         cutTabRight.rect = rect();
1894         cutTabRight.rect = style()->subElementRect(QStyle::SE_TabBarTearIndicatorRight, &cutTabRight, this);
1895         p.drawPrimitive(QStyle::PE_IndicatorTabTearRight, cutTabRight);
1896     }
1897 }
1898 
1899 /*
1900     When index changes visibility, we have to find first & last visible indexes.
1901     If remove is set, we force both
1902  */
calculateFirstLastVisible(int index,bool visible,bool remove)1903 void QTabBarPrivate::calculateFirstLastVisible(int index, bool visible, bool remove)
1904 {
1905     if (visible) {
1906         firstVisible = qMin(index, firstVisible);
1907         lastVisible  = qMax(index, lastVisible);
1908     } else {
1909         if (remove || (index == firstVisible)) {
1910             firstVisible = -1;
1911             for (int i = 0; i < tabList.count(); ++i) {
1912                 if (tabList.at(i).visible) {
1913                     firstVisible = i;
1914                     break;
1915                 }
1916             }
1917             if (firstVisible < 0)
1918                 firstVisible = 0;
1919         }
1920         if (remove || (index == lastVisible)) {
1921             lastVisible = -1;
1922             for (int i = tabList.count() - 1; i >= 0; --i) {
1923                 if (tabList.at(i).visible) {
1924                     lastVisible = i;
1925                     break;
1926                 }
1927             }
1928         }
1929     }
1930 }
1931 
1932 /*
1933     Selects the new current index starting at "fromIndex". If "fromIndex" is visible we're done.
1934     Else it tries any index AFTER fromIndex, then any BEFORE fromIndex and, if everything fails,
1935     it returns -1 indicating that no index is available
1936  */
selectNewCurrentIndexFrom(int fromIndex)1937 int QTabBarPrivate::selectNewCurrentIndexFrom(int fromIndex)
1938 {
1939     int newindex = -1;
1940     for (int i = fromIndex; i < tabList.count(); ++i) {
1941         if (at(i)->visible && at(i)->enabled) {
1942           newindex = i;
1943           break;
1944         }
1945     }
1946     if (newindex < 0) {
1947         for (int i = fromIndex-1; i > -1; --i) {
1948             if (at(i)->visible && at(i)->enabled) {
1949               newindex = i;
1950               break;
1951             }
1952         }
1953     }
1954 
1955     return newindex;
1956 }
1957 
1958 /*
1959     Given that index at position from moved to position to where return where index goes.
1960  */
calculateNewPosition(int from,int to,int index) const1961 int QTabBarPrivate::calculateNewPosition(int from, int to, int index) const
1962 {
1963     if (index == from)
1964         return to;
1965 
1966     int start = qMin(from, to);
1967     int end = qMax(from, to);
1968     if (index >= start && index <= end)
1969         index += (from < to) ? -1 : 1;
1970     return index;
1971 }
1972 
1973 /*!
1974     Moves the item at index position \a from to index position \a to.
1975     \since 4.5
1976 
1977     \sa tabMoved(), tabLayoutChange()
1978  */
moveTab(int from,int to)1979 void QTabBar::moveTab(int from, int to)
1980 {
1981     Q_D(QTabBar);
1982     if (from == to
1983         || !d->validIndex(from)
1984         || !d->validIndex(to))
1985         return;
1986 
1987     bool vertical = verticalTabs(d->shape);
1988     int oldPressedPosition = 0;
1989     if (d->pressedIndex != -1) {
1990         // Record the position of the pressed tab before reordering the tabs.
1991         oldPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.y()
1992                              : d->tabList[d->pressedIndex].rect.x();
1993     }
1994 
1995     // Update the locations of the tabs first
1996     int start = qMin(from, to);
1997     int end = qMax(from, to);
1998     int width = vertical ? d->tabList[from].rect.height() : d->tabList[from].rect.width();
1999     if (from < to)
2000         width *= -1;
2001     bool rtl = isRightToLeft();
2002     for (int i = start; i <= end; ++i) {
2003         if (i == from)
2004             continue;
2005         if (vertical)
2006             d->tabList[i].rect.moveTop(d->tabList[i].rect.y() + width);
2007         else
2008             d->tabList[i].rect.moveLeft(d->tabList[i].rect.x() + width);
2009         int direction = -1;
2010         if (rtl && !vertical)
2011             direction *= -1;
2012         if (d->tabList[i].dragOffset != 0)
2013             d->tabList[i].dragOffset += (direction * width);
2014     }
2015 
2016     if (vertical) {
2017         if (from < to)
2018             d->tabList[from].rect.moveTop(d->tabList[to].rect.bottom() + 1);
2019         else
2020             d->tabList[from].rect.moveTop(d->tabList[to].rect.top() - width);
2021     } else {
2022         if (from < to)
2023             d->tabList[from].rect.moveLeft(d->tabList[to].rect.right() + 1);
2024         else
2025             d->tabList[from].rect.moveLeft(d->tabList[to].rect.left() - width);
2026     }
2027 
2028     // Move the actual data structures
2029     d->tabList.move(from, to);
2030 
2031     // update lastTab locations
2032     for (int i = 0; i < d->tabList.count(); ++i)
2033         d->tabList[i].lastTab = d->calculateNewPosition(from, to, d->tabList[i].lastTab);
2034 
2035     // update external variables
2036     int previousIndex = d->currentIndex;
2037     d->currentIndex = d->calculateNewPosition(from, to, d->currentIndex);
2038 
2039     // If we are in the middle of a drag update the dragStartPosition
2040     if (d->pressedIndex != -1) {
2041         d->pressedIndex = d->calculateNewPosition(from, to, d->pressedIndex);
2042         int newPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.top() : d->tabList[d->pressedIndex].rect.left();
2043         int diff = oldPressedPosition - newPressedPosition;
2044         if (isRightToLeft() && !vertical)
2045             diff *= -1;
2046         if (vertical)
2047             d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
2048         else
2049             d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
2050     }
2051 
2052     d->layoutWidgets(start);
2053     update();
2054     emit tabMoved(from, to);
2055     if (previousIndex != d->currentIndex)
2056         emit currentChanged(d->currentIndex);
2057     emit tabLayoutChange();
2058 }
2059 
slide(int from,int to)2060 void QTabBarPrivate::slide(int from, int to)
2061 {
2062     Q_Q(QTabBar);
2063     if (from == to
2064             || !validIndex(from)
2065             || !validIndex(to))
2066         return;
2067     bool vertical = verticalTabs(shape);
2068     int preLocation = vertical ? q->tabRect(from).y() : q->tabRect(from).x();
2069     q->setUpdatesEnabled(false);
2070     q->moveTab(from, to);
2071     q->setUpdatesEnabled(true);
2072     int postLocation = vertical ? q->tabRect(to).y() : q->tabRect(to).x();
2073     int length = postLocation - preLocation;
2074     tabList[to].dragOffset -= length;
2075     tabList[to].startAnimation(this, ANIMATION_DURATION);
2076 }
2077 
moveTab(int index,int offset)2078 void QTabBarPrivate::moveTab(int index, int offset)
2079 {
2080     if (!validIndex(index))
2081         return;
2082     tabList[index].dragOffset = offset;
2083     layoutTab(index); // Make buttons follow tab
2084     q_func()->update();
2085 }
2086 
2087 /*!\reimp
2088 */
mousePressEvent(QMouseEvent * event)2089 void QTabBar::mousePressEvent(QMouseEvent *event)
2090 {
2091     Q_D(QTabBar);
2092 
2093     const QPoint pos = event->pos();
2094     const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(pos))
2095                                      || (!d->rightB->isHidden() && d->rightB->geometry().contains(pos));
2096     if (!isEventInCornerButtons) {
2097         const int index = d->indexAtPos(pos);
2098         emit tabBarClicked(index);
2099     }
2100 
2101     if (event->button() != Qt::LeftButton) {
2102         event->ignore();
2103         return;
2104     }
2105     // Be safe!
2106     if (d->pressedIndex != -1 && d->movable)
2107         d->moveTabFinished(d->pressedIndex);
2108 
2109     d->pressedIndex = d->indexAtPos(event->pos());
2110 
2111     if (d->validIndex(d->pressedIndex)) {
2112         QStyleOptionTabBarBase optTabBase;
2113         optTabBase.init(this);
2114         optTabBase.documentMode = d->documentMode;
2115         if (event->type() == style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this))
2116             setCurrentIndex(d->pressedIndex);
2117         else
2118             repaint(tabRect(d->pressedIndex));
2119         if (d->movable) {
2120             d->dragStartPosition = event->pos();
2121         }
2122     }
2123 }
2124 
2125 /*!\reimp
2126  */
mouseMoveEvent(QMouseEvent * event)2127 void QTabBar::mouseMoveEvent(QMouseEvent *event)
2128 {
2129     Q_D(QTabBar);
2130     if (d->movable) {
2131         // Be safe!
2132         if (d->pressedIndex != -1
2133             && event->buttons() == Qt::NoButton)
2134             d->moveTabFinished(d->pressedIndex);
2135 
2136         // Start drag
2137         if (!d->dragInProgress && d->pressedIndex != -1) {
2138             if ((event->pos() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
2139                 d->dragInProgress = true;
2140                 d->setupMovableTab();
2141             }
2142         }
2143 
2144         if (event->buttons() == Qt::LeftButton
2145             && d->dragInProgress
2146             && d->validIndex(d->pressedIndex)) {
2147             bool vertical = verticalTabs(d->shape);
2148             int dragDistance;
2149             if (vertical) {
2150                 dragDistance = (event->pos().y() - d->dragStartPosition.y());
2151             } else {
2152                 dragDistance = (event->pos().x() - d->dragStartPosition.x());
2153             }
2154             d->tabList[d->pressedIndex].dragOffset = dragDistance;
2155 
2156             QRect startingRect = tabRect(d->pressedIndex);
2157             if (vertical)
2158                 startingRect.moveTop(startingRect.y() + dragDistance);
2159             else
2160                 startingRect.moveLeft(startingRect.x() + dragDistance);
2161 
2162             int overIndex;
2163             if (dragDistance < 0)
2164                 overIndex = tabAt(startingRect.topLeft());
2165             else
2166                 overIndex = tabAt(startingRect.topRight());
2167 
2168             if (overIndex != d->pressedIndex && overIndex != -1) {
2169                 int offset = 1;
2170                 if (isRightToLeft() && !vertical)
2171                     offset *= -1;
2172                 if (dragDistance < 0) {
2173                     dragDistance *= -1;
2174                     offset *= -1;
2175                 }
2176                 for (int i = d->pressedIndex;
2177                      offset > 0 ? i < overIndex : i > overIndex;
2178                      i += offset) {
2179                     QRect overIndexRect = tabRect(overIndex);
2180                     int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
2181                     if (dragDistance > needsToBeOver)
2182                         d->slide(i + offset, d->pressedIndex);
2183                 }
2184             }
2185             // Buttons needs to follow the dragged tab
2186             d->layoutTab(d->pressedIndex);
2187 
2188             update();
2189         }
2190     }
2191 
2192     if (event->buttons() != Qt::LeftButton) {
2193         event->ignore();
2194         return;
2195     }
2196 }
2197 
setupMovableTab()2198 void QTabBarPrivate::setupMovableTab()
2199 {
2200     Q_Q(QTabBar);
2201     if (!movingTab)
2202         movingTab = new QMovableTabWidget(q);
2203 
2204     int taboverlap = q->style()->pixelMetric(QStyle::PM_TabBarTabOverlap, nullptr ,q);
2205     QRect grabRect = q->tabRect(pressedIndex);
2206     if (verticalTabs(shape))
2207         grabRect.adjust(0, -taboverlap, 0, taboverlap);
2208     else
2209         grabRect.adjust(-taboverlap, 0, taboverlap, 0);
2210 
2211     QPixmap grabImage(grabRect.size() * q->devicePixelRatioF());
2212     grabImage.setDevicePixelRatio(q->devicePixelRatioF());
2213     grabImage.fill(Qt::transparent);
2214     QStylePainter p(&grabImage, q);
2215 
2216     QStyleOptionTabV4 tab;
2217     q->initStyleOption(&tab, pressedIndex);
2218     tab.position = QStyleOptionTab::OnlyOneTab;
2219     if (verticalTabs(shape))
2220         tab.rect.moveTopLeft(QPoint(0, taboverlap));
2221     else
2222         tab.rect.moveTopLeft(QPoint(taboverlap, 0));
2223     p.drawControl(QStyle::CE_TabBarTab, tab);
2224     p.end();
2225 
2226     movingTab->setPixmap(grabImage);
2227     movingTab->setGeometry(grabRect);
2228     movingTab->raise();
2229 
2230     // Re-arrange widget order to avoid overlaps
2231     if (tabList[pressedIndex].leftWidget)
2232         tabList[pressedIndex].leftWidget->raise();
2233     if (tabList[pressedIndex].rightWidget)
2234         tabList[pressedIndex].rightWidget->raise();
2235     if (leftB)
2236         leftB->raise();
2237     if (rightB)
2238         rightB->raise();
2239     movingTab->setVisible(true);
2240 }
2241 
moveTabFinished(int index)2242 void QTabBarPrivate::moveTabFinished(int index)
2243 {
2244     Q_Q(QTabBar);
2245     bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
2246     bool allAnimationsFinished = true;
2247 #if QT_CONFIG(animation)
2248     for(int i = 0; allAnimationsFinished && i < tabList.count(); ++i) {
2249         const Tab &t = tabList.at(i);
2250         if (t.animation && t.animation->state() == QAbstractAnimation::Running)
2251             allAnimationsFinished = false;
2252     }
2253 #endif // animation
2254     if (allAnimationsFinished && cleanup) {
2255         if(movingTab)
2256             movingTab->setVisible(false); // We might not get a mouse release
2257         for (int i = 0; i < tabList.count(); ++i) {
2258             tabList[i].dragOffset = 0;
2259         }
2260         if (pressedIndex != -1 && movable) {
2261             pressedIndex = -1;
2262             dragInProgress = false;
2263             dragStartPosition = QPoint();
2264         }
2265         layoutWidgets();
2266     } else {
2267         if (!validIndex(index))
2268             return;
2269         tabList[index].dragOffset = 0;
2270     }
2271     q->update();
2272 }
2273 
2274 /*!\reimp
2275 */
mouseReleaseEvent(QMouseEvent * event)2276 void QTabBar::mouseReleaseEvent(QMouseEvent *event)
2277 {
2278     Q_D(QTabBar);
2279     if (event->button() != Qt::LeftButton) {
2280         event->ignore();
2281         return;
2282     }
2283 
2284     if (d->movable && d->dragInProgress && d->validIndex(d->pressedIndex)) {
2285         int length = d->tabList[d->pressedIndex].dragOffset;
2286         int width = verticalTabs(d->shape)
2287             ? tabRect(d->pressedIndex).height()
2288             : tabRect(d->pressedIndex).width();
2289         int duration = qMin(ANIMATION_DURATION,
2290                 (qAbs(length) * ANIMATION_DURATION) / width);
2291         d->tabList[d->pressedIndex].startAnimation(d, duration);
2292         d->dragInProgress = false;
2293         d->movingTab->setVisible(false);
2294         d->dragStartPosition = QPoint();
2295     }
2296 
2297     // mouse release event might happen outside the tab, so keep the pressed index
2298     int oldPressedIndex = d->pressedIndex;
2299     int i = d->indexAtPos(event->pos()) == d->pressedIndex ? d->pressedIndex : -1;
2300     d->pressedIndex = -1;
2301     QStyleOptionTabBarBase optTabBase;
2302     optTabBase.initFrom(this);
2303     optTabBase.documentMode = d->documentMode;
2304     const bool selectOnRelease =
2305             (style()->styleHint(QStyle::SH_TabBar_SelectMouseType, &optTabBase, this) == QEvent::MouseButtonRelease);
2306     if (selectOnRelease)
2307         setCurrentIndex(i);
2308     if (d->validIndex(oldPressedIndex))
2309         update(tabRect(oldPressedIndex));
2310 }
2311 
2312 /*!\reimp
2313  */
keyPressEvent(QKeyEvent * event)2314 void QTabBar::keyPressEvent(QKeyEvent *event)
2315 {
2316     Q_D(QTabBar);
2317     if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
2318         event->ignore();
2319         return;
2320     }
2321     int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
2322     d->setCurrentNextEnabledIndex(offset);
2323 }
2324 
2325 /*!\reimp
2326  */
2327 #if QT_CONFIG(wheelevent)
wheelEvent(QWheelEvent * event)2328 void QTabBar::wheelEvent(QWheelEvent *event)
2329 {
2330 #ifndef Q_OS_MAC
2331     Q_D(QTabBar);
2332     int delta = (qAbs(event->angleDelta().x()) > qAbs(event->angleDelta().y()) ?
2333                      event->angleDelta().x() : event->angleDelta().y());
2334     int offset = delta > 0 ? -1 : 1;
2335     d->setCurrentNextEnabledIndex(offset);
2336     QWidget::wheelEvent(event);
2337 #else
2338     Q_UNUSED(event)
2339 #endif
2340 }
2341 #endif // QT_CONFIG(wheelevent)
2342 
setCurrentNextEnabledIndex(int offset)2343 void QTabBarPrivate::setCurrentNextEnabledIndex(int offset)
2344 {
2345     Q_Q(QTabBar);
2346     for (int index = currentIndex + offset; validIndex(index); index += offset) {
2347         if (tabList.at(index).enabled) {
2348             q->setCurrentIndex(index);
2349             break;
2350         }
2351     }
2352 }
2353 
2354 /*!\reimp
2355  */
changeEvent(QEvent * event)2356 void QTabBar::changeEvent(QEvent *event)
2357 {
2358     Q_D(QTabBar);
2359     switch (event->type()) {
2360     case QEvent::StyleChange:
2361         if (!d->elideModeSetByUser)
2362             d->elideMode = Qt::TextElideMode(style()->styleHint(QStyle::SH_TabBar_ElideMode, nullptr, this));
2363         if (!d->useScrollButtonsSetByUser)
2364             d->useScrollButtons = !style()->styleHint(QStyle::SH_TabBar_PreferNoArrows, nullptr, this);
2365         Q_FALLTHROUGH();
2366     case QEvent::FontChange:
2367         d->textSizes.clear();
2368         d->refresh();
2369         break;
2370     default:
2371         break;
2372     }
2373 
2374     QWidget::changeEvent(event);
2375 }
2376 
2377 /*!
2378     \reimp
2379 */
timerEvent(QTimerEvent * event)2380 void QTabBar::timerEvent(QTimerEvent *event)
2381 {
2382     Q_D(QTabBar);
2383     if (event->timerId() == d->switchTabTimerId) {
2384         killTimer(d->switchTabTimerId);
2385         d->switchTabTimerId = 0;
2386         setCurrentIndex(d->switchTabCurrentIndex);
2387         d->switchTabCurrentIndex = -1;
2388     }
2389     QWidget::timerEvent(event);
2390 }
2391 
2392 /*!
2393     \property QTabBar::elideMode
2394     \brief how to elide text in the tab bar
2395     \since 4.2
2396 
2397     This property controls how items are elided when there is not
2398     enough space to show them for a given tab bar size.
2399 
2400     By default the value is style dependent.
2401 
2402     \sa QTabWidget::elideMode, usesScrollButtons, QStyle::SH_TabBar_ElideMode
2403 */
2404 
elideMode() const2405 Qt::TextElideMode QTabBar::elideMode() const
2406 {
2407     Q_D(const QTabBar);
2408     return d->elideMode;
2409 }
2410 
setElideMode(Qt::TextElideMode mode)2411 void QTabBar::setElideMode(Qt::TextElideMode mode)
2412 {
2413     Q_D(QTabBar);
2414     d->elideMode = mode;
2415     d->elideModeSetByUser = true;
2416     d->textSizes.clear();
2417     d->refresh();
2418 }
2419 
2420 /*!
2421     \property QTabBar::usesScrollButtons
2422     \brief Whether or not a tab bar should use buttons to scroll tabs when it
2423     has many tabs.
2424     \since 4.2
2425 
2426     When there are too many tabs in a tab bar for its size, the tab bar can either choose
2427     to expand its size or to add buttons that allow you to scroll through the tabs.
2428 
2429     By default the value is style dependant.
2430 
2431     \sa elideMode, QTabWidget::usesScrollButtons, QStyle::SH_TabBar_PreferNoArrows
2432 */
usesScrollButtons() const2433 bool QTabBar::usesScrollButtons() const
2434 {
2435     return d_func()->useScrollButtons;
2436 }
2437 
setUsesScrollButtons(bool useButtons)2438 void QTabBar::setUsesScrollButtons(bool useButtons)
2439 {
2440     Q_D(QTabBar);
2441     d->useScrollButtonsSetByUser = true;
2442     if (d->useScrollButtons == useButtons)
2443         return;
2444     d->useScrollButtons = useButtons;
2445     d->refresh();
2446 }
2447 
2448 /*!
2449     \property QTabBar::tabsClosable
2450     \brief Whether or not a tab bar should place close buttons on each tab
2451     \since 4.5
2452 
2453     When tabsClosable is set to true a close button will appear on the tab on
2454     either the left or right hand side depending upon the style.  When the button
2455     is clicked the tab the signal tabCloseRequested will be emitted.
2456 
2457     By default the value is false.
2458 
2459     \sa setTabButton(), tabRemoved()
2460 */
2461 
tabsClosable() const2462 bool QTabBar::tabsClosable() const
2463 {
2464     Q_D(const QTabBar);
2465     return d->closeButtonOnTabs;
2466 }
2467 
setTabsClosable(bool closable)2468 void QTabBar::setTabsClosable(bool closable)
2469 {
2470     Q_D(QTabBar);
2471     if (d->closeButtonOnTabs == closable)
2472         return;
2473     d->closeButtonOnTabs = closable;
2474     ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, this);
2475     if (!closable) {
2476         for (int i = 0; i < d->tabList.count(); ++i) {
2477             if (closeSide == LeftSide && d->tabList[i].leftWidget) {
2478                 d->tabList[i].leftWidget->deleteLater();
2479                 d->tabList[i].leftWidget = nullptr;
2480             }
2481             if (closeSide == RightSide && d->tabList[i].rightWidget) {
2482                 d->tabList[i].rightWidget->deleteLater();
2483                 d->tabList[i].rightWidget = nullptr;
2484             }
2485         }
2486     } else {
2487         bool newButtons = false;
2488         for (int i = 0; i < d->tabList.count(); ++i) {
2489             if (tabButton(i, closeSide))
2490                 continue;
2491             newButtons = true;
2492             QAbstractButton *closeButton = new CloseButton(this);
2493             connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
2494             setTabButton(i, closeSide, closeButton);
2495         }
2496         if (newButtons)
2497             d->layoutTabs();
2498     }
2499     update();
2500 }
2501 
2502 /*!
2503     \enum QTabBar::ButtonPosition
2504     \since 4.5
2505 
2506     This enum type lists the location of the widget on a tab.
2507 
2508     \value LeftSide Left side of the tab.
2509 
2510     \value RightSide Right side of the tab.
2511 
2512 */
2513 
2514 /*!
2515     \enum QTabBar::SelectionBehavior
2516     \since 4.5
2517 
2518     This enum type lists the behavior of QTabBar when a tab is removed
2519     and the tab being removed is also the current tab.
2520 
2521     \value SelectLeftTab  Select the tab to the left of the one being removed.
2522 
2523     \value SelectRightTab  Select the tab to the right of the one being removed.
2524 
2525     \value SelectPreviousTab  Select the previously selected tab.
2526 
2527 */
2528 
2529 /*!
2530     \property QTabBar::selectionBehaviorOnRemove
2531     \brief What tab should be set as current when removeTab is called if
2532     the removed tab is also the current tab.
2533     \since 4.5
2534 
2535     By default the value is SelectRightTab.
2536 
2537     \sa removeTab()
2538 */
2539 
2540 
selectionBehaviorOnRemove() const2541 QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove() const
2542 {
2543     Q_D(const QTabBar);
2544     return d->selectionBehaviorOnRemove;
2545 }
2546 
setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)2547 void QTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)
2548 {
2549     Q_D(QTabBar);
2550     d->selectionBehaviorOnRemove = behavior;
2551 }
2552 
2553 /*!
2554     \property QTabBar::expanding
2555     \brief When expanding is true QTabBar will expand the tabs to use the empty space.
2556     \since 4.5
2557 
2558     By default the value is true.
2559 
2560     \sa QTabWidget::documentMode
2561 */
2562 
expanding() const2563 bool QTabBar::expanding() const
2564 {
2565     Q_D(const QTabBar);
2566     return d->expanding;
2567 }
2568 
setExpanding(bool enabled)2569 void QTabBar::setExpanding(bool enabled)
2570 {
2571     Q_D(QTabBar);
2572     if (d->expanding == enabled)
2573         return;
2574     d->expanding = enabled;
2575     d->layoutTabs();
2576 }
2577 
2578 /*!
2579     \property QTabBar::movable
2580     \brief This property holds whether the user can move the tabs
2581     within the tabbar area.
2582 
2583     \since 4.5
2584 
2585     By default, this property is \c false;
2586 */
2587 
isMovable() const2588 bool QTabBar::isMovable() const
2589 {
2590     Q_D(const QTabBar);
2591     return d->movable;
2592 }
2593 
setMovable(bool movable)2594 void QTabBar::setMovable(bool movable)
2595 {
2596     Q_D(QTabBar);
2597     d->movable = movable;
2598 }
2599 
2600 
2601 /*!
2602     \property QTabBar::documentMode
2603     \brief Whether or not the tab bar is rendered in a mode suitable for the main window.
2604     \since 4.5
2605 
2606     This property is used as a hint for styles to draw the tabs in a different
2607     way then they would normally look in a tab widget.  On \macos this will
2608     look similar to the tabs in Safari or Sierra's Terminal.app.
2609 
2610     \sa QTabWidget::documentMode
2611 */
documentMode() const2612 bool QTabBar::documentMode() const
2613 {
2614     return d_func()->documentMode;
2615 }
2616 
setDocumentMode(bool enabled)2617 void QTabBar::setDocumentMode(bool enabled)
2618 {
2619     Q_D(QTabBar);
2620 
2621     d->documentMode = enabled;
2622     d->updateMacBorderMetrics();
2623 }
2624 
2625 /*!
2626     \property QTabBar::autoHide
2627     \brief If true, the tab bar is automatically hidden when it contains less
2628     than 2 tabs.
2629     \since 5.4
2630 
2631     By default, this property is false.
2632 
2633     \sa QWidget::visible
2634 */
2635 
autoHide() const2636 bool QTabBar::autoHide() const
2637 {
2638     Q_D(const QTabBar);
2639     return d->autoHide;
2640 }
2641 
setAutoHide(bool hide)2642 void QTabBar::setAutoHide(bool hide)
2643 {
2644     Q_D(QTabBar);
2645     if (d->autoHide == hide)
2646         return;
2647 
2648     d->autoHide = hide;
2649     if (hide)
2650         d->autoHideTabs();
2651     else
2652         setVisible(true);
2653 }
2654 
2655 /*!
2656     \property QTabBar::changeCurrentOnDrag
2657     \brief If true, then the current tab is automatically changed when dragging
2658     over the tabbar.
2659     \since 5.4
2660 
2661     \note You should also set acceptDrops property to true to make this feature
2662     work.
2663 
2664     By default, this property is false.
2665 */
2666 
changeCurrentOnDrag() const2667 bool QTabBar::changeCurrentOnDrag() const
2668 {
2669     Q_D(const QTabBar);
2670     return d->changeCurrentOnDrag;
2671 }
2672 
setChangeCurrentOnDrag(bool change)2673 void QTabBar::setChangeCurrentOnDrag(bool change)
2674 {
2675     Q_D(QTabBar);
2676     d->changeCurrentOnDrag = change;
2677     if (!change)
2678         d->killSwitchTabTimer();
2679 }
2680 
2681 /*!
2682     Sets \a widget on the tab \a index.  The widget is placed
2683     on the left or right hand side depending upon the \a position.
2684     \since 4.5
2685 
2686     Any previously set widget in \a position is hidden.
2687 
2688     The tab bar will take ownership of the widget and so all widgets set here
2689     will be deleted by the tab bar when it is destroyed unless you separately
2690     reparent the widget after setting some other widget (or \nullptr).
2691 
2692     \sa tabsClosable()
2693   */
setTabButton(int index,ButtonPosition position,QWidget * widget)2694 void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
2695 {
2696     Q_D(QTabBar);
2697     if (index < 0 || index >= d->tabList.count())
2698         return;
2699     if (widget) {
2700         widget->setParent(this);
2701         // make sure our left and right widgets stay on top
2702         widget->lower();
2703         widget->show();
2704     }
2705     if (position == LeftSide) {
2706         if (d->tabList[index].leftWidget)
2707             d->tabList[index].leftWidget->hide();
2708         d->tabList[index].leftWidget = widget;
2709     } else {
2710         if (d->tabList[index].rightWidget)
2711             d->tabList[index].rightWidget->hide();
2712         d->tabList[index].rightWidget = widget;
2713     }
2714     d->layoutTabs();
2715     d->refresh();
2716     update();
2717 }
2718 
2719 /*!
2720     Returns the widget set a tab \a index and \a position or \nullptr
2721     if one is not set.
2722   */
tabButton(int index,ButtonPosition position) const2723 QWidget *QTabBar::tabButton(int index, ButtonPosition position) const
2724 {
2725     Q_D(const QTabBar);
2726     if (index < 0 || index >= d->tabList.count())
2727         return nullptr;
2728     if (position == LeftSide)
2729         return d->tabList.at(index).leftWidget;
2730     else
2731         return d->tabList.at(index).rightWidget;
2732 }
2733 
2734 #ifndef QT_NO_ACCESSIBILITY
2735 /*!
2736     Sets the accessibleName of the tab at position \a index to \a name.
2737 */
setAccessibleTabName(int index,const QString & name)2738 void QTabBar::setAccessibleTabName(int index, const QString &name)
2739 {
2740     Q_D(QTabBar);
2741     if (QTabBarPrivate::Tab *tab = d->at(index)) {
2742         tab->accessibleName = name;
2743         QAccessibleEvent event(this, QAccessible::NameChanged);
2744         event.setChild(index);
2745         QAccessible::updateAccessibility(&event);
2746     }
2747 }
2748 
2749 /*!
2750     Returns the accessibleName of the tab at position \a index, or an empty
2751     string if \a index is out of range.
2752 */
accessibleTabName(int index) const2753 QString QTabBar::accessibleTabName(int index) const
2754 {
2755     Q_D(const QTabBar);
2756     if (const QTabBarPrivate::Tab *tab = d->at(index))
2757         return tab->accessibleName;
2758     return QString();
2759 }
2760 #endif // QT_NO_ACCESSIBILITY
2761 
CloseButton(QWidget * parent)2762 CloseButton::CloseButton(QWidget *parent)
2763     : QAbstractButton(parent)
2764 {
2765     setFocusPolicy(Qt::NoFocus);
2766 #ifndef QT_NO_CURSOR
2767     setCursor(Qt::ArrowCursor);
2768 #endif
2769 #ifndef QT_NO_TOOLTIP
2770     setToolTip(tr("Close Tab"));
2771 #endif
2772     resize(sizeHint());
2773 }
2774 
sizeHint() const2775 QSize CloseButton::sizeHint() const
2776 {
2777     ensurePolished();
2778     int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, nullptr, this);
2779     int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, nullptr, this);
2780     return QSize(width, height);
2781 }
2782 
enterEvent(QEvent * event)2783 void CloseButton::enterEvent(QEvent *event)
2784 {
2785     if (isEnabled())
2786         update();
2787     QAbstractButton::enterEvent(event);
2788 }
2789 
leaveEvent(QEvent * event)2790 void CloseButton::leaveEvent(QEvent *event)
2791 {
2792     if (isEnabled())
2793         update();
2794     QAbstractButton::leaveEvent(event);
2795 }
2796 
paintEvent(QPaintEvent *)2797 void CloseButton::paintEvent(QPaintEvent *)
2798 {
2799     QPainter p(this);
2800     QStyleOption opt;
2801     opt.init(this);
2802     opt.state |= QStyle::State_AutoRaise;
2803     if (isEnabled() && underMouse() && !isChecked() && !isDown())
2804         opt.state |= QStyle::State_Raised;
2805     if (isChecked())
2806         opt.state |= QStyle::State_On;
2807     if (isDown())
2808         opt.state |= QStyle::State_Sunken;
2809 
2810     if (const QTabBar *tb = qobject_cast<const QTabBar *>(parent())) {
2811         int index = tb->currentIndex();
2812         QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, tb);
2813         if (tb->tabButton(index, position) == this)
2814             opt.state |= QStyle::State_Selected;
2815     }
2816 
2817     style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p, this);
2818 }
2819 
2820 #if QT_CONFIG(animation)
updateCurrentValue(const QVariant & current)2821 void QTabBarPrivate::Tab::TabBarAnimation::updateCurrentValue(const QVariant &current)
2822 {
2823     priv->moveTab(priv->tabList.indexOf(*tab), current.toInt());
2824 }
2825 
updateState(QAbstractAnimation::State newState,QAbstractAnimation::State)2826 void QTabBarPrivate::Tab::TabBarAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State)
2827 {
2828     if (newState == Stopped) priv->moveTabFinished(priv->tabList.indexOf(*tab));
2829 }
2830 #endif
2831 
2832 QT_END_NAMESPACE
2833 
2834 #include "moc_qtabbar.cpp"
2835 #include "qtabbar.moc"
2836