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